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