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