2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
25 #include "NGStreamCoder.h"
26 #include "NGStream+serialization.h"
28 #if APPLE_RUNTIME || NeXT_RUNTIME
29 # include <objc/objc-class.h>
32 #define FINAL static inline
34 extern id nil_method(id, SEL, ...);
42 typedef unsigned char NGTagType;
47 static unsigned __NGHashPointer(void *table, const void *anObject)
49 return (unsigned)((long)anObject / 4);
51 static BOOL __NGComparePointers(void *table,
52 const void *anObject1, const void *anObject2)
54 return anObject1 == anObject2 ? YES : NO;
56 static void __NGRetainObjects(void *table, const void *anObject)
58 (void)[(NSObject*)anObject retain];
60 static void __NGReleaseObjects(void *table, void *anObject)
62 [(NSObject*)anObject release];
64 static NSString* __NGDescribePointers(void *table, const void *anObject)
66 return [NSString stringWithFormat:@"%p", anObject];
69 static NSMapTableKeyCallBacks NGIdentityObjectMapKeyCallbacks = {
70 (unsigned(*)(NSMapTable *, const void *)) __NGHashPointer,
71 (BOOL(*)(NSMapTable *, const void *, const void *))__NGComparePointers,
72 (void (*)(NSMapTable *, const void *anObject)) __NGRetainObjects,
73 (void (*)(NSMapTable *, void *anObject)) __NGReleaseObjects,
74 (NSString *(*)(NSMapTable *, const void *)) __NGDescribePointers,
78 static const char *NGCoderSignature = "MDlink NGStreamCoder";
79 static int NGCoderVersion = 1100;
81 @implementation NGStreamCoder
83 static NSMapTable *classToAliasMappings = NULL; // archive name => decoded name
86 BOOL isInitialized = NO;
90 classToAliasMappings = NSCreateMapTable(NSObjectMapKeyCallBacks,
91 NSObjectMapValueCallBacks,
96 + (id)coderWithStream:(id<NGStream>)_stream {
97 return AUTORELEASE([[self alloc] initWithStream:_stream]);
100 - (id)initWithStream:(id<NGStream>)_stream mode:(NGStreamMode)_mode {
101 if ((self = [super init])) {
102 self->stream = [_stream retain];
104 self->classForCoder = @selector(classForCoder);
105 self->replObjectForCoder = @selector(replacementObjectForCoder:);
107 if ([self->stream isKindOfClass:[NSObject class]]) {
108 self->readIMP = (NGIOSafeReadMethodType)
109 [(NSObject*)self->stream methodForSelector:@selector(safeReadBytes:count:)];
110 self->writeIMP = (NGIOSafeWriteMethodType)
111 [(NSObject*)self->stream methodForSelector:@selector(safeWriteBytes:count:)];
114 if (NGCanReadInStreamMode(_mode)) { // setup decoder
115 self->inObjects = NSCreateMapTable(NSIntMapKeyCallBacks,
116 NSObjectMapValueCallBacks,
118 self->inClasses = NSCreateMapTable(NSIntMapKeyCallBacks,
119 NSObjectMapValueCallBacks,
121 self->inPointers = NSCreateMapTable(NSIntMapKeyCallBacks,
122 NSIntMapValueCallBacks,
124 self->inClassAlias = NSCreateMapTable(NSObjectMapKeyCallBacks,
125 NSObjectMapValueCallBacks,
127 self->inClassVersions = NSCreateMapTable(NSObjectMapKeyCallBacks,
128 NSObjectMapValueCallBacks,
132 if (NGCanWriteInStreamMode(_mode)) { // setup encoder
133 self->outObjects = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 119);
134 self->outConditionals = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 119);
135 self->outPointers = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 0);
136 self->replacements = NSCreateMapTable(NGIdentityObjectMapKeyCallbacks,
137 NSObjectMapValueCallBacks,
145 return [self initWithStream:nil mode:NGStreamMode_undefined];
147 - (id)initWithStream:(id<NGStream>)_stream {
148 return [self initWithStream:_stream mode:[_stream mode]];
152 // release encoding restreams
153 if (self->outObjects) {
154 NSFreeHashTable(self->outObjects); self->outObjects = NULL; }
155 if (self->outConditionals) {
156 NSFreeHashTable(self->outConditionals); self->outConditionals = NULL; }
157 if (self->outPointers) {
158 NSFreeHashTable(self->outPointers); self->outPointers = NULL; }
159 if (self->replacements) {
160 NSFreeMapTable(self->replacements); self->replacements = NULL; }
162 // release decoding restreams
163 if (self->inObjects) {
164 NSFreeMapTable(self->inObjects); self->inObjects = NULL; }
165 if (self->inClasses) {
166 NSFreeMapTable(self->inClasses); self->inClasses = NULL; }
167 if (self->inPointers) {
168 NSFreeMapTable(self->inPointers); self->inPointers = NULL; }
169 if (self->inClassAlias) {
170 NSFreeMapTable(self->inClassAlias); self->inClassAlias = NULL; }
171 if (self->inClassVersions) {
172 NSFreeMapTable(self->inClassVersions); self->inClassVersions = NULL; }
174 [self->stream release]; self->stream = nil;
181 - (id<NGStream>)stream {
185 - (NSString *)coderSignature {
186 return [NSString stringWithCString:NGCoderSignature];
188 - (int)coderVersion {
189 return NGCoderVersion;
192 - (unsigned int)systemVersion {
193 return self->inArchiverVersion;
198 FINAL BOOL isBaseType(const char *_type) {
200 case _C_CHR: case _C_UCHR:
201 case _C_SHT: case _C_USHT:
202 case _C_INT: case _C_UINT:
203 case _C_LNG: case _C_ULNG:
204 case _C_FLT: case _C_DBL:
212 FINAL BOOL isReferenceTag(NGTagType _tag) {
213 return (_tag & REFERENCE) ? YES : NO;
216 FINAL NGTagType tagValue(NGTagType _tag) {
217 return _tag & VALUE; // mask out bit 8
220 FINAL int _archiveIdOfObject(NGStreamCoder *self, id _object) {
221 return (_object == nil)
225 FINAL int _archiveIdOfClass(NGStreamCoder *self, Class _class) {
226 return _archiveIdOfObject(self, _class);
230 // primitive encoding
232 FINAL void _writeBytes(NGStreamCoder *self, const void *_bytes, unsigned _len);
234 FINAL void _writeTag (NGStreamCoder *self, NGTagType _tag);
236 FINAL void _writeChar (NGStreamCoder *self, char _value);
237 FINAL void _writeShort(NGStreamCoder *self, short _value);
238 FINAL void _writeInt (NGStreamCoder *self, int _value);
239 FINAL void _writeLong (NGStreamCoder *self, long _value);
240 FINAL void _writeFloat(NGStreamCoder *self, float _value);
242 FINAL void _writeCString(NGStreamCoder *self, const char *_value);
243 FINAL void _writeObjC(NGStreamCoder *self, const void *_value, const char *_type);
245 // primitive decoding
247 FINAL void _readBytes(NGStreamCoder *self, void *_bytes, unsigned _len);
249 FINAL NGTagType _readTag(NGStreamCoder *self);
251 FINAL char _readChar (NGStreamCoder *self);
252 FINAL short _readShort(NGStreamCoder *self);
253 FINAL int _readInt (NGStreamCoder *self);
254 FINAL long _readLong (NGStreamCoder *self);
255 FINAL float _readFloat(NGStreamCoder *self);
257 FINAL char *_readCString(NGStreamCoder *self);
258 FINAL void _readObjC(NGStreamCoder *self, void *_value, const char *_type);
260 // -------------------- encoding --------------------
262 - (void)beginEncoding {
263 self->traceMode = NO;
264 self->encodingRoot = YES;
266 - (void)endEncoding {
267 NSResetHashTable(self->outObjects);
268 NSResetHashTable(self->outConditionals);
269 NSResetHashTable(self->outPointers);
270 NSResetMapTable(self->replacements);
271 self->traceMode = NO;
272 self->encodingRoot = NO;
275 - (void)writeArchiveHeader {
276 if (self->didWriteHeader == NO) {
277 _writeCString(self, [[self coderSignature] cString]);
278 _writeInt(self, [self coderVersion]);
279 self->didWriteHeader = YES;
282 - (void)writeArchiveTrailer {
285 - (void)traceObjectsWithRoot:(id)_root {
289 self->traceMode = YES;
291 [self encodeObject:_root];
294 self->traceMode = NO;
295 NSResetHashTable(self->outObjects);
296 [localException raise];
299 self->traceMode = NO;
300 NSResetHashTable(self->outObjects);
303 - (void)encodeObjectsWithRoot:(id)_root {
305 [self encodeObject:_root];
308 - (void)encodeRootObject:(id)_object {
309 NSAutoreleasePool *pool = [[NSAutoreleasePool allocWithZone:[self zone]] init];
311 [self beginEncoding];
315 * Prepare for writing the graph objects for which `rootObject' is the root
316 * node. The algorithm consists from two passes. In the first pass it
317 * determines the nodes so-called 'conditionals' - the nodes encoded *only*
318 * with -encodeConditionalObject:. They represent nodes that are not
319 * related directly to the graph. In the second pass objects are encoded
320 * normally, except for the conditional objects which are encoded as nil.
323 // pass1: start tracing for conditionals
324 [self traceObjectsWithRoot:_object];
326 // pass2: start writing
327 [self writeArchiveHeader];
328 [self encodeObjectsWithRoot:_object];
329 [self writeArchiveTrailer];
332 [self endEncoding]; // release resources
333 [localException raise];
336 [self endEncoding]; // release resources
338 [pool release]; pool = nil;
341 - (void)encodeConditionalObject:(id)_object {
342 if (self->traceMode) { // pass 1
344 * This is the first pass of the determining the conditionals
345 * algorithm. We traverse the graph and insert into the `conditionals'
346 * set. In the second pass all objects that are still in this set will
347 * be encoded as nil when they receive -encodeConditionalObject:. An
348 * object is removed from this set when it receives -encodeObject:.
352 if (NSHashGet(self->outObjects, _object))
353 // object isn't conditional any more .. (was stored using encodeObject:)
355 else if (NSHashGet(self->outConditionals, _object))
356 // object is already stored as conditional
359 // insert object in conditionals set
360 NSHashInsert(self->outConditionals, _object);
366 isConditional = (NSHashGet(self->outConditionals, _object) != nil);
368 // If anObject is still in the `conditionals' set, it is encoded as nil.
369 [self encodeObject:isConditional ? nil : _object];
373 - (void)_traceObject:(id)_object {
374 if (_object == nil) // don't trace nil objects ..
377 if (NSHashGet(self->outObjects, _object) == nil) { // object wasn't traced yet
378 // Look-up the object in the `conditionals' set. If the object is
379 // there, then remove it because it is no longer a conditional one.
380 if (NSHashGet(self->outConditionals, _object)) {
381 // object was marked conditional ..
382 NSHashRemove(self->outConditionals, _object);
385 // mark object as traced
386 NSHashInsert(self->outObjects, _object);
388 if (object_is_instance(_object)) {
389 Class archiveClass = Nil;
390 id replacement = nil;
392 replacement = [_object performSelector:self->replObjectForCoder
395 if (replacement != _object) {
396 NSMapInsert(self->replacements, _object, replacement);
397 _object = replacement;
400 if (object_is_instance(_object)) {
401 archiveClass = [_object performSelector:self->classForCoder
405 [self encodeObject:archiveClass];
406 [_object encodeWithCoder:self];
409 // there are no class-variables ..
413 - (void)_encodeObject:(id)_object {
415 int archiveId = _archiveIdOfObject(self, _object);
417 tag = object_is_instance(_object) ? _C_ID : _C_CLASS;
419 if (_object == nil) { // nil object
421 NSLog(@"encoding nil reference ..");
423 _writeTag(self, tag | REFERENCE);
424 _writeInt(self, archiveId);
426 else if (NSHashGet(self->outObjects, _object)) { // object was already written
428 if (tag == _C_CLASS) {
429 NSLog(@"encoding reference to class <%s> ..",
430 class_get_class_name(_object));
433 NSLog(@"encoding reference to object 0x%08X<%s> ..",
434 _object, class_get_class_name(*(Class *)_object));
438 _writeTag(self, tag | REFERENCE);
439 _writeInt(self, archiveId);
442 // mark object as written
443 NSHashInsert(self->outObjects, _object);
446 if (tag == _C_CLASS) { // a class object
447 NSLog( @"encoding class %s:%i ..",
448 class_get_class_name(_object), [_object version]);
451 NSLog(@"encoding object 0x%08X<%s> ..",
452 _object, class_get_class_name(*(Class *)_object));
456 _writeTag(self, tag);
457 _writeInt(self, archiveId);
459 if (tag == _C_CLASS) { // a class object
460 _writeCString(self, class_get_class_name(_object));
461 _writeInt(self, [_object version]);
464 Class archiveClass = Nil;
465 id replacement = nil;
467 replacement = NSMapGet(self->replacements, _object);
468 if (replacement) _object = replacement;
471 _object = [_object performSelector:self->replObjectForCoder
474 archiveClass = [_object performSelector:self->classForCoder
475 withObject:self]; // class of replacement
477 NSAssert(archiveClass, @"no archive class found ..");
479 [self encodeObject:archiveClass];
480 [_object encodeWithCoder:self];
485 - (void)encodeObject:(id)_object {
486 if (self->encodingRoot) {
487 [self encodeValueOfObjCType:object_is_instance(_object) ? "@" : "#"
491 [self encodeRootObject:_object];
495 - (void)_traceValueOfObjCType:(const char *)_type at:(const void *)_value {
497 NSLog(@"tracing value of ObjC-type '%s'", _type);
503 [self _traceObject:*(id *)_value];
507 int count = atoi(_type + 1); // eg '[15I' => count = 15
508 const char *itemType = _type;
509 while(isdigit((int)*(++itemType))) ; // skip dimension
510 [self encodeArrayOfObjCType:itemType count:count at:_value];
514 case _C_STRUCT_B: { // C-structure begin '{'
517 while ((*_type != _C_STRUCT_E) && (*_type++ != '=')); // skip "<name>="
520 [self encodeValueOfObjCType:_type at:((char *)_value) + offset];
522 offset += objc_sizeof_type(_type);
523 _type = objc_skip_typespec(_type);
525 if(*_type != _C_STRUCT_E) { // C-structure end '}'
526 int align, remainder;
528 align = objc_alignof_type(_type);
529 if((remainder = offset % align))
530 offset += (align - remainder);
540 - (void)_encodeValueOfObjCType:(const char *)_type at:(const void *)_value {
544 // ?? Write another tag just to be possible to read using the
545 // ?? decodeObject method. (Otherwise a lookahead would be required)
546 // ?? _writeTag(self, *_type);
547 [self _encodeObject:*(id *)_value];
551 int count = atoi(_type + 1); // eg '[15I' => count = 15
552 const char *itemType = _type;
554 while(isdigit((int)*(++itemType))) ; // skip dimension
556 // Write another tag just to be possible to read using the
557 // decodeArrayOfObjCType:count:at: method.
558 _writeTag(self, _C_ARY_B);
559 [self encodeArrayOfObjCType:itemType count:count at:_value];
563 case _C_STRUCT_B: { // C-structure begin '{'
566 _writeTag(self, '{');
568 while ((*_type != _C_STRUCT_E) && (*_type++ != '=')); // skip "<name>="
571 [self encodeValueOfObjCType:_type at:((char *)_value) + offset];
573 offset += objc_sizeof_type(_type);
574 _type = objc_skip_typespec(_type);
576 if(*_type != _C_STRUCT_E) { // C-structure end '}'
577 int align, remainder;
579 align = objc_alignof_type(_type);
580 if((remainder = offset % align))
581 offset += (align - remainder);
590 _writeTag(self, _C_SEL);
591 _writeCString(self, (*(SEL *)_value) ? sel_get_name(*(SEL *)_value) : NULL);
595 _writeTag(self, *_type);
596 _writeObjC(self, *(char **)_value, _type + 1);
599 _writeTag(self, *_type);
600 _writeObjC(self, _value, _type);
603 case _C_CHR: case _C_UCHR:
604 case _C_SHT: case _C_USHT:
605 case _C_INT: case _C_UINT:
606 case _C_LNG: case _C_ULNG:
607 case _C_FLT: case _C_DBL:
608 _writeTag(self, *_type);
609 _writeObjC(self, _value, _type);
613 NSLog(@"unsupported C type '%s' ..", _type);
618 - (void)encodeValueOfObjCType:(const char *)_type at:(const void *)_value {
620 [self _traceValueOfObjCType:_type at:_value];
622 if (self->didWriteHeader == NO)
623 [self writeArchiveHeader];
625 [self _encodeValueOfObjCType:_type at:_value];
629 - (void)encodeArrayOfObjCType:(const char *)_type count:(unsigned int)_count
630 at:(const void *)_array {
632 if ((self->didWriteHeader == NO) && (self->traceMode == NO))
633 [self writeArchiveHeader];
636 if (self->traceMode == NO) { // nothing is written during trace-mode
637 _writeTag(self, _C_ARY_B);
638 _writeInt(self, _count);
641 // Optimize writing arrays of elementary types. If such an array has to
642 // be written, write the type and then the elements of array.
644 if ((*_type == _C_ID) || (*_type == _C_CLASS)) { // object array
647 if (self->traceMode == NO)
648 _writeTag(self, *_type); // object array
650 for (i = 0; i < _count; i++)
651 [self encodeObject:((id *)_array)[i]];
653 else if ((*_type == _C_CHR) || (*_type == _C_UCHR)) { // byte array
654 if (self->traceMode == NO) {
656 // write base type tag
657 _writeTag(self, *_type);
660 _writeBytes(self, _array, _count);
663 else if (isBaseType(_type)) {
664 if (self->traceMode == NO) {
665 unsigned offset, itemSize = objc_sizeof_type(_type);
668 // write base type tag
669 _writeTag(self, *_type);
672 for (i = offset = 0; i < _count; i++, offset += itemSize)
673 _writeObjC(self, (char *)_array + offset, _type);
676 else { // encoded using normal method
677 IMP encodeValue = NULL;
678 unsigned offset, itemSize = objc_sizeof_type(_type);
681 encodeValue = [self methodForSelector:@selector(encodeValueOfObjCType:at:)];
683 for (i = offset = 0; i < _count; i++, offset += itemSize) {
684 encodeValue(self, @selector(encodeValueOfObjCType:at:),
685 (char *)_array + offset, _type);
690 // -------------------- decoding --------------------
692 - (void)decodeArchiveHeader {
693 if (self->didReadHeader == NO) {
694 char *archiver = _readCString(self);
696 self->inArchiverVersion = _readInt(self);
698 if (strcmp(archiver, [[self coderSignature] cString])) {
699 NSLog(@"WARNING: used a different archiver (signature %s:%i)",
700 archiver, [self systemVersion]);
702 else if ([self systemVersion] != [self coderVersion]) {
703 NSLog(@"WARNING: used a different archiver version "
704 @"(archiver=%i, unarchiver=%i)",
705 [self systemVersion], [self coderVersion]);
712 self->didReadHeader = YES;
716 - (void)beginDecoding {
718 NSLog(@"start decoding ..");
720 [self decodeArchiveHeader];
722 - (void)endDecoding {
724 NSLog(@"finish decoding ..");
726 NSResetMapTable(self->inObjects);
727 NSResetMapTable(self->inClasses);
728 NSResetMapTable(self->inPointers);
729 NSResetMapTable(self->inClassAlias);
730 NSResetMapTable(self->inClassVersions);
733 - (Class)_decodeClass:(BOOL)_isReference {
734 int archiveId = _readInt(self);
738 result = (Class)NSMapGet(self->inClasses, (void *)archiveId);
740 NSLog(@"did not find class for archive-id %i", archiveId);
744 NSString *name = NULL;
747 name = [NSString stringWithCString:_readCString(self)];
748 version = _readInt(self);
751 [NSException raise:NSInconsistentArchiveException
752 format:@"did not find class name"];
755 { // check whether the class is to be replaced
756 NSString *newName = NSMapGet(self->inClassAlias, name);
761 newName = NSMapGet(classToAliasMappings, name);
767 result = NSClassFromString(name);
769 NSLog(@"decoded class %@:%i (result=%@).", name, version, result);
772 NSAssert([result version] == version, @"class versions do not match ..");
774 NSMapInsert(self->inClasses, (void *)archiveId, result);
777 NSAssert(result, @"class may not be Nil ..");
781 - (id)_decodeObject:(BOOL)_isReference {
782 // this method returns a retained object !
783 int archiveId = _readInt(self);
786 if (archiveId == 0) // nil object or unused conditional object
790 result = [(id)NSMapGet(self->inObjects, (void *)archiveId) retain];
794 id replacement = nil;
797 [self decodeValueOfObjCType:"#" at:&class];
798 NSAssert(class, @"invalid class ..");
800 result = [class allocWithZone:self->objectZone];
801 NSMapInsert(self->inObjects, (void *)archiveId, result);
803 replacement = [result initWithCoder:self];
804 if (replacement != result) {
806 replacement = [replacement retain];
807 NSMapRemove(self->inObjects, result);
808 result = replacement;
809 NSMapInsert(self->inObjects, (void *)archiveId, result);
810 [replacement release];
813 replacement = [result awakeAfterUsingCoder:self];
814 if (replacement != result) {
815 replacement = [replacement retain];
816 NSMapRemove(self->inObjects, result);
817 result = replacement;
818 NSMapInsert(self->inObjects, (void *)archiveId, result);
819 [replacement release];
822 NSAssert([result retainCount] > 0, @"invalid retain count ..");
829 [self decodeValueOfObjCType:"@" at:&result];
831 // result is retained
832 return [result autorelease];
835 - (void)decodeValueOfObjCType:(const char *)_type at:(void *)_value {
836 BOOL startedDecoding = NO;
838 BOOL isReference = NO;
840 if (self->decodingRoot == NO) {
841 self->decodingRoot = YES;
842 startedDecoding = YES;
843 [self beginDecoding];
846 tag = _readTag(self);
847 isReference = isReferenceTag(tag);
852 NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
853 *(id *)_value = [self _decodeObject:isReference];
856 NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
857 *(Class *)_value = [self _decodeClass:isReference];
861 int count = atoi(_type + 1); // eg '[15I' => count = 15
862 const char *itemType = _type;
864 NSAssert(*_type == _C_ARY_B, @"invalid type ..");
866 while(isdigit((int)*(++itemType))) ; // skip dimension
868 [self decodeArrayOfObjCType:itemType count:count at:_value];
875 NSAssert(*_type == _C_STRUCT_B, @"invalid type ..");
877 while ((*_type != _C_STRUCT_E) && (*_type++ != '=')); // skip "<name>="
880 [self decodeValueOfObjCType:_type at:((char *)_value) + offset];
882 offset += objc_sizeof_type(_type);
883 _type = objc_skip_typespec(_type);
885 if(*_type != _C_STRUCT_E) { // C-structure end '}'
886 int align, remainder;
888 align = objc_alignof_type(_type);
889 if((remainder = offset % align))
890 offset += (align - remainder);
901 NSAssert(*_type == tag, @"invalid type ..");
902 _readObjC(self, &name, @encode(char *));
903 *(SEL *)_value = name ? sel_get_any_uid(name) : NULL;
904 NGFree(name); name = NULL;
908 _readObjC(self, *(char **)_value, _type + 1); // skip '^'
912 case _C_CHR: case _C_UCHR:
913 case _C_SHT: case _C_USHT:
914 case _C_INT: case _C_UINT:
915 case _C_LNG: case _C_ULNG:
916 case _C_FLT: case _C_DBL:
917 NSAssert(*_type == tag, @"invalid type ..");
918 _readObjC(self, _value, _type);
922 NSAssert2(0, @"unsupported tag '%c', type %s ..", tag, _type);
926 if (startedDecoding) {
928 self->decodingRoot = NO;
932 - (void)decodeArrayOfObjCType:(const char *)_type count:(unsigned int)_count
935 BOOL startedDecoding = NO;
936 NGTagType tag = _readTag(self);
937 int count = _readInt(self);
939 if (self->decodingRoot == NO) {
940 self->decodingRoot = YES;
941 startedDecoding = YES;
942 [self beginDecoding];
946 NSLog(@"decoding array[%i/%i] of ObjC-type '%s' array-tag='%c'",
947 _count, count, _type, tag);
950 NSAssert(tag == _C_ARY_B, @"invalid type ..");
951 NSAssert(count == _count, @"invalid array size ..");
953 // Arrays of elementary types are written optimized: the type is written
954 // then the elements of array follow.
955 if ((*_type == _C_ID) || (*_type == _C_CLASS)) { // object array
959 NSLog(@"decoding object-array[%i] type='%s'", _count, _type);
962 tag = _readTag(self); // object array
963 NSAssert(tag == *_type, @"invalid array element type ..");
965 for (i = 0; i < _count; i++)
966 ((id *)_array)[i] = [self decodeObject];
968 else if ((*_type == _C_CHR) || (*_type == _C_UCHR)) { // byte array
969 tag = _readTag(self);
970 NSAssert((tag == _C_CHR) || (tag == _C_UCHR), @"invalid byte array type ..");
973 NSLog(@"decoding byte-array[%i] type='%s' tag='%c'",
978 _readBytes(self, _array, _count);
980 else if (isBaseType(_type)) {
981 unsigned offset, itemSize = objc_sizeof_type(_type);
984 tag = _readTag(self);
985 NSAssert(tag == *_type, @"invalid array base type ..");
987 for (i = offset = 0; i < _count; i++, offset += itemSize)
988 _readObjC(self, (char *)_array + offset, _type);
991 IMP decodeValue = NULL;
992 unsigned offset, itemSize = objc_sizeof_type(_type);
995 decodeValue = [self methodForSelector:@selector(decodeValueOfObjCType:at:)];
997 for (i = offset = 0; i < count; i++, offset += itemSize) {
998 decodeValue(self, @selector(decodeValueOfObjCType:at:),
999 (char *)_array + offset, _type);
1003 if (startedDecoding) {
1005 self->decodingRoot = NO;
1009 // Substituting One Class for Another
1011 + (NSString *)classNameDecodedForArchiveClassName:(NSString *)nameInArchive {
1012 NSString *className = NSMapGet(classToAliasMappings, nameInArchive);
1013 return className ? className : nameInArchive;
1015 + (void)decodeClassName:(NSString *)nameInArchive asClassName:(NSString *)trueName {
1016 NSMapInsert(classToAliasMappings, nameInArchive, trueName);
1019 - (NSString *)classNameDecodedForArchiveClassName:(NSString *)_nameInArchive {
1020 NSString *className = NSMapGet(self->inClassAlias, _nameInArchive);
1021 return className ? className : _nameInArchive;
1023 - (void)decodeClassName:(NSString *)nameInArchive asClassName:(NSString *)trueName {
1024 NSMapInsert(self->inClassAlias, nameInArchive, trueName);
1027 // ******************** primitives ********************
1031 FINAL void _writeBytes(NGStreamCoder *self, const void *_bytes, unsigned _len) {
1032 NSCAssert(self->traceMode == NO, @"nothing can be written during trace-mode ..");
1035 ? self->writeIMP(self->stream, @selector(safeWriteBytes:count:), _bytes, _len)
1036 : [self->stream safeWriteBytes:_bytes count:_len];
1039 FINAL void _writeTag(NGStreamCoder *self, NGTagType _tag) {
1040 NSCAssert(self, @"invalid self ..");
1042 NSLog(@"write tag '%s%c'",
1043 isReferenceTag(_tag) ? "&" : "", tagValue(_tag));
1046 [self->stream serializeChar:_tag];
1049 FINAL void _writeChar(NGStreamCoder *self, char _value) {
1050 [self->stream serializeChar:_value];
1052 FINAL void _writeShort(NGStreamCoder *self, short _value) {
1053 [self->stream serializeShort:_value];
1055 FINAL void _writeInt(NGStreamCoder *self, int _value) {
1056 [self->stream serializeInt:_value];
1058 FINAL void _writeLong(NGStreamCoder *self, long _value) {
1059 [self->stream serializeLong:_value];
1061 FINAL void _writeFloat(NGStreamCoder *self, float _value) {
1062 [self->stream serializeFloat:_value];
1065 FINAL void _writeCString(NGStreamCoder *self, const char *_value) {
1066 [(id)self->stream serializeDataAt:&_value ofObjCType:@encode(char *) context:self];
1069 FINAL void _writeObjC(NGStreamCoder *self,
1070 const void *_value, const char *_type) {
1071 if ((_value == NULL) || (_type == NULL))
1074 if (self->traceMode) {
1075 // no need to track base-types in trace-mode
1084 [(id)self->stream serializeDataAt:_value ofObjCType:_type context:self];
1092 [(id)self->stream serializeDataAt:_value ofObjCType:_type context:self];
1098 FINAL void _readBytes(NGStreamCoder *self, void *_bytes, unsigned _len) {
1100 ? self->readIMP(self->stream, @selector(safeReadBytes:count:), _bytes, _len)
1101 : [self->stream safeReadBytes:_bytes count:_len];
1104 FINAL NGTagType _readTag(NGStreamCoder *self) {
1105 return [self->stream deserializeChar];
1107 FINAL char _readChar(NGStreamCoder *self) {
1108 return [self->stream deserializeChar];
1110 FINAL short _readShort(NGStreamCoder *self) {
1111 return [self->stream deserializeShort];
1113 FINAL int _readInt(NGStreamCoder *self) {
1114 return [self->stream deserializeInt];
1116 FINAL long _readLong (NGStreamCoder *self) {
1117 return [self->stream deserializeLong];
1119 FINAL float _readFloat(NGStreamCoder *self) {
1120 return [self->stream deserializeFloat];
1123 FINAL char *_readCString(NGStreamCoder *self) {
1124 char *result = NULL;
1125 [(id)self->stream deserializeDataAt:&result ofObjCType:@encode(char *) context:self];
1129 FINAL void _readObjC(NGStreamCoder *self, void *_value, const char *_type) {
1130 [(id)self->stream deserializeDataAt:_value ofObjCType:_type context:(id)self];
1133 // NSObjCTypeSerializationCallBack
1135 - (void)serializeObjectAt:(id *)_object ofObjCType:(const char *)_type
1136 intoData:(NSMutableData *)_data {
1141 if (self->traceMode)
1142 [self _traceObject:*_object];
1144 [self _encodeObject:*_object];
1153 - (void)deserializeObjectAt:(id *)_object ofObjCType:(const char *)_type
1154 fromData:(NSData *)_data atCursor:(unsigned int *)_cursor {
1157 BOOL isReference = NO;
1159 tag = _readTag(self);
1160 isReference = isReferenceTag(tag);
1161 tag = tagValue(tag);
1165 NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
1167 *_object = [self _decodeObject:isReference];
1170 NSAssert((*_type == _C_ID) || (*_type == _C_CLASS), @"invalid type ..");
1171 *_object = [self _decodeClass:isReference];