2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
24 #include "NGStreamCoder.h"
25 #include "NGStream+serialization.h"
27 #if APPLE_RUNTIME || NeXT_RUNTIME
28 # include <objc/objc-class.h>
31 #define FINAL static inline
33 extern id nil_method(id, SEL, ...);
41 typedef unsigned char NGTagType;
46 static unsigned __NGHashPointer(void *table, const void *anObject)
48 return (unsigned)((long)anObject / 4);
50 static BOOL __NGComparePointers(void *table,
51 const void *anObject1, const void *anObject2)
53 return anObject1 == anObject2 ? YES : NO;
55 static void __NGRetainObjects(void *table, const void *anObject)
57 (void)[(NSObject*)anObject retain];
59 static void __NGReleaseObjects(void *table, void *anObject)
61 [(NSObject*)anObject release];
63 static NSString* __NGDescribePointers(void *table, const void *anObject)
65 return [NSString stringWithFormat:@"%p", anObject];
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,
77 static const char *NGCoderSignature = "MDlink NGStreamCoder";
78 static int NGCoderVersion = 1100;
80 @implementation NGStreamCoder
82 static NSMapTable *classToAliasMappings = NULL; // archive name => decoded name
85 BOOL isInitialized = NO;
89 classToAliasMappings = NSCreateMapTable(NSObjectMapKeyCallBacks,
90 NSObjectMapValueCallBacks,
95 + (id)coderWithStream:(id<NGStream>)_stream {
96 return AUTORELEASE([[self alloc] initWithStream:_stream]);
99 - (id)initWithStream:(id<NGStream>)_stream mode:(NGStreamMode)_mode {
100 if ((self = [super init])) {
101 self->stream = [_stream retain];
103 self->classForCoder = @selector(classForCoder);
104 self->replObjectForCoder = @selector(replacementObjectForCoder:);
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:)];
113 if (NGCanReadInStreamMode(_mode)) { // setup decoder
114 self->inObjects = NSCreateMapTable(NSIntMapKeyCallBacks,
115 NSObjectMapValueCallBacks,
117 self->inClasses = NSCreateMapTable(NSIntMapKeyCallBacks,
118 NSObjectMapValueCallBacks,
120 self->inPointers = NSCreateMapTable(NSIntMapKeyCallBacks,
121 NSIntMapValueCallBacks,
123 self->inClassAlias = NSCreateMapTable(NSObjectMapKeyCallBacks,
124 NSObjectMapValueCallBacks,
126 self->inClassVersions = NSCreateMapTable(NSObjectMapKeyCallBacks,
127 NSObjectMapValueCallBacks,
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,
144 return [self initWithStream:nil mode:NGStreamMode_undefined];
146 - (id)initWithStream:(id<NGStream>)_stream {
147 return [self initWithStream:_stream mode:[_stream mode]];
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; }
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; }
173 [self->stream release]; self->stream = nil;
180 - (id<NGStream>)stream {
184 - (NSString *)coderSignature {
185 return [NSString stringWithCString:NGCoderSignature];
187 - (int)coderVersion {
188 return NGCoderVersion;
191 - (unsigned int)systemVersion {
192 return self->inArchiverVersion;
197 FINAL BOOL isBaseType(const char *_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:
211 FINAL BOOL isReferenceTag(NGTagType _tag) {
212 return (_tag & REFERENCE) ? YES : NO;
215 FINAL NGTagType tagValue(NGTagType _tag) {
216 return _tag & VALUE; // mask out bit 8
219 FINAL int _archiveIdOfObject(NGStreamCoder *self, id _object) {
220 return (_object == nil)
224 FINAL int _archiveIdOfClass(NGStreamCoder *self, Class _class) {
225 return _archiveIdOfObject(self, _class);
229 // primitive encoding
231 FINAL void _writeBytes(NGStreamCoder *self, const void *_bytes, unsigned _len);
233 FINAL void _writeTag (NGStreamCoder *self, NGTagType _tag);
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);
241 FINAL void _writeCString(NGStreamCoder *self, const char *_value);
242 FINAL void _writeObjC(NGStreamCoder *self, const void *_value, const char *_type);
244 // primitive decoding
246 FINAL void _readBytes(NGStreamCoder *self, void *_bytes, unsigned _len);
248 FINAL NGTagType _readTag(NGStreamCoder *self);
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);
256 FINAL char *_readCString(NGStreamCoder *self);
257 FINAL void _readObjC(NGStreamCoder *self, void *_value, const char *_type);
259 // -------------------- encoding --------------------
261 - (void)beginEncoding {
262 self->traceMode = NO;
263 self->encodingRoot = YES;
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;
274 - (void)writeArchiveHeader {
275 if (self->didWriteHeader == NO) {
276 _writeCString(self, [[self coderSignature] cString]);
277 _writeInt(self, [self coderVersion]);
278 self->didWriteHeader = YES;
281 - (void)writeArchiveTrailer {
284 - (void)traceObjectsWithRoot:(id)_root {
288 self->traceMode = YES;
290 [self encodeObject:_root];
293 self->traceMode = NO;
294 NSResetHashTable(self->outObjects);
295 [localException raise];
298 self->traceMode = NO;
299 NSResetHashTable(self->outObjects);
302 - (void)encodeObjectsWithRoot:(id)_root {
304 [self encodeObject:_root];
307 - (void)encodeRootObject:(id)_object {
308 NSAutoreleasePool *pool = [[NSAutoreleasePool allocWithZone:[self zone]] init];
310 [self beginEncoding];
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.
322 // pass1: start tracing for conditionals
323 [self traceObjectsWithRoot:_object];
325 // pass2: start writing
326 [self writeArchiveHeader];
327 [self encodeObjectsWithRoot:_object];
328 [self writeArchiveTrailer];
331 [self endEncoding]; // release resources
332 [localException raise];
335 [self endEncoding]; // release resources
337 [pool release]; pool = nil;
340 - (void)encodeConditionalObject:(id)_object {
341 if (self->traceMode) { // pass 1
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:.
351 if (NSHashGet(self->outObjects, _object))
352 // object isn't conditional any more .. (was stored using encodeObject:)
354 else if (NSHashGet(self->outConditionals, _object))
355 // object is already stored as conditional
358 // insert object in conditionals set
359 NSHashInsert(self->outConditionals, _object);
365 isConditional = (NSHashGet(self->outConditionals, _object) != nil);
367 // If anObject is still in the `conditionals' set, it is encoded as nil.
368 [self encodeObject:isConditional ? nil : _object];
372 - (void)_traceObject:(id)_object {
373 if (_object == nil) // don't trace nil objects ..
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);
384 // mark object as traced
385 NSHashInsert(self->outObjects, _object);
387 if (object_is_instance(_object)) {
388 Class archiveClass = Nil;
389 id replacement = nil;
391 replacement = [_object performSelector:self->replObjectForCoder
394 if (replacement != _object) {
395 NSMapInsert(self->replacements, _object, replacement);
396 _object = replacement;
399 if (object_is_instance(_object)) {
400 archiveClass = [_object performSelector:self->classForCoder
404 [self encodeObject:archiveClass];
405 [_object encodeWithCoder:self];
408 // there are no class-variables ..
412 - (void)_encodeObject:(id)_object {
414 int archiveId = _archiveIdOfObject(self, _object);
416 tag = object_is_instance(_object) ? _C_ID : _C_CLASS;
418 if (_object == nil) { // nil object
420 NSLog(@"encoding nil reference ..");
422 _writeTag(self, tag | REFERENCE);
423 _writeInt(self, archiveId);
425 else if (NSHashGet(self->outObjects, _object)) { // object was already written
427 if (tag == _C_CLASS) {
428 NSLog(@"encoding reference to class <%s> ..",
429 class_get_class_name(_object));
432 NSLog(@"encoding reference to object 0x%p<%s> ..",
433 _object, class_get_class_name(*(Class *)_object));
437 _writeTag(self, tag | REFERENCE);
438 _writeInt(self, archiveId);
441 // mark object as written
442 NSHashInsert(self->outObjects, _object);
445 if (tag == _C_CLASS) { // a class object
446 NSLog( @"encoding class %s:%i ..",
447 class_get_class_name(_object), [_object version]);
450 NSLog(@"encoding object 0x%p<%s> ..",
451 _object, class_get_class_name(*(Class *)_object));
455 _writeTag(self, tag);
456 _writeInt(self, archiveId);
458 if (tag == _C_CLASS) { // a class object
459 _writeCString(self, class_get_class_name(_object));
460 _writeInt(self, [_object version]);
463 Class archiveClass = Nil;
464 id replacement = nil;
466 replacement = NSMapGet(self->replacements, _object);
467 if (replacement) _object = replacement;
470 _object = [_object performSelector:self->replObjectForCoder
473 archiveClass = [_object performSelector:self->classForCoder
474 withObject:self]; // class of replacement
476 NSAssert(archiveClass, @"no archive class found ..");
478 [self encodeObject:archiveClass];
479 [_object encodeWithCoder:self];
484 - (void)encodeObject:(id)_object {
485 if (self->encodingRoot) {
486 [self encodeValueOfObjCType:object_is_instance(_object) ? "@" : "#"
490 [self encodeRootObject:_object];
494 - (void)_traceValueOfObjCType:(const char *)_type at:(const void *)_value {
496 NSLog(@"tracing value of ObjC-type '%s'", _type);
502 [self _traceObject:*(id *)_value];
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];
513 case _C_STRUCT_B: { // C-structure begin '{'
516 while ((*_type != _C_STRUCT_E) && (*_type++ != '=')); // skip "<name>="
519 [self encodeValueOfObjCType:_type at:((char *)_value) + offset];
521 offset += objc_sizeof_type(_type);
522 _type = objc_skip_typespec(_type);
524 if(*_type != _C_STRUCT_E) { // C-structure end '}'
525 int align, remainder;
527 align = objc_alignof_type(_type);
528 if((remainder = offset % align))
529 offset += (align - remainder);
539 - (void)_encodeValueOfObjCType:(const char *)_type at:(const void *)_value {
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];
550 int count = atoi(_type + 1); // eg '[15I' => count = 15
551 const char *itemType = _type;
553 while(isdigit((int)*(++itemType))) ; // skip dimension
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];
562 case _C_STRUCT_B: { // C-structure begin '{'
565 _writeTag(self, '{');
567 while ((*_type != _C_STRUCT_E) && (*_type++ != '=')); // skip "<name>="
570 [self encodeValueOfObjCType:_type at:((char *)_value) + offset];
572 offset += objc_sizeof_type(_type);
573 _type = objc_skip_typespec(_type);
575 if(*_type != _C_STRUCT_E) { // C-structure end '}'
576 int align, remainder;
578 align = objc_alignof_type(_type);
579 if((remainder = offset % align))
580 offset += (align - remainder);
589 _writeTag(self, _C_SEL);
590 _writeCString(self, (*(SEL *)_value) ? sel_get_name(*(SEL *)_value) : NULL);
594 _writeTag(self, *_type);
595 _writeObjC(self, *(char **)_value, _type + 1);
598 _writeTag(self, *_type);
599 _writeObjC(self, _value, _type);
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);
612 NSLog(@"unsupported C type '%s' ..", _type);
617 - (void)encodeValueOfObjCType:(const char *)_type at:(const void *)_value {
619 [self _traceValueOfObjCType:_type at:_value];
621 if (self->didWriteHeader == NO)
622 [self writeArchiveHeader];
624 [self _encodeValueOfObjCType:_type at:_value];
628 - (void)encodeArrayOfObjCType:(const char *)_type count:(unsigned int)_count
629 at:(const void *)_array {
631 if ((self->didWriteHeader == NO) && (self->traceMode == NO))
632 [self writeArchiveHeader];
635 if (self->traceMode == NO) { // nothing is written during trace-mode
636 _writeTag(self, _C_ARY_B);
637 _writeInt(self, _count);
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.
643 if ((*_type == _C_ID) || (*_type == _C_CLASS)) { // object array
646 if (self->traceMode == NO)
647 _writeTag(self, *_type); // object array
649 for (i = 0; i < _count; i++)
650 [self encodeObject:((id *)_array)[i]];
652 else if ((*_type == _C_CHR) || (*_type == _C_UCHR)) { // byte array
653 if (self->traceMode == NO) {
655 // write base type tag
656 _writeTag(self, *_type);
659 _writeBytes(self, _array, _count);
662 else if (isBaseType(_type)) {
663 if (self->traceMode == NO) {
664 unsigned offset, itemSize = objc_sizeof_type(_type);
667 // write base type tag
668 _writeTag(self, *_type);
671 for (i = offset = 0; i < _count; i++, offset += itemSize)
672 _writeObjC(self, (char *)_array + offset, _type);
675 else { // encoded using normal method
676 IMP encodeValue = NULL;
677 unsigned offset, itemSize = objc_sizeof_type(_type);
680 encodeValue = [self methodForSelector:@selector(encodeValueOfObjCType:at:)];
682 for (i = offset = 0; i < _count; i++, offset += itemSize) {
683 encodeValue(self, @selector(encodeValueOfObjCType:at:),
684 (char *)_array + offset, _type);
689 // -------------------- decoding --------------------
691 - (void)decodeArchiveHeader {
692 if (self->didReadHeader == NO) {
693 char *archiver = _readCString(self);
695 self->inArchiverVersion = _readInt(self);
697 if (strcmp(archiver, [[self coderSignature] cString])) {
698 NSLog(@"WARNING: used a different archiver (signature %s:%i)",
699 archiver, [self systemVersion]);
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]);
711 self->didReadHeader = YES;
715 - (void)beginDecoding {
717 NSLog(@"start decoding ..");
719 [self decodeArchiveHeader];
721 - (void)endDecoding {
723 NSLog(@"finish decoding ..");
725 NSResetMapTable(self->inObjects);
726 NSResetMapTable(self->inClasses);
727 NSResetMapTable(self->inPointers);
728 NSResetMapTable(self->inClassAlias);
729 NSResetMapTable(self->inClassVersions);
732 - (Class)_decodeClass:(BOOL)_isReference {
733 int archiveId = _readInt(self);
737 result = (Class)NSMapGet(self->inClasses, (void *)archiveId);
739 NSLog(@"did not find class for archive-id %i", archiveId);
743 NSString *name = NULL;
746 name = [NSString stringWithCString:_readCString(self)];
747 version = _readInt(self);
750 [NSException raise:NSInconsistentArchiveException
751 format:@"did not find class name"];
754 { // check whether the class is to be replaced
755 NSString *newName = NSMapGet(self->inClassAlias, name);
760 newName = NSMapGet(classToAliasMappings, name);
766 result = NSClassFromString(name);
768 NSLog(@"decoded class %@:%i (result=%@).", name, version, result);
771 NSAssert([result version] == version, @"class versions do not match ..");
773 NSMapInsert(self->inClasses, (void *)archiveId, result);
776 NSAssert(result, @"class may not be Nil ..");
780 - (id)_decodeObject:(BOOL)_isReference {
781 // this method returns a retained object !
782 int archiveId = _readInt(self);
785 if (archiveId == 0) // nil object or unused conditional object
789 result = [(id)NSMapGet(self->inObjects, (void *)archiveId) retain];
793 id replacement = nil;
796 [self decodeValueOfObjCType:"#" at:&class];
797 NSAssert(class, @"invalid class ..");
799 result = [class allocWithZone:self->objectZone];
800 NSMapInsert(self->inObjects, (void *)archiveId, result);
802 replacement = [result initWithCoder:self];
803 if (replacement != result) {
805 replacement = [replacement retain];
806 NSMapRemove(self->inObjects, result);
807 result = replacement;
808 NSMapInsert(self->inObjects, (void *)archiveId, result);
809 [replacement release];
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];
821 NSAssert([result retainCount] > 0, @"invalid retain count ..");
828 [self decodeValueOfObjCType:"@" at:&result];
830 // result is retained
831 return [result autorelease];
834 - (void)decodeValueOfObjCType:(const char *)_type at:(void *)_value {
835 BOOL startedDecoding = NO;
837 BOOL isReference = NO;
839 if (self->decodingRoot == NO) {
840 self->decodingRoot = YES;
841 startedDecoding = YES;
842 [self beginDecoding];
845 tag = _readTag(self);
846 isReference = isReferenceTag(tag);
851 NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
852 *(id *)_value = [self _decodeObject:isReference];
855 NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
856 *(Class *)_value = [self _decodeClass:isReference];
860 int count = atoi(_type + 1); // eg '[15I' => count = 15
861 const char *itemType = _type;
863 NSAssert(*_type == _C_ARY_B, @"invalid type ..");
865 while(isdigit((int)*(++itemType))) ; // skip dimension
867 [self decodeArrayOfObjCType:itemType count:count at:_value];
874 NSAssert(*_type == _C_STRUCT_B, @"invalid type ..");
876 while ((*_type != _C_STRUCT_E) && (*_type++ != '=')); // skip "<name>="
879 [self decodeValueOfObjCType:_type at:((char *)_value) + offset];
881 offset += objc_sizeof_type(_type);
882 _type = objc_skip_typespec(_type);
884 if(*_type != _C_STRUCT_E) { // C-structure end '}'
885 int align, remainder;
887 align = objc_alignof_type(_type);
888 if((remainder = offset % align))
889 offset += (align - remainder);
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;
907 _readObjC(self, *(char **)_value, _type + 1); // skip '^'
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);
921 NSAssert2(0, @"unsupported tag '%c', type %s ..", tag, _type);
925 if (startedDecoding) {
927 self->decodingRoot = NO;
931 - (void)decodeArrayOfObjCType:(const char *)_type count:(unsigned int)_count
934 BOOL startedDecoding = NO;
935 NGTagType tag = _readTag(self);
936 int count = _readInt(self);
938 if (self->decodingRoot == NO) {
939 self->decodingRoot = YES;
940 startedDecoding = YES;
941 [self beginDecoding];
945 NSLog(@"decoding array[%i/%i] of ObjC-type '%s' array-tag='%c'",
946 _count, count, _type, tag);
949 NSAssert(tag == _C_ARY_B, @"invalid type ..");
950 NSAssert(count == _count, @"invalid array size ..");
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
958 NSLog(@"decoding object-array[%i] type='%s'", _count, _type);
961 tag = _readTag(self); // object array
962 NSAssert(tag == *_type, @"invalid array element type ..");
964 for (i = 0; i < _count; i++)
965 ((id *)_array)[i] = [self decodeObject];
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 ..");
972 NSLog(@"decoding byte-array[%i] type='%s' tag='%c'",
977 _readBytes(self, _array, _count);
979 else if (isBaseType(_type)) {
980 unsigned offset, itemSize = objc_sizeof_type(_type);
983 tag = _readTag(self);
984 NSAssert(tag == *_type, @"invalid array base type ..");
986 for (i = offset = 0; i < _count; i++, offset += itemSize)
987 _readObjC(self, (char *)_array + offset, _type);
990 IMP decodeValue = NULL;
991 unsigned offset, itemSize = objc_sizeof_type(_type);
994 decodeValue = [self methodForSelector:@selector(decodeValueOfObjCType:at:)];
996 for (i = offset = 0; i < count; i++, offset += itemSize) {
997 decodeValue(self, @selector(decodeValueOfObjCType:at:),
998 (char *)_array + offset, _type);
1002 if (startedDecoding) {
1004 self->decodingRoot = NO;
1008 // Substituting One Class for Another
1010 + (NSString *)classNameDecodedForArchiveClassName:(NSString *)nameInArchive {
1011 NSString *className = NSMapGet(classToAliasMappings, nameInArchive);
1012 return className ? className : nameInArchive;
1014 + (void)decodeClassName:(NSString *)nameInArchive asClassName:(NSString *)trueName {
1015 NSMapInsert(classToAliasMappings, nameInArchive, trueName);
1018 - (NSString *)classNameDecodedForArchiveClassName:(NSString *)_nameInArchive {
1019 NSString *className = NSMapGet(self->inClassAlias, _nameInArchive);
1020 return className ? className : _nameInArchive;
1022 - (void)decodeClassName:(NSString *)nameInArchive asClassName:(NSString *)trueName {
1023 NSMapInsert(self->inClassAlias, nameInArchive, trueName);
1026 // ******************** primitives ********************
1030 FINAL void _writeBytes(NGStreamCoder *self, const void *_bytes, unsigned _len) {
1031 NSCAssert(self->traceMode == NO, @"nothing can be written during trace-mode ..");
1034 ? self->writeIMP(self->stream, @selector(safeWriteBytes:count:), _bytes, _len)
1035 : [self->stream safeWriteBytes:_bytes count:_len];
1038 FINAL void _writeTag(NGStreamCoder *self, NGTagType _tag) {
1039 NSCAssert(self, @"invalid self ..");
1041 NSLog(@"write tag '%s%c'",
1042 isReferenceTag(_tag) ? "&" : "", tagValue(_tag));
1045 [self->stream serializeChar:_tag];
1048 FINAL void _writeChar(NGStreamCoder *self, char _value) {
1049 [self->stream serializeChar:_value];
1051 FINAL void _writeShort(NGStreamCoder *self, short _value) {
1052 [self->stream serializeShort:_value];
1054 FINAL void _writeInt(NGStreamCoder *self, int _value) {
1055 [self->stream serializeInt:_value];
1057 FINAL void _writeLong(NGStreamCoder *self, long _value) {
1058 [self->stream serializeLong:_value];
1060 FINAL void _writeFloat(NGStreamCoder *self, float _value) {
1061 [self->stream serializeFloat:_value];
1064 FINAL void _writeCString(NGStreamCoder *self, const char *_value) {
1065 [(id)self->stream serializeDataAt:&_value ofObjCType:@encode(char *) context:self];
1068 FINAL void _writeObjC(NGStreamCoder *self,
1069 const void *_value, const char *_type) {
1070 if ((_value == NULL) || (_type == NULL))
1073 if (self->traceMode) {
1074 // no need to track base-types in trace-mode
1083 [(id)self->stream serializeDataAt:_value ofObjCType:_type context:self];
1091 [(id)self->stream serializeDataAt:_value ofObjCType:_type context:self];
1097 FINAL void _readBytes(NGStreamCoder *self, void *_bytes, unsigned _len) {
1099 ? self->readIMP(self->stream, @selector(safeReadBytes:count:), _bytes, _len)
1100 : [self->stream safeReadBytes:_bytes count:_len];
1103 FINAL NGTagType _readTag(NGStreamCoder *self) {
1104 return [self->stream deserializeChar];
1106 FINAL char _readChar(NGStreamCoder *self) {
1107 return [self->stream deserializeChar];
1109 FINAL short _readShort(NGStreamCoder *self) {
1110 return [self->stream deserializeShort];
1112 FINAL int _readInt(NGStreamCoder *self) {
1113 return [self->stream deserializeInt];
1115 FINAL long _readLong (NGStreamCoder *self) {
1116 return [self->stream deserializeLong];
1118 FINAL float _readFloat(NGStreamCoder *self) {
1119 return [self->stream deserializeFloat];
1122 FINAL char *_readCString(NGStreamCoder *self) {
1123 char *result = NULL;
1124 [(id)self->stream deserializeDataAt:&result ofObjCType:@encode(char *) context:self];
1128 FINAL void _readObjC(NGStreamCoder *self, void *_value, const char *_type) {
1129 [(id)self->stream deserializeDataAt:_value ofObjCType:_type context:(id)self];
1132 // NSObjCTypeSerializationCallBack
1134 - (void)serializeObjectAt:(id *)_object ofObjCType:(const char *)_type
1135 intoData:(NSMutableData *)_data {
1140 if (self->traceMode)
1141 [self _traceObject:*_object];
1143 [self _encodeObject:*_object];
1152 - (void)deserializeObjectAt:(id *)_object ofObjCType:(const char *)_type
1153 fromData:(NSData *)_data atCursor:(unsigned int *)_cursor {
1156 BOOL isReference = NO;
1158 tag = _readTag(self);
1159 isReference = isReferenceTag(tag);
1160 tag = tagValue(tag);
1164 NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
1166 *_object = [self _decodeObject:isReference];
1169 NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
1170 *_object = [self _decodeClass:isReference];