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
33extern id nil_method(id, SEL, ...);
34
35/*
36  Debugging topics:
37    encoder
38    decoder
39*/
40
41typedef unsigned char NGTagType;
42
43#define REFERENCE 128
44#define VALUE     127
45
46static unsigned __NGHashPointer(void *table, const void *anObject)
47{
48    return (unsigned)((long)anObject / 4);
49}
50static BOOL __NGComparePointers(void *table,
51	const void *anObject1, const void *anObject2)
52{
53    return anObject1 == anObject2 ? YES : NO;
54}
55static void __NGRetainObjects(void *table, const void *anObject)
56{
57    (void)[(NSObject*)anObject retain];
58}
59static void __NGReleaseObjects(void *table, void *anObject)
60{
61    [(NSObject*)anObject release];
62}
63static NSString* __NGDescribePointers(void *table, const void *anObject)
64{
65    return [NSString stringWithFormat:@"%p", anObject];
66}
67
68static 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
77static const char *NGCoderSignature = "MDlink NGStreamCoder";
78static int        NGCoderVersion    = 1100;
79
80@implementation NGStreamCoder
81
82static 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
197FINAL 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
211FINAL BOOL isReferenceTag(NGTagType _tag) {
212  return (_tag & REFERENCE) ? YES : NO;
213}
214
215FINAL NGTagType tagValue(NGTagType _tag) {
216  return _tag & VALUE; // mask out bit 8
217}
218
219FINAL int _archiveIdOfObject(NGStreamCoder *self, id _object) {
220  return (_object == nil)
221    ? 0
222    : (int)_object;
223}
224FINAL int _archiveIdOfClass(NGStreamCoder *self, Class _class) {
225  return _archiveIdOfObject(self, _class);
226}
227
228
229// primitive encoding
230
231FINAL void _writeBytes(NGStreamCoder *self, const void *_bytes, unsigned _len);
232
233FINAL void _writeTag  (NGStreamCoder *self, NGTagType _tag);
234
235FINAL void _writeChar (NGStreamCoder *self, char _value);
236FINAL void _writeShort(NGStreamCoder *self, short _value);
237FINAL void _writeInt  (NGStreamCoder *self, int _value);
238FINAL void _writeLong (NGStreamCoder *self, long _value);
239FINAL void _writeFloat(NGStreamCoder *self, float _value);
240
241FINAL void _writeCString(NGStreamCoder *self, const char *_value);
242FINAL void _writeObjC(NGStreamCoder *self, const void *_value, const char *_type);
243
244// primitive decoding
245
246FINAL void _readBytes(NGStreamCoder *self, void *_bytes, unsigned _len);
247
248FINAL NGTagType _readTag(NGStreamCoder *self);
249
250FINAL char  _readChar (NGStreamCoder *self);
251FINAL short _readShort(NGStreamCoder *self);
252FINAL int   _readInt  (NGStreamCoder *self);
253FINAL long  _readLong (NGStreamCoder *self);
254FINAL float _readFloat(NGStreamCoder *self);
255
256FINAL char *_readCString(NGStreamCoder *self);
257FINAL 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_getName(*(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_registerName(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
1030FINAL 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
1038FINAL 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
1048FINAL void _writeChar(NGStreamCoder *self, char _value) {
1049  [self->stream serializeChar:_value];
1050}
1051FINAL void _writeShort(NGStreamCoder *self, short _value) {
1052  [self->stream serializeShort:_value];
1053}
1054FINAL void _writeInt(NGStreamCoder *self, int _value) {
1055  [self->stream serializeInt:_value];
1056}
1057FINAL void _writeLong(NGStreamCoder *self, long _value) {
1058  [self->stream serializeLong:_value];
1059}
1060FINAL void _writeFloat(NGStreamCoder *self, float _value) {
1061  [self->stream serializeFloat:_value];
1062}
1063
1064FINAL void _writeCString(NGStreamCoder *self, const char *_value) {
1065  [(id)self->stream serializeDataAt:&_value ofObjCType:@encode(char *) context:self];
1066}
1067
1068FINAL 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
1097FINAL 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
1103FINAL NGTagType _readTag(NGStreamCoder *self) {
1104  return [self->stream deserializeChar];
1105}
1106FINAL char _readChar(NGStreamCoder *self) {
1107  return [self->stream deserializeChar];
1108}
1109FINAL short _readShort(NGStreamCoder *self) {
1110  return [self->stream deserializeShort];
1111}
1112FINAL int _readInt(NGStreamCoder *self) {
1113  return [self->stream deserializeInt];
1114}
1115FINAL long _readLong (NGStreamCoder *self) {
1116  return [self->stream deserializeLong];
1117}
1118FINAL float _readFloat(NGStreamCoder *self) {
1119  return [self->stream deserializeFloat];
1120}
1121
1122FINAL char *_readCString(NGStreamCoder *self) {
1123  char *result = NULL;
1124  [(id)self->stream deserializeDataAt:&result ofObjCType:@encode(char *) context:self];
1125  return result;
1126}
1127
1128FINAL 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
1180