]> err.no Git - sope/blob - sope-core/NGStreams/NGStreamCoder.m
do not run autoconf in NGStreams
[sope] / sope-core / NGStreams / NGStreamCoder.m
1 /*
2   Copyright (C) 2000-2003 SKYRIX Software AG
3
4   This file is part of OGo
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include "config.h"
24 #include "common.h"
25 #include "NGStreamCoder.h"
26 #include "NGStream+serialization.h"
27
28 #if APPLE_RUNTIME || NeXT_RUNTIME
29 #  include <objc/objc-class.h>
30 #endif
31
32 #define FINAL static inline
33
34 extern id nil_method(id, SEL, ...);
35
36 /*
37   Debugging topics:
38     encoder
39     decoder
40 */
41
42 typedef unsigned char NGTagType;
43
44 #define REFERENCE 128
45 #define VALUE     127
46
47 static unsigned __NGHashPointer(void *table, const void *anObject)
48 {
49     return (unsigned)((long)anObject / 4);
50 }
51 static BOOL __NGComparePointers(void *table, 
52         const void *anObject1, const void *anObject2)
53 {
54     return anObject1 == anObject2 ? YES : NO;
55 }
56 static void __NGRetainObjects(void *table, const void *anObject)
57 {
58     (void)[(NSObject*)anObject retain];
59 }
60 static void __NGReleaseObjects(void *table, void *anObject)
61 {
62     [(NSObject*)anObject release];
63 }
64 static NSString* __NGDescribePointers(void *table, const void *anObject)
65 {
66     return [NSString stringWithFormat:@"%p", anObject];
67 }
68
69 static NSMapTableKeyCallBacks NGIdentityObjectMapKeyCallbacks = {
70   (unsigned(*)(NSMapTable *, const void *))          __NGHashPointer,
71   (BOOL(*)(NSMapTable *, const void *, const void *))__NGComparePointers,
72   (void (*)(NSMapTable *, const void *anObject))     __NGRetainObjects,
73   (void (*)(NSMapTable *, void *anObject))           __NGReleaseObjects,
74   (NSString *(*)(NSMapTable *, const void *))        __NGDescribePointers,
75   (const void *)NULL
76 };
77
78 static const char *NGCoderSignature = "MDlink NGStreamCoder";
79 static int        NGCoderVersion    = 1100;
80
81 @implementation NGStreamCoder
82
83 static NSMapTable *classToAliasMappings = NULL; // archive name => decoded name
84
85 + (void)initialize {
86   BOOL isInitialized = NO;
87   if (!isInitialized) {
88     isInitialized = YES;
89
90     classToAliasMappings = NSCreateMapTable(NSObjectMapKeyCallBacks,
91                                             NSObjectMapValueCallBacks,
92                                             19);
93   }
94 }
95
96 + (id)coderWithStream:(id<NGStream>)_stream {
97   return AUTORELEASE([[self alloc] initWithStream:_stream]);
98 }
99
100 - (id)initWithStream:(id<NGStream>)_stream mode:(NGStreamMode)_mode {
101   if ((self = [super init])) {
102     self->stream = [_stream retain];
103
104     self->classForCoder      = @selector(classForCoder);
105     self->replObjectForCoder = @selector(replacementObjectForCoder:);
106
107     if ([self->stream isKindOfClass:[NSObject class]]) {
108       self->readIMP = (NGIOSafeReadMethodType)
109         [(NSObject*)self->stream methodForSelector:@selector(safeReadBytes:count:)];
110       self->writeIMP = (NGIOSafeWriteMethodType)
111         [(NSObject*)self->stream methodForSelector:@selector(safeWriteBytes:count:)];
112     }
113
114     if (NGCanReadInStreamMode(_mode)) { // setup decoder
115       self->inObjects       = NSCreateMapTable(NSIntMapKeyCallBacks,
116                                                NSObjectMapValueCallBacks,
117                                                119);
118       self->inClasses       = NSCreateMapTable(NSIntMapKeyCallBacks,
119                                                NSObjectMapValueCallBacks,
120                                                19);
121       self->inPointers      = NSCreateMapTable(NSIntMapKeyCallBacks,
122                                                NSIntMapValueCallBacks,
123                                                19);
124       self->inClassAlias    = NSCreateMapTable(NSObjectMapKeyCallBacks,
125                                                NSObjectMapValueCallBacks,
126                                                19);
127       self->inClassVersions = NSCreateMapTable(NSObjectMapKeyCallBacks,
128                                                NSObjectMapValueCallBacks,
129                                                19);
130     }
131
132     if (NGCanWriteInStreamMode(_mode)) { // setup encoder
133       self->outObjects      = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 119);
134       self->outConditionals = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 119);
135       self->outPointers     = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 0);
136       self->replacements    = NSCreateMapTable(NGIdentityObjectMapKeyCallbacks,
137                                                NSObjectMapValueCallBacks,
138                                                19);
139     }
140   }
141   return self;
142 }
143
144 - (id)init {
145   return [self initWithStream:nil mode:NGStreamMode_undefined];
146 }
147 - (id)initWithStream:(id<NGStream>)_stream {
148   return [self initWithStream:_stream mode:[_stream mode]];
149 }
150
151 - (void)dealloc {
152   // release encoding restreams
153   if (self->outObjects) {
154     NSFreeHashTable(self->outObjects); self->outObjects = NULL; }
155   if (self->outConditionals) {
156     NSFreeHashTable(self->outConditionals); self->outConditionals = NULL; }
157   if (self->outPointers) {
158     NSFreeHashTable(self->outPointers); self->outPointers = NULL; }
159   if (self->replacements) {
160     NSFreeMapTable(self->replacements); self->replacements = NULL; }
161
162   // release decoding restreams
163   if (self->inObjects) {
164     NSFreeMapTable(self->inObjects); self->inObjects = NULL; }
165   if (self->inClasses) {
166     NSFreeMapTable(self->inClasses); self->inClasses = NULL; }
167   if (self->inPointers) {
168     NSFreeMapTable(self->inPointers); self->inPointers = NULL; }
169   if (self->inClassAlias) {
170     NSFreeMapTable(self->inClassAlias); self->inClassAlias = NULL; }
171   if (self->inClassVersions) {
172     NSFreeMapTable(self->inClassVersions); self->inClassVersions = NULL; }
173
174   [self->stream release]; self->stream = nil;
175   
176   [super dealloc];
177 }
178
179 /* accessors */
180
181 - (id<NGStream>)stream {
182   return self->stream;
183 }
184
185 - (NSString *)coderSignature {
186   return [NSString stringWithCString:NGCoderSignature];
187 }
188 - (int)coderVersion {
189   return NGCoderVersion;
190 }
191
192 - (unsigned int)systemVersion {
193   return self->inArchiverVersion;
194 }
195
196 // misc
197
198 FINAL BOOL isBaseType(const char *_type) {
199   switch (*_type) {
200     case _C_CHR: case _C_UCHR:
201     case _C_SHT: case _C_USHT:
202     case _C_INT: case _C_UINT:
203     case _C_LNG: case _C_ULNG:
204     case _C_FLT: case _C_DBL:
205       return YES;
206
207     default:
208       return NO;
209   }
210 }
211
212 FINAL BOOL isReferenceTag(NGTagType _tag) {
213   return (_tag & REFERENCE) ? YES : NO;
214 }
215
216 FINAL NGTagType tagValue(NGTagType _tag) {
217   return _tag & VALUE; // mask out bit 8
218 }
219
220 FINAL int _archiveIdOfObject(NGStreamCoder *self, id _object) {
221   return (_object == nil)
222     ? 0
223     : (int)_object;
224 }
225 FINAL int _archiveIdOfClass(NGStreamCoder *self, Class _class) {
226   return _archiveIdOfObject(self, _class);
227 }
228
229
230 // primitive encoding
231
232 FINAL void _writeBytes(NGStreamCoder *self, const void *_bytes, unsigned _len);
233
234 FINAL void _writeTag  (NGStreamCoder *self, NGTagType _tag);
235
236 FINAL void _writeChar (NGStreamCoder *self, char _value);
237 FINAL void _writeShort(NGStreamCoder *self, short _value);
238 FINAL void _writeInt  (NGStreamCoder *self, int _value);
239 FINAL void _writeLong (NGStreamCoder *self, long _value);
240 FINAL void _writeFloat(NGStreamCoder *self, float _value);
241
242 FINAL void _writeCString(NGStreamCoder *self, const char *_value);
243 FINAL void _writeObjC(NGStreamCoder *self, const void *_value, const char *_type);
244
245 // primitive decoding
246
247 FINAL void _readBytes(NGStreamCoder *self, void *_bytes, unsigned _len);
248
249 FINAL NGTagType _readTag(NGStreamCoder *self);
250
251 FINAL char  _readChar (NGStreamCoder *self);
252 FINAL short _readShort(NGStreamCoder *self);
253 FINAL int   _readInt  (NGStreamCoder *self);
254 FINAL long  _readLong (NGStreamCoder *self);
255 FINAL float _readFloat(NGStreamCoder *self);
256
257 FINAL char *_readCString(NGStreamCoder *self);
258 FINAL void _readObjC(NGStreamCoder *self, void *_value, const char *_type);
259
260 // -------------------- encoding --------------------
261
262 - (void)beginEncoding {
263   self->traceMode       = NO;
264   self->encodingRoot    = YES;
265 }
266 - (void)endEncoding {
267   NSResetHashTable(self->outObjects);
268   NSResetHashTable(self->outConditionals);
269   NSResetHashTable(self->outPointers);
270   NSResetMapTable(self->replacements);
271   self->traceMode      = NO;
272   self->encodingRoot   = NO;
273 }
274
275 - (void)writeArchiveHeader {
276   if (self->didWriteHeader == NO) {
277     _writeCString(self, [[self coderSignature] cString]);
278     _writeInt(self, [self coderVersion]);
279     self->didWriteHeader = YES;
280   }
281 }
282 - (void)writeArchiveTrailer {
283 }
284
285 - (void)traceObjectsWithRoot:(id)_root {
286   // encoding pass 1
287
288   NS_DURING {
289     self->traceMode = YES;
290
291     [self encodeObject:_root];
292   }
293   NS_HANDLER {
294     self->traceMode = NO;
295     NSResetHashTable(self->outObjects);
296     [localException raise];
297   }
298   NS_ENDHANDLER;
299   self->traceMode = NO;
300   NSResetHashTable(self->outObjects);
301 }
302
303 - (void)encodeObjectsWithRoot:(id)_root {
304   // encoding pass 2
305   [self encodeObject:_root];
306 }
307
308 - (void)encodeRootObject:(id)_object {
309   NSAutoreleasePool *pool = [[NSAutoreleasePool allocWithZone:[self zone]] init];
310   
311   [self beginEncoding];
312
313   NS_DURING {
314     /*
315      * Prepare for writing the graph objects for which `rootObject' is the root
316      * node. The algorithm consists from two passes. In the first pass it
317      * determines the nodes so-called 'conditionals' - the nodes encoded *only*
318      * with -encodeConditionalObject:. They represent nodes that are not
319      * related directly to the graph. In the second pass objects are encoded
320      * normally, except for the conditional objects which are encoded as nil.
321      */
322
323     // pass1: start tracing for conditionals
324     [self traceObjectsWithRoot:_object];
325
326     // pass2: start writing
327     [self writeArchiveHeader];
328     [self encodeObjectsWithRoot:_object];
329     [self writeArchiveTrailer];
330   }
331   NS_HANDLER {
332     [self endEncoding]; // release resources
333     [localException raise];
334   }
335   NS_ENDHANDLER;
336   [self endEncoding]; // release resources
337
338   [pool release]; pool = nil;
339 }
340
341 - (void)encodeConditionalObject:(id)_object {
342   if (self->traceMode) { // pass 1
343     /*
344      * This is the first pass of the determining the conditionals
345      * algorithm. We traverse the graph and insert into the `conditionals'
346      * set. In the second pass all objects that are still in this set will
347      * be encoded as nil when they receive -encodeConditionalObject:. An
348      * object is removed from this set when it receives -encodeObject:.
349      */
350
351     if (_object) {
352       if (NSHashGet(self->outObjects, _object))
353         // object isn't conditional any more .. (was stored using encodeObject:)
354         ;
355       else if (NSHashGet(self->outConditionals, _object))
356         // object is already stored as conditional
357         ;
358       else
359         // insert object in conditionals set
360         NSHashInsert(self->outConditionals, _object);
361     }
362   }
363   else { // pass 2
364     BOOL isConditional;
365
366     isConditional = (NSHashGet(self->outConditionals, _object) != nil);
367
368     // If anObject is still in the `conditionals' set, it is encoded as nil.
369     [self encodeObject:isConditional ? nil : _object];
370   }
371 }
372
373 - (void)_traceObject:(id)_object {
374   if (_object == nil) // don't trace nil objects ..
375     return;
376
377   if (NSHashGet(self->outObjects, _object) == nil) { // object wasn't traced yet
378     // Look-up the object in the `conditionals' set. If the object is
379     // there, then remove it because it is no longer a conditional one.
380     if (NSHashGet(self->outConditionals, _object)) {
381       // object was marked conditional ..
382       NSHashRemove(self->outConditionals, _object);
383     }
384
385     // mark object as traced
386     NSHashInsert(self->outObjects, _object);
387       
388     if (object_is_instance(_object)) {
389       Class archiveClass = Nil;
390       id    replacement  = nil;
391
392       replacement = [_object performSelector:self->replObjectForCoder
393                              withObject:self];
394
395       if (replacement != _object) {
396         NSMapInsert(self->replacements, _object, replacement);
397         _object = replacement;
398       }
399       
400       if (object_is_instance(_object)) {
401         archiveClass = [_object performSelector:self->classForCoder
402                                 withObject:self];
403       }
404         
405       [self encodeObject:archiveClass];
406       [_object encodeWithCoder:self];
407     }
408     else {
409       // there are no class-variables ..
410     }
411   }
412 }
413 - (void)_encodeObject:(id)_object {
414   NGTagType tag;
415   int       archiveId = _archiveIdOfObject(self, _object);
416
417   tag = object_is_instance(_object) ? _C_ID : _C_CLASS;
418     
419   if (_object == nil) { // nil object
420 #if 0
421     NSLog(@"encoding nil reference ..");
422 #endif
423     _writeTag(self, tag | REFERENCE);
424     _writeInt(self, archiveId);
425   }
426   else if (NSHashGet(self->outObjects, _object)) { // object was already written
427 #if 0
428     if (tag == _C_CLASS) {
429       NSLog(@"encoding reference to class <%s> ..",
430              class_get_class_name(_object));
431     }
432     else {
433       NSLog(@"encoding reference to object 0x%08X<%s> ..",
434              _object, class_get_class_name(*(Class *)_object));
435     }
436 #endif
437
438     _writeTag(self, tag | REFERENCE);
439     _writeInt(self, archiveId);
440   }
441   else {
442     // mark object as written
443     NSHashInsert(self->outObjects, _object);
444
445 #if 0
446     if (tag == _C_CLASS) { // a class object
447       NSLog( @"encoding class %s:%i ..",
448              class_get_class_name(_object), [_object version]);
449     }
450     else {
451       NSLog(@"encoding object 0x%08X<%s> ..",
452              _object, class_get_class_name(*(Class *)_object));
453     }
454 #endif
455     
456     _writeTag(self, tag);
457     _writeInt(self, archiveId);
458
459     if (tag == _C_CLASS) { // a class object
460       _writeCString(self, class_get_class_name(_object));
461       _writeInt(self, [_object version]);
462     }
463     else {
464       Class archiveClass = Nil;
465       id    replacement  = nil;
466
467       replacement = NSMapGet(self->replacements, _object);
468       if (replacement) _object = replacement;
469
470       /*
471       _object      = [_object performSelector:self->replObjectForCoder
472                               withObject:self];
473       */
474       archiveClass = [_object performSelector:self->classForCoder
475                               withObject:self]; // class of replacement
476
477       NSAssert(archiveClass, @"no archive class found ..");
478
479       [self encodeObject:archiveClass];
480       [_object encodeWithCoder:self];
481     }
482   }
483 }
484
485 - (void)encodeObject:(id)_object {
486   if (self->encodingRoot) {
487     [self encodeValueOfObjCType:object_is_instance(_object) ? "@" : "#"
488           at:&_object];
489   }
490   else {
491     [self encodeRootObject:_object];
492   }
493 }
494
495 - (void)_traceValueOfObjCType:(const char *)_type at:(const void *)_value {
496 #if 0
497   NSLog(@"tracing value of ObjC-type '%s'", _type);
498 #endif
499
500   switch (*_type) {
501     case _C_ID:
502     case _C_CLASS:
503       [self _traceObject:*(id *)_value];
504       break;
505
506     case _C_ARY_B: {
507       int        count     = atoi(_type + 1); // eg '[15I' => count = 15
508       const char *itemType = _type;
509       while(isdigit((int)*(++itemType))) ; // skip dimension
510       [self encodeArrayOfObjCType:itemType count:count at:_value];
511       break;
512     }
513
514     case _C_STRUCT_B: { // C-structure begin '{'
515       int offset = 0;
516
517       while ((*_type != _C_STRUCT_E) && (*_type++ != '=')); // skip "<name>="
518         
519       while (YES) {
520         [self encodeValueOfObjCType:_type at:((char *)_value) + offset];
521             
522         offset += objc_sizeof_type(_type);
523         _type  =  objc_skip_typespec(_type);
524             
525         if(*_type != _C_STRUCT_E) { // C-structure end '}'
526           int align, remainder;
527                     
528           align = objc_alignof_type(_type);
529           if((remainder = offset % align))
530             offset += (align - remainder);
531         }
532         else
533           break;
534       }
535       break;
536     }
537   }
538 }
539
540 - (void)_encodeValueOfObjCType:(const char *)_type at:(const void *)_value {
541   switch (*_type) {
542     case _C_ID:
543     case _C_CLASS:
544       // ?? Write another tag just to be possible to read using the
545       // ?? decodeObject method. (Otherwise a lookahead would be required)
546       // ?? _writeTag(self, *_type);
547       [self _encodeObject:*(id *)_value];
548       break;
549
550     case _C_ARY_B: {
551       int        count     = atoi(_type + 1); // eg '[15I' => count = 15
552       const char *itemType = _type;
553
554       while(isdigit((int)*(++itemType))) ; // skip dimension
555
556       // Write another tag just to be possible to read using the
557       // decodeArrayOfObjCType:count:at: method.
558       _writeTag(self, _C_ARY_B);
559       [self encodeArrayOfObjCType:itemType count:count at:_value];
560       break;
561     }
562
563     case _C_STRUCT_B: { // C-structure begin '{'
564       int offset = 0;
565
566       _writeTag(self, '{');
567
568       while ((*_type != _C_STRUCT_E) && (*_type++ != '=')); // skip "<name>="
569         
570       while (YES) {
571         [self encodeValueOfObjCType:_type at:((char *)_value) + offset];
572             
573         offset += objc_sizeof_type(_type);
574         _type  =  objc_skip_typespec(_type);
575             
576         if(*_type != _C_STRUCT_E) { // C-structure end '}'
577           int align, remainder;
578                     
579           align = objc_alignof_type(_type);
580           if((remainder = offset % align))
581             offset += (align - remainder);
582         }
583         else
584           break;
585       }
586       break;
587     }
588
589     case _C_SEL:
590       _writeTag(self, _C_SEL);
591       _writeCString(self, (*(SEL *)_value) ? sel_get_name(*(SEL *)_value) : NULL);
592       break;
593       
594     case _C_PTR:
595       _writeTag(self, *_type);
596       _writeObjC(self, *(char **)_value, _type + 1);
597       break;
598     case _C_CHARPTR:
599       _writeTag(self, *_type);
600       _writeObjC(self, _value, _type);
601       break;
602       
603     case _C_CHR:    case _C_UCHR:
604     case _C_SHT:    case _C_USHT:
605     case _C_INT:    case _C_UINT:
606     case _C_LNG:    case _C_ULNG:
607     case _C_FLT:    case _C_DBL:
608       _writeTag(self, *_type);
609       _writeObjC(self, _value, _type);
610       break;
611       
612     default:
613       NSLog(@"unsupported C type '%s' ..", _type);
614       break;
615   }
616 }
617
618 - (void)encodeValueOfObjCType:(const char *)_type at:(const void *)_value {
619   if (self->traceMode)
620     [self _traceValueOfObjCType:_type at:_value];
621   else {
622     if (self->didWriteHeader == NO)
623       [self writeArchiveHeader];
624   
625     [self _encodeValueOfObjCType:_type at:_value];
626   }
627 }
628
629 - (void)encodeArrayOfObjCType:(const char *)_type count:(unsigned int)_count
630   at:(const void *)_array {
631
632   if ((self->didWriteHeader == NO) && (self->traceMode == NO))
633     [self writeArchiveHeader];
634
635   // array header
636   if (self->traceMode == NO) { // nothing is written during trace-mode
637     _writeTag(self, _C_ARY_B);
638     _writeInt(self, _count);
639   }
640
641   // Optimize writing arrays of elementary types. If such an array has to
642   // be written, write the type and then the elements of array.
643
644   if ((*_type == _C_ID) || (*_type == _C_CLASS)) { // object array
645     int i;
646
647     if (self->traceMode == NO)
648       _writeTag(self, *_type); // object array
649
650     for (i = 0; i < _count; i++)
651       [self encodeObject:((id *)_array)[i]];
652   }
653   else if ((*_type == _C_CHR) || (*_type == _C_UCHR)) { // byte array
654     if (self->traceMode == NO) {
655
656       // write base type tag
657       _writeTag(self, *_type);
658
659       // write buffer
660       _writeBytes(self, _array, _count);
661     }
662   }
663   else if (isBaseType(_type)) {
664     if (self->traceMode == NO) {
665       unsigned offset, itemSize = objc_sizeof_type(_type);
666       int      i;
667
668       // write base type tag
669       _writeTag(self, *_type);
670
671       // write contents
672       for (i = offset = 0; i < _count; i++, offset += itemSize)
673         _writeObjC(self, (char *)_array + offset, _type);
674     }
675   }
676   else { // encoded using normal method
677     IMP      encodeValue = NULL;
678     unsigned offset, itemSize = objc_sizeof_type(_type);
679     int      i;
680
681     encodeValue = [self methodForSelector:@selector(encodeValueOfObjCType:at:)];
682
683     for (i = offset = 0; i < _count; i++, offset += itemSize) {
684       encodeValue(self, @selector(encodeValueOfObjCType:at:),
685                   (char *)_array + offset, _type);
686     }
687   }
688 }
689
690 // -------------------- decoding --------------------
691
692 - (void)decodeArchiveHeader {
693   if (self->didReadHeader == NO) {
694     char *archiver = _readCString(self);
695
696     self->inArchiverVersion = _readInt(self);
697
698     if (strcmp(archiver, [[self coderSignature] cString])) {
699       NSLog(@"WARNING: used a different archiver (signature %s:%i)",
700             archiver, [self systemVersion]);
701     }
702     else if ([self systemVersion] != [self coderVersion]) {
703       NSLog(@"WARNING: used a different archiver version "
704             @"(archiver=%i, unarchiver=%i)",
705             [self systemVersion], [self coderVersion]);
706     }
707
708     if (archiver) {
709       NGFree(archiver);
710       archiver = NULL;
711     }
712     self->didReadHeader = YES;
713   }
714 }
715
716 - (void)beginDecoding {
717 #if 0
718   NSLog(@"start decoding ..");
719 #endif
720   [self decodeArchiveHeader];
721 }
722 - (void)endDecoding {
723 #if 0
724   NSLog(@"finish decoding ..");
725 #endif
726   NSResetMapTable(self->inObjects);
727   NSResetMapTable(self->inClasses);
728   NSResetMapTable(self->inPointers);
729   NSResetMapTable(self->inClassAlias);
730   NSResetMapTable(self->inClassVersions);
731 }
732
733 - (Class)_decodeClass:(BOOL)_isReference {
734   int   archiveId = _readInt(self);
735   Class result    = Nil;
736   
737   if (_isReference) {
738     result = (Class)NSMapGet(self->inClasses, (void *)archiveId);
739     if (result == Nil) {
740       NSLog(@"did not find class for archive-id %i", archiveId);
741     }
742   }
743   else {
744     NSString *name   = NULL;
745     int      version = 0;
746
747     name    = [NSString stringWithCString:_readCString(self)];
748     version = _readInt(self);
749
750     if (name == nil) {
751       [NSException raise:NSInconsistentArchiveException
752                    format:@"did not find class name"];
753     }
754
755     { // check whether the class is to be replaced
756       NSString *newName = NSMapGet(self->inClassAlias, name);
757       
758       if (newName)
759         name = newName;
760       else {
761         newName = NSMapGet(classToAliasMappings, name);
762         if (newName)
763           name = newName;
764       }
765     }
766     
767     result = NSClassFromString(name);
768 #if 0
769     NSLog(@"decoded class %@:%i (result=%@).", name, version, result);
770 #endif
771     
772     NSAssert([result version] == version, @"class versions do not match ..");
773
774     NSMapInsert(self->inClasses, (void *)archiveId, result);
775   }
776   
777   NSAssert(result, @"class may not be Nil ..");
778   
779   return result;
780 }
781 - (id)_decodeObject:(BOOL)_isReference {
782   // this method returns a retained object !
783   int archiveId = _readInt(self);
784   id  result    = nil;
785
786   if (archiveId == 0) // nil object or unused conditional object
787     return nil;
788   
789   if (_isReference) {
790     result = [(id)NSMapGet(self->inObjects, (void *)archiveId) retain];
791   }
792   else {
793     Class class       = Nil;
794     id    replacement = nil;
795
796     // decode class info
797     [self decodeValueOfObjCType:"#" at:&class];
798     NSAssert(class, @"invalid class ..");
799     
800     result = [class allocWithZone:self->objectZone];
801     NSMapInsert(self->inObjects, (void *)archiveId, result);
802
803     replacement = [result initWithCoder:self];
804     if (replacement != result) {
805
806       replacement = [replacement retain];
807       NSMapRemove(self->inObjects, result);
808       result = replacement;
809       NSMapInsert(self->inObjects, (void *)archiveId, result);
810       [replacement release];
811     }
812
813     replacement = [result awakeAfterUsingCoder:self];
814     if (replacement != result) {
815       replacement = [replacement retain];
816       NSMapRemove(self->inObjects, result);
817       result = replacement;
818       NSMapInsert(self->inObjects, (void *)archiveId, result);
819       [replacement release];
820     }
821   }
822   NSAssert([result retainCount] > 0, @"invalid retain count ..");
823   return result;
824 }
825
826 - (id)decodeObject {
827   id result = nil;
828
829   [self decodeValueOfObjCType:"@" at:&result];
830   
831   // result is retained
832   return [result autorelease];
833 }
834
835 - (void)decodeValueOfObjCType:(const char *)_type at:(void *)_value {
836   BOOL      startedDecoding = NO;
837   NGTagType tag             = 0;
838   BOOL      isReference     = NO;
839
840   if (self->decodingRoot == NO) {
841     self->decodingRoot = YES;
842     startedDecoding = YES;
843     [self beginDecoding];
844   }
845
846   tag         = _readTag(self);
847   isReference = isReferenceTag(tag);
848   tag         = tagValue(tag);
849
850   switch (tag) {
851     case _C_ID:
852       NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
853       *(id *)_value = [self _decodeObject:isReference];
854       break;
855     case _C_CLASS:
856       NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
857       *(Class *)_value = [self _decodeClass:isReference];
858       break;
859
860     case _C_ARY_B: {
861       int        count     = atoi(_type + 1); // eg '[15I' => count = 15
862       const char *itemType = _type;
863
864       NSAssert(*_type == _C_ARY_B, @"invalid type ..");
865
866       while(isdigit((int)*(++itemType))) ; // skip dimension
867
868       [self decodeArrayOfObjCType:itemType count:count at:_value];
869       break;
870     }
871
872     case _C_STRUCT_B: {
873       int offset = 0;
874
875       NSAssert(*_type == _C_STRUCT_B, @"invalid type ..");
876       
877       while ((*_type != _C_STRUCT_E) && (*_type++ != '=')); // skip "<name>="
878         
879       while (YES) {
880         [self decodeValueOfObjCType:_type at:((char *)_value) + offset];
881             
882         offset += objc_sizeof_type(_type);
883         _type  =  objc_skip_typespec(_type);
884             
885         if(*_type != _C_STRUCT_E) { // C-structure end '}'
886           int align, remainder;
887                     
888           align = objc_alignof_type(_type);
889           if((remainder = offset % align))
890             offset += (align - remainder);
891         }
892         else
893           break;
894       }
895       break;
896     }
897
898     case _C_SEL: {
899       char *name = NULL;
900       
901       NSAssert(*_type == tag, @"invalid type ..");
902       _readObjC(self, &name, @encode(char *));
903       *(SEL *)_value = name ? sel_get_any_uid(name) : NULL;
904       NGFree(name); name = NULL;
905     }
906
907     case _C_PTR:
908       _readObjC(self, *(char **)_value, _type + 1); // skip '^'
909       break;
910       
911     case _C_CHARPTR:
912     case _C_CHR:    case _C_UCHR:
913     case _C_SHT:    case _C_USHT:
914     case _C_INT:    case _C_UINT:
915     case _C_LNG:    case _C_ULNG:
916     case _C_FLT:    case _C_DBL:
917       NSAssert(*_type == tag, @"invalid type ..");
918       _readObjC(self, _value, _type);
919       break;
920       
921     default:
922       NSAssert2(0, @"unsupported tag '%c', type %s ..", tag, _type);
923       break;
924   }
925
926   if (startedDecoding) {
927     [self endDecoding];
928     self->decodingRoot = NO;
929   }
930 }
931
932 - (void)decodeArrayOfObjCType:(const char *)_type count:(unsigned int)_count
933   at:(void *)_array {
934
935   BOOL      startedDecoding = NO;
936   NGTagType tag   = _readTag(self);
937   int       count = _readInt(self);
938
939   if (self->decodingRoot == NO) {
940     self->decodingRoot = YES;
941     startedDecoding = YES;
942     [self beginDecoding];
943   }
944
945 #if 0
946   NSLog(@"decoding array[%i/%i] of ObjC-type '%s' array-tag='%c'",
947          _count, count, _type, tag);
948 #endif
949   
950   NSAssert(tag == _C_ARY_B, @"invalid type ..");
951   NSAssert(count == _count, @"invalid array size ..");
952
953   // Arrays of elementary types are written optimized: the type is written
954   // then the elements of array follow.
955   if ((*_type == _C_ID) || (*_type == _C_CLASS)) { // object array
956     int i;
957
958 #if 0
959     NSLog(@"decoding object-array[%i] type='%s'", _count, _type);
960 #endif
961
962     tag = _readTag(self); // object array
963     NSAssert(tag == *_type, @"invalid array element type ..");
964       
965     for (i = 0; i < _count; i++)
966       ((id *)_array)[i] = [self decodeObject];
967   }
968   else if ((*_type == _C_CHR) || (*_type == _C_UCHR)) { // byte array
969     tag = _readTag(self);
970     NSAssert((tag == _C_CHR) || (tag == _C_UCHR), @"invalid byte array type ..");
971
972 #if 0
973     NSLog(@"decoding byte-array[%i] type='%s' tag='%c'",
974            _count, _type, tag);
975 #endif
976
977     // read buffer
978     _readBytes(self, _array, _count);
979   }
980   else if (isBaseType(_type)) {
981     unsigned offset, itemSize = objc_sizeof_type(_type);
982     int      i;
983       
984     tag = _readTag(self);
985     NSAssert(tag == *_type, @"invalid array base type ..");
986
987     for (i = offset = 0; i < _count; i++, offset += itemSize)
988       _readObjC(self, (char *)_array + offset, _type);
989   }
990   else {
991     IMP      decodeValue = NULL;
992     unsigned offset, itemSize = objc_sizeof_type(_type);
993     int      i;
994
995     decodeValue = [self methodForSelector:@selector(decodeValueOfObjCType:at:)];
996     
997     for (i = offset = 0; i < count; i++, offset += itemSize) {
998       decodeValue(self, @selector(decodeValueOfObjCType:at:),
999                   (char *)_array + offset, _type);
1000     }
1001   }
1002
1003   if (startedDecoding) {
1004     [self endDecoding];
1005     self->decodingRoot = NO;
1006   }
1007 }
1008
1009 // Substituting One Class for Another
1010
1011 + (NSString *)classNameDecodedForArchiveClassName:(NSString *)nameInArchive {
1012   NSString *className = NSMapGet(classToAliasMappings, nameInArchive);
1013   return className ? className : nameInArchive;
1014 }
1015 + (void)decodeClassName:(NSString *)nameInArchive asClassName:(NSString *)trueName {
1016   NSMapInsert(classToAliasMappings, nameInArchive, trueName);
1017 }
1018
1019 - (NSString *)classNameDecodedForArchiveClassName:(NSString *)_nameInArchive {
1020   NSString *className = NSMapGet(self->inClassAlias, _nameInArchive);
1021   return className ? className : _nameInArchive;
1022 }
1023 - (void)decodeClassName:(NSString *)nameInArchive asClassName:(NSString *)trueName {
1024   NSMapInsert(self->inClassAlias, nameInArchive, trueName);
1025 }
1026
1027 // ******************** primitives ********************
1028
1029 // encoding
1030
1031 FINAL void _writeBytes(NGStreamCoder *self, const void *_bytes, unsigned _len) {
1032   NSCAssert(self->traceMode == NO, @"nothing can be written during trace-mode ..");
1033   
1034   self->writeIMP
1035     ? self->writeIMP(self->stream, @selector(safeWriteBytes:count:), _bytes, _len)
1036     : [self->stream safeWriteBytes:_bytes count:_len];
1037 }
1038
1039 FINAL void _writeTag(NGStreamCoder *self, NGTagType _tag) {
1040   NSCAssert(self, @"invalid self ..");
1041 #if 0
1042   NSLog(@"write tag '%s%c'",
1043         isReferenceTag(_tag) ? "&" : "", tagValue(_tag));
1044 #endif
1045   
1046   [self->stream serializeChar:_tag];
1047 }
1048
1049 FINAL void _writeChar(NGStreamCoder *self, char _value) {
1050   [self->stream serializeChar:_value];
1051 }
1052 FINAL void _writeShort(NGStreamCoder *self, short _value) {
1053   [self->stream serializeShort:_value];
1054 }
1055 FINAL void _writeInt(NGStreamCoder *self, int _value) {
1056   [self->stream serializeInt:_value];
1057 }
1058 FINAL void _writeLong(NGStreamCoder *self, long _value) {
1059   [self->stream serializeLong:_value];
1060 }
1061 FINAL void _writeFloat(NGStreamCoder *self, float _value) {
1062   [self->stream serializeFloat:_value];
1063 }
1064
1065 FINAL void _writeCString(NGStreamCoder *self, const char *_value) {
1066   [(id)self->stream serializeDataAt:&_value ofObjCType:@encode(char *) context:self];
1067 }
1068
1069 FINAL void _writeObjC(NGStreamCoder *self,
1070                               const void *_value, const char *_type) {
1071   if ((_value == NULL) || (_type == NULL))
1072     return;
1073
1074   if (self->traceMode) {
1075     // no need to track base-types in trace-mode
1076     
1077     switch (*_type) {
1078       case _C_ID:
1079       case _C_CLASS:
1080       case _C_CHARPTR:
1081       case _C_ARY_B:
1082       case _C_STRUCT_B:
1083       case _C_PTR:
1084         [(id)self->stream serializeDataAt:_value ofObjCType:_type context:self];
1085         break;
1086
1087       default:
1088         break;
1089     }
1090   }
1091   else {
1092     [(id)self->stream serializeDataAt:_value ofObjCType:_type context:self];
1093   }
1094 }
1095
1096 // decoding
1097
1098 FINAL void _readBytes(NGStreamCoder *self, void *_bytes, unsigned _len) {
1099   self->readIMP
1100     ? self->readIMP(self->stream, @selector(safeReadBytes:count:), _bytes, _len)
1101     : [self->stream safeReadBytes:_bytes count:_len];
1102 }
1103
1104 FINAL NGTagType _readTag(NGStreamCoder *self) {
1105   return [self->stream deserializeChar];
1106 }
1107 FINAL char _readChar(NGStreamCoder *self) {
1108   return [self->stream deserializeChar];
1109 }
1110 FINAL short _readShort(NGStreamCoder *self) {
1111   return [self->stream deserializeShort];
1112 }
1113 FINAL int _readInt(NGStreamCoder *self) {
1114   return [self->stream deserializeInt];
1115 }
1116 FINAL long _readLong (NGStreamCoder *self) {
1117   return [self->stream deserializeLong];
1118 }
1119 FINAL float _readFloat(NGStreamCoder *self) {
1120   return [self->stream deserializeFloat];
1121 }
1122
1123 FINAL char *_readCString(NGStreamCoder *self) {
1124   char *result = NULL;
1125   [(id)self->stream deserializeDataAt:&result ofObjCType:@encode(char *) context:self];
1126   return result;
1127 }
1128
1129 FINAL void _readObjC(NGStreamCoder *self, void *_value, const char *_type) {
1130   [(id)self->stream deserializeDataAt:_value ofObjCType:_type context:(id)self];
1131 }
1132
1133 // NSObjCTypeSerializationCallBack
1134
1135 - (void)serializeObjectAt:(id *)_object ofObjCType:(const char *)_type
1136   intoData:(NSMutableData *)_data {
1137
1138   switch (*_type) {
1139     case _C_ID:
1140     case _C_CLASS:
1141       if (self->traceMode)
1142         [self _traceObject:*_object];
1143       else
1144         [self _encodeObject:*_object];
1145       break;
1146         
1147     default:
1148       abort();
1149       break;
1150   }
1151 }
1152
1153 - (void)deserializeObjectAt:(id *)_object ofObjCType:(const char *)_type
1154   fromData:(NSData *)_data atCursor:(unsigned int *)_cursor {
1155
1156   NGTagType tag             = 0;
1157   BOOL      isReference     = NO;
1158
1159   tag         = _readTag(self);
1160   isReference = isReferenceTag(tag);
1161   tag         = tagValue(tag);
1162   
1163   switch (*_type) {
1164     case _C_ID:
1165       NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
1166       break;
1167       *_object = [self _decodeObject:isReference];
1168       break;
1169     case _C_CLASS:
1170       NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
1171       *_object = [self _decodeClass:isReference];
1172       break;
1173       
1174     default:
1175       abort();
1176       break;
1177   }
1178 }
1179
1180 @end