blob: fd740732565098f6c53d991166fb0f905be606fd [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 1999-2005 Open Source Telecom Corporation.
2// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17//
18// As a special exception, you may use this file as part of a free software
19// library without restriction. Specifically, if other files instantiate
20// templates or use macros or inline functions from this file, or you compile
21// this file and link it with other files to produce an executable, this
22// file does not by itself cause the resulting executable to be covered by
23// the GNU General Public License. This exception does not however
24// invalidate any other reasons why the executable file might be covered by
25// the GNU General Public License.
26//
27// This exception applies only to the code released under the name GNU
28// Common C++. If you copy code from other releases into a copy of GNU
29// Common C++, as the General Public License permits, the exception does
30// not apply to the code that you add in this way. To avoid misleading
31// anyone as to the status of such modified files, you must delete
32// this exception notice from them.
33//
34// If you write modifications of your own for GNU Common C++, it is your choice
35// whether to permit this exception to apply to your modifications.
36// If you do not wish that, delete this exception notice.
37//
38
39#include <cc++/config.h>
40#include <cc++/string.h>
41#include <cc++/exception.h>
42#include <cstdlib>
43
44#if !defined(_MSC_VER) || _MSC_VER >= 1300
45
46#include <cc++/export.h>
47#include <cc++/persist.h>
48#ifndef HAVE_EXCEPTION
49#include "assert.h"
50#endif
51
52#ifdef CCXX_NAMESPACES
53namespace ost {
54using namespace std;
55#endif
56
57#ifndef NO_COMPRESSION
58const uint32 MAX_BUFFER = 16384;
59#endif
60
61/**
62 * NullObject is a const uint32 which is the ID streamed to disk
63 * if an attempt to stream a NULL Persistence::BaseObject or
64 * Derivative is made...
65 */
66
67const uint32 NullObject = 0xffffffff;
68
69Engine::Engine(std::iostream& stream, EngineMode mode, bool compress) THROWS (PersistException) :
70myUnderlyingStream(stream), myOperationalMode(mode), use_compression(compress)
71{
72 // Nothing else to initialise for now
73#ifndef NO_COMPRESSION
74 if ( use_compression ) {
75 myZStream.zalloc = ( alloc_func ) NULL;
76 myZStream.zfree = ( free_func ) NULL;
77 myZStream.opaque = ( voidpf ) NULL;
78 myCompressedDataBuffer = new uint8[MAX_BUFFER];
79 myUncompressedDataBuffer = new uint8[MAX_BUFFER];
80 myLastUncompressedDataRead = myUncompressedDataBuffer;
81 if ( myOperationalMode == modeRead ) {
82 myZStream.next_in = myCompressedDataBuffer;
83 myZStream.next_out = myUncompressedDataBuffer;
84 myZStream.avail_in = 0;
85 myZStream.avail_out = MAX_BUFFER;
86 int err = inflateInit ( &myZStream );
87 if ( err != Z_OK ) {
88 THROW ( PersistException ( String ( "zLib didn't initialise for inflating." ) ) );
89 }
90 }
91 else {
92 myZStream.next_in = myUncompressedDataBuffer;
93 myZStream.next_out = myCompressedDataBuffer;
94 myZStream.avail_in = 0;
95 myZStream.avail_out = MAX_BUFFER;
96 int err = deflateInit ( &myZStream, 9 ); // TODO: tweak compression level
97 if ( err != Z_OK ) {
98 THROW ( PersistException ( String ( "zLib didn't initialise for deflating." ) ) );
99 }
100 }
101 }
102#endif
103}
104
105bool Engine::more()
106{
107#ifndef NO_COMPRESSION
108 if ( use_compression && myOperationalMode == modeRead )
109 return ( myLastUncompressedDataRead < myZStream.next_out );
110#endif
111 return false;
112}
113
114void Engine::sync()
115{
116 // Flush compression buffers etc here.
117#ifndef NO_COMPRESSION
118 if ( use_compression ) {
119 if ( myOperationalMode == modeRead ) {
120 inflateEnd ( &myZStream );
121 }
122 else {
123 int zret = Z_OK;
124 while ( myZStream.avail_in > 0 || zret == Z_OK ) {
125 zret = deflate ( &myZStream, Z_FINISH );
126 if ( myZStream.avail_out >= 0 ) {
127 myUnderlyingStream.write ( ( char* ) myCompressedDataBuffer, MAX_BUFFER - myZStream.avail_out );
128 myZStream.next_out = myCompressedDataBuffer;
129 myZStream.avail_out = MAX_BUFFER;
130 }
131 }
132 deflateEnd ( &myZStream );
133 }
134 }
135#endif
136}
137
138Engine::~Engine()
139{
140 if ( myUnderlyingStream.good() )
141 sync();
142
143#ifndef NO_COMPRESSION
144 if ( use_compression ) {
145 delete [] myCompressedDataBuffer;
146 delete [] myUncompressedDataBuffer;
147 }
148#endif
149}
150
151void Engine::writeBinary(const uint8* data, const uint32 size) THROWS (PersistException)
152{
153 if ( myOperationalMode != modeWrite )
154 THROW (PersistException( "Cannot write to an input Engine" ));
155#ifdef NO_COMPRESSION
156 myUnderlyingStream.write((const char *)data,size);
157#else
158 if (use_compression) {
159 // Compress the data here and doit :)
160 uint32 written = 0;
161 while ( written < size ) {
162 // transfer as much information as we can into the input buffer.
163 if ( myZStream.avail_in < MAX_BUFFER ) {
164 uint32 toAdd = size - written;
165 if ( toAdd > ( MAX_BUFFER - myZStream.avail_in ) )
166 toAdd = ( MAX_BUFFER - myZStream.avail_in );
167 memcpy ( myZStream.next_in + myZStream.avail_in, data + written, toAdd );
168 written += toAdd;
169 myZStream.avail_in += toAdd;
170 }
171 if ( myZStream.avail_in < MAX_BUFFER )
172 return; // We have not filled the buffer, so let's carry on streaming
173 // We have a full input buffer, so we compressit.
174 while ( myZStream.avail_in > 0 ) {
175 deflate ( &myZStream, 0 );
176 if ( myZStream.avail_out == 0 ) {
177 // We filled the output buffer, let's stream it
178 myUnderlyingStream.write ( ( char* ) myCompressedDataBuffer, MAX_BUFFER );
179 myZStream.next_out = myCompressedDataBuffer;
180 myZStream.avail_out = MAX_BUFFER;
181 }
182 // Repeat whilst the input buffer isn't flushed
183 }
184 // Now we have flushed the input buffer we can reset it
185 myZStream.avail_in = 0;
186 myZStream.next_in = myUncompressedDataBuffer;
187 }
188 }
189 else {
190 myUnderlyingStream.write ( ( const char * ) data, size );
191 }
192#endif
193}
194
195void Engine::readBinary(uint8* data, uint32 size) THROWS (PersistException)
196{
197 if ( myOperationalMode != modeRead )
198 THROW (PersistException( "Cannot read from an output Engine" ));
199#ifdef NO_COMPRESSION
200 myUnderlyingStream.read((char *)data,size);
201#else
202 if ( use_compression ) {
203 uint32 read = 0;
204 while ( read < size ) {
205 // If we have any data left in the uncompressed buffer - use it
206 if ( myLastUncompressedDataRead < myZStream.next_out ) {
207 uint32 toRead = size - read;
208 if ( toRead > ( uint32 ) ( myZStream.next_out - myLastUncompressedDataRead ) )
209 toRead = ( myZStream.next_out - myLastUncompressedDataRead );
210 memcpy ( data + read, myLastUncompressedDataRead, toRead );
211 myLastUncompressedDataRead += toRead;
212 read += toRead;
213 }
214 if ( read == size )
215 return; // We have read all we need to
216 // Reset the stream for the next block of data
217 myLastUncompressedDataRead = myUncompressedDataBuffer;
218 myZStream.next_out = myUncompressedDataBuffer;
219 myZStream.avail_out = MAX_BUFFER;
220 // Next we have to deal such that, until we have a full output buffer,
221 // (Or we run out of input)
222 if ( myUnderlyingStream.good() ) {
223 while ( myUnderlyingStream.good() && myZStream.avail_out > 0 ) {
224 // Right then, if we have run out of input, fetch another chunk
225 if ( myZStream.avail_in == 0 ) {
226 myZStream.next_in = myCompressedDataBuffer;
227 myUnderlyingStream.read ( ( char* ) myCompressedDataBuffer, MAX_BUFFER );
228 myZStream.avail_in = myUnderlyingStream.gcount();
229 }
230 inflate ( &myZStream, 0 );
231 }
232 }
233 else
234 {
235 // Oh dear - we ran out of input on the buffer.
236 // Maybe we can still inflate some
237 inflate ( &myZStream, 0 );
238 if ( myZStream.avail_out == MAX_BUFFER )
239 THROW ( PersistException ( String ( "Oh dear - ran out of input" ) ) );
240 }
241 }
242 }
243 else {
244 myUnderlyingStream.read ( ( char * ) data, size );
245 }
246#endif
247}
248
249/*
250 * note, does not (yet?) throw an exception, but interface
251 * prepared ..
252 */
253void Engine::write(const BaseObject *object) THROWS (PersistException)
254{
255 // Pre-step, if object is NULL, then don't serialise it - serialise a
256 // marker to say that it is null.
257 // as ID's are uint32's, NullObject will do nicely for the task
258 if (object == NULL) {
259 uint32 id = NullObject;
260 write(id);
261 return;
262 }
263
264 // First off - has this Object been serialised already?
265 ArchiveMap::const_iterator itor = myArchiveMap.find(object);
266 if (itor == myArchiveMap.end()) {
267 // Unfortunately we need to serialise it - here we go ....
268 uint32 id = (uint32)myArchiveMap.size();
269 myArchiveMap[object] = id; // bumps id automatically for next one
270 write(id);
271 ClassMap::const_iterator classItor = myClassMap.find(object->getPersistenceID());
272 if (classItor == myClassMap.end()) {
273 uint32 classId = (uint32)myClassMap.size();
274 myClassMap[object->getPersistenceID()] = classId;
275 write(classId);
276 write(static_cast<String>(object->getPersistenceID()));
277 }
278 else {
279 write(classItor->second);
280 }
281 String majik;
282 majik = "OBST";
283 write(majik);
284 object->write(*this);
285 majik = "OBEN";
286 write(majik);
287 }
288 else {
289 // This object has been serialised, so just pop its ID out
290 write(itor->second);
291 }
292}
293
294/*
295 * reads in a BaseObject into a reference (pre-instantiated object)
296 */
297void Engine::read(BaseObject &object) THROWS (PersistException)
298{
299 uint32 id = 0;
300 read(id);
301 if (id == NullObject)
302 THROW (PersistException("Object Id should not be NULL when unpersisting to a reference"));
303
304 // Do we already have this object in memory?
305 if (id < myArchiveVector.size()) {
306 object = *(myArchiveVector[id]);
307 return;
308 }
309
310 // Okay - read the identifier for the class in...
311 // we won't need it later since this object is already allocated
312 readClass();
313
314 // Okay then - we can read data straight into this object
315 readObject(&object);
316}
317
318/*
319 * reads in a BaseObject into a pointer allocating if the pointer is NULL
320 */
321void Engine::read(BaseObject *&object) THROWS (PersistException)
322{
323 uint32 id = 0;
324 read(id);
325 // Is the ID a NULL object?
326 if (id == NullObject) {
327 object = NULL;
328 return;
329 }
330
331 // Do we already have this object in memory?
332 if (id < myArchiveVector.size()) {
333 object = myArchiveVector[id];
334 return;
335 }
336
337 // Okay - read the identifier for the class in...
338 String className = readClass();
339
340 // is the pointer already initialized? if so then no need to reallocate
341 if (object != NULL) {
342 readObject(object);
343 return;
344 }
345
346 // Create the object (of the relevant type)
347 object = TypeManager::createInstanceOf(className.c_str());
348 if (object) {
349 // Okay then - we can make this object
350 readObject(object);
351 }
352 else
353 THROW (PersistException(String("Unable to instantiate object of class ")+className));
354}
355
356/*
357 * reads the actual object data in
358 */
359void Engine::readObject(BaseObject* object) THROWS (PersistException)
360{
361 // Okay then - we can make this object
362 myArchiveVector.push_back(object);
363 String majik;
364 read(majik);
365 if(majik != String("OBST"))
366 THROW(PersistException("Missing Start-of-Object marker"));
367 object->read(*this);
368 read(majik);
369 if(majik != String("OBEN"))
370 THROW(PersistException("Missing End-of-Object marker"));
371}
372
373/*
374 * reads the class information in
375 */
376const String Engine::readClass() THROWS (PersistException)
377{
378 // Okay - read the identifier for the class in...
379 uint32 classId = 0;
380 read(classId);
381 String className;
382 if (classId < myClassVector.size()) {
383 className = myClassVector[classId];
384 }
385 else {
386 // Okay the class wasn't known yet - save its name
387 read(className);
388 myClassVector.push_back(className);
389 }
390 return className;
391}
392
393/*
394 * note, does not (yet?) throw an exception, but interface
395 * prepared ..
396 */
397void Engine::write(const String& str) THROWS (PersistException)
398{
399 uint32 len = (uint32)str.length();
400 write(len);
401 writeBinary((uint8*)str.c_str(),len);
402}
403
404void Engine::read(String& str) THROWS (PersistException)
405{
406 uint32 len = 0;
407 read(len);
408 uint8 *buffer = new uint8[len+1];
409 readBinary(buffer,len);
410 buffer[len] = 0;
411 str = (char*)buffer;
412 delete[] buffer;
413}
414
415/*
416 * note, does not (yet?) throw an exception, but interface
417 * prepared ..
418 */
419void Engine::write(const std::string& str) THROWS (PersistException)
420{
421 uint32 len = (uint32)str.length();
422 write(len);
423 writeBinary((uint8*)str.c_str(),len);
424}
425
426void Engine::read(std::string& str) THROWS (PersistException)
427{
428 uint32 len = 0;
429 read(len);
430 uint8 *buffer = new uint8[len+1];
431 readBinary(buffer,len);
432 buffer[len] = 0;
433 str = (char*)buffer;
434 delete[] buffer;
435}
436
437#define CCXX_RE(ar,ob) ar.read(ob); return ar
438#define CCXX_WE(ar,ob) ar.write(ob); return ar
439
440Engine& operator >>( Engine& ar, BaseObject &ob) THROWS (PersistException) {CCXX_RE(ar,ob);}
441Engine& operator >>( Engine& ar, BaseObject *&ob) THROWS (PersistException) {CCXX_RE(ar,ob);}
442Engine& operator <<( Engine& ar, BaseObject const &ob) THROWS (PersistException) {CCXX_WE(ar,&ob);}
443Engine& operator <<( Engine& ar, BaseObject const *ob) THROWS (PersistException) {CCXX_WE(ar,ob);}
444
445Engine& operator >>( Engine& ar, int8& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
446Engine& operator <<( Engine& ar, int8 ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
447
448Engine& operator >>( Engine& ar, uint8& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
449Engine& operator <<( Engine& ar, uint8 ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
450
451Engine& operator >>( Engine& ar, int16& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
452Engine& operator <<( Engine& ar, int16 ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
453
454Engine& operator >>( Engine& ar, uint16& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
455Engine& operator <<( Engine& ar, uint16 ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
456
457Engine& operator >>( Engine& ar, int32& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
458Engine& operator <<( Engine& ar, int32 ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
459
460Engine& operator >>( Engine& ar, uint32& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
461Engine& operator <<( Engine& ar, uint32 ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
462
463#ifdef HAVE_64_BITS
464Engine& operator >>( Engine& ar, int64& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
465Engine& operator <<( Engine& ar, int64 ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
466
467Engine& operator >>( Engine& ar, uint64& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
468Engine& operator <<( Engine& ar, uint64 ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
469#endif
470
471Engine& operator >>( Engine& ar, float& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
472Engine& operator <<( Engine& ar, float ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
473
474Engine& operator >>( Engine& ar, double& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
475Engine& operator <<( Engine& ar, double ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
476
477Engine& operator >>( Engine& ar, String& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
478Engine& operator <<( Engine& ar, String ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
479
480Engine& operator >>( Engine& ar, std::string& ob) THROWS (PersistException) {CCXX_RE (ar,ob);}
481Engine& operator <<( Engine& ar, std::string ob) THROWS (PersistException) {CCXX_WE (ar,ob);}
482
483Engine& operator >>( Engine& ar, bool& ob) THROWS (PersistException) {
484 uint32 a; ar.read(a); ob=a==1;return ar;
485}
486
487Engine& operator <<( Engine& ar, bool ob) THROWS (PersistException) {
488 uint32 a=ob?1:0; ar.write(a); return ar;
489}
490
491#undef CCXX_RE
492#undef CCXX_WE
493
494#ifdef CCXX_NAMESPACES
495}
496#endif
497
498#endif
499
500/** EMACS **
501 * Local variables:
502 * mode: c++
503 * c-basic-offset: 4
504 * End:
505 */