1/** Implementation of NSArchiver for GNUstep 2 Copyright (C) 1998,1999 Free Software Foundation, Inc. 3 4 Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk> 5 Created: October 1998 6 7 This file is part of the GNUstep Base Library. 8 9 This library is free software; you can redistribute it and/or 10 modify it under the terms of the GNU Lesser General Public 11 License as published by the Free Software Foundation; either 12 version 2 of the License, or (at your option) any later version. 13 14 This library is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 Lesser General Public License for more details. 18 19 You should have received a copy of the GNU Lesser General Public 20 License along with this library; if not, write to the Free 21 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 Boston, MA 02110 USA. 23 24 <title>NSArchiver class reference</title> 25 $Date$ $Revision$ 26 */ 27 28#import "common.h" 29 30#if !defined (__GNU_LIBOBJC__) 31# include <objc/encoding.h> 32#endif 33 34#define EXPOSE_NSArchiver_IVARS 1 35#define EXPOSE_NSUnarchiver_IVARS 1 36/* 37 * Setup for inline operation of pointer map tables. 38 */ 39#define GSI_MAP_KTYPES GSUNION_NSINT | GSUNION_PTR | GSUNION_OBJ | GSUNION_CLS 40#define GSI_MAP_VTYPES GSUNION_NSINT | GSUNION_PTR | GSUNION_OBJ 41#define GSI_MAP_RETAIN_KEY(M, X) 42#define GSI_MAP_RELEASE_KEY(M, X) 43#define GSI_MAP_RETAIN_VAL(M, X) 44#define GSI_MAP_RELEASE_VAL(M, X) 45#define GSI_MAP_HASH(M, X) ((X).nsu) 46#define GSI_MAP_EQUAL(M, X,Y) ((X).ptr == (Y).ptr) 47#define GSI_MAP_NOCLEAN 1 48 49 50#include "GNUstepBase/GSIMap.h" 51 52#define _IN_NSARCHIVER_M 53#import "Foundation/NSArchiver.h" 54#undef _IN_NSARCHIVER_M 55 56#import "Foundation/NSCoder.h" 57#import "Foundation/NSData.h" 58#import "Foundation/NSException.h" 59 60typedef unsigned char uchar; 61 62NSString * const NSInconsistentArchiveException = 63 @"NSInconsistentArchiveException"; 64 65#define PREFIX "GNUstep archive" 66 67static SEL serSel; 68static SEL tagSel; 69static SEL xRefSel; 70static SEL eObjSel; 71static SEL eValSel; 72 73@class NSMutableDataMalloc; 74@interface NSMutableDataMalloc : NSObject // Help the compiler 75@end 76static Class NSMutableDataMallocClass; 77 78/** 79 * <p>Implementation of [NSCoder] capable of creating sequential archives which 80 * must be read in the same order they were written. This class implements 81 * methods for saving to and restoring from a serial archive (usually a file 82 * on disk, but can be an [NSData] object) as well as methods that can be 83 * used by objects that need to write/restore themselves.</p> 84 * 85 * <p>Note, the sibling class [NSKeyedArchiver] supports a form of archive 86 * that is more robust to class changes, and is recommended over this one.</p> 87 */ 88@implementation NSArchiver 89 90+ (void) initialize 91{ 92 if (self == [NSArchiver class]) 93 { 94 serSel = @selector(serializeDataAt:ofObjCType:context:); 95 tagSel = @selector(serializeTypeTag:); 96 xRefSel = @selector(serializeTypeTag:andCrossRef:); 97 eObjSel = @selector(encodeObject:); 98 eValSel = @selector(encodeValueOfObjCType:at:); 99 NSMutableDataMallocClass = [NSMutableDataMalloc class]; 100 } 101} 102 103/** 104 * Creates an NSMutableData instance and calls 105 * [initForWritingWithMutableData:]. 106 */ 107- (id) init 108{ 109 NSMutableData *d; 110 111 d = [[NSMutableDataMallocClass allocWithZone: [self zone]] init]; 112 self = [self initForWritingWithMutableData: d]; 113 RELEASE(d); 114 return self; 115} 116 117/** 118 * Init instance that will archive its data to mdata. (Even if 119 * [archiveRootObject:toFile:] is called, this still gets written to.) 120 */ 121- (id) initForWritingWithMutableData: (NSMutableData*)mdata 122{ 123 self = [super init]; 124 if (self) 125 { 126 NSZone *zone = [self zone]; 127 128 _data = RETAIN(mdata); 129 if ([self directDataAccess] == YES) 130 { 131 _dst = _data; 132 } 133 else 134 { 135 _dst = self; 136 } 137 _serImp = [_dst methodForSelector: serSel]; 138 _tagImp = [_dst methodForSelector: tagSel]; 139 _xRefImp = [_dst methodForSelector: xRefSel]; 140 _eObjImp = [self methodForSelector: eObjSel]; 141 _eValImp = [self methodForSelector: eValSel]; 142 143 [self resetArchiver]; 144 145 /* 146 * Set up map tables. 147 */ 148 _clsMap = (GSIMapTable)NSZoneMalloc(zone, sizeof(GSIMapTable_t)*6); 149 _cIdMap = &_clsMap[1]; 150 _uIdMap = &_clsMap[2]; 151 _ptrMap = &_clsMap[3]; 152 _namMap = &_clsMap[4]; 153 _repMap = &_clsMap[5]; 154 GSIMapInitWithZoneAndCapacity(_clsMap, zone, 100); 155 GSIMapInitWithZoneAndCapacity(_cIdMap, zone, 10); 156 GSIMapInitWithZoneAndCapacity(_uIdMap, zone, 200); 157 GSIMapInitWithZoneAndCapacity(_ptrMap, zone, 100); 158 GSIMapInitWithZoneAndCapacity(_namMap, zone, 1); 159 GSIMapInitWithZoneAndCapacity(_repMap, zone, 1); 160 } 161 return self; 162} 163 164- (void) dealloc 165{ 166 RELEASE(_data); 167 if (_clsMap) 168 { 169 GSIMapEmptyMap(_clsMap); 170 if (_cIdMap) 171 { 172 GSIMapEmptyMap(_cIdMap); 173 } 174 if (_uIdMap) 175 { 176 GSIMapEmptyMap(_uIdMap); 177 } 178 if (_ptrMap) 179 { 180 GSIMapEmptyMap(_ptrMap); 181 } 182 if (_namMap) 183 { 184 GSIMapEmptyMap(_namMap); 185 } 186 if (_repMap) 187 { 188 GSIMapEmptyMap(_repMap); 189 } 190 NSZoneFree(_clsMap->zone, (void*)_clsMap); 191 } 192 [super dealloc]; 193} 194 195/** 196 * Writes serialized representation of object and, recursively, any 197 * other objects it holds references to, to byte array. 198 */ 199+ (NSData*) archivedDataWithRootObject: (id)rootObject 200{ 201 NSArchiver *archiver; 202 id d; 203 NSZone *z = NSDefaultMallocZone(); 204 205 d = [[NSMutableDataMallocClass allocWithZone: z] initWithCapacity: 0]; 206 if (d == nil) 207 { 208 return nil; 209 } 210 archiver = [[self allocWithZone: z] initForWritingWithMutableData: d]; 211 RELEASE(d); 212 d = nil; 213 if (archiver) 214 { 215 NS_DURING 216 { 217 [archiver encodeRootObject: rootObject]; 218 d = AUTORELEASE([archiver->_data copy]); 219 } 220 NS_HANDLER 221 { 222 RELEASE(archiver); 223 [localException raise]; 224 } 225 NS_ENDHANDLER 226 RELEASE(archiver); 227 } 228 229 return d; 230} 231 232/** 233 * Writes out serialized representation of object and, recursively, any 234 * other objects it holds references to. 235 */ 236+ (BOOL) archiveRootObject: (id)rootObject 237 toFile: (NSString*)path 238{ 239 id d = [self archivedDataWithRootObject: rootObject]; 240 241 return [d writeToFile: path atomically: YES]; 242} 243 244- (void) encodeArrayOfObjCType: (const char*)type 245 count: (NSUInteger)count 246 at: (const void*)buf 247{ 248 uint32_t c; 249 uint8_t bytes[20]; 250 uint8_t *bytePtr = 0; 251 uint8_t byteCount = 0; 252 NSUInteger i; 253 NSUInteger offset = 0; 254 uint32_t size; 255 uint32_t version; 256 uchar info; 257 258 type = GSSkipTypeQualifierAndLayoutInfo(type); 259 size = objc_sizeof_type(type); 260 version = [self systemVersion]; 261 262 if (12402 == version) 263 { 264 NSUInteger tmp = count; 265 266 bytes[sizeof(bytes) - ++byteCount] = (uint8_t)(tmp % 128); 267 tmp /= 128; 268 while (tmp > 0) 269 { 270 bytes[sizeof(bytes) - ++byteCount] = (uint8_t)(128 | (tmp % 128)); 271 tmp /= 128; 272 } 273 bytePtr = &bytes[sizeof(bytes) - byteCount]; 274 } 275 276 /* We normally store the count as a 32bit integer ... but if it's 277 * very big, we store 0xffffffff and then an additional 64bit value 278 * containing the actual count. 279 */ 280 if (count >= 0xffffffff) 281 { 282 c = 0xffffffff; 283 } 284 else 285 { 286 c = count; 287 } 288 289 switch (*type) 290 { 291 case _C_ID: info = _GSC_NONE; break; 292 case _C_CHR: info = _GSC_CHR; break; 293 case _C_UCHR: info = _GSC_UCHR; break; 294 case _C_SHT: info = _GSC_SHT | _GSC_S_SHT; break; 295 case _C_USHT: info = _GSC_USHT | _GSC_S_SHT; break; 296 case _C_INT: info = _GSC_INT | _GSC_S_INT; break; 297 case _C_UINT: info = _GSC_UINT | _GSC_S_INT; break; 298 case _C_LNG: info = _GSC_LNG | _GSC_S_LNG; break; 299 case _C_ULNG: info = _GSC_ULNG | _GSC_S_LNG; break; 300 case _C_LNG_LNG: info = _GSC_LNG_LNG | _GSC_S_LNG_LNG; break; 301 case _C_ULNG_LNG: info = _GSC_ULNG_LNG | _GSC_S_LNG_LNG; break; 302 case _C_FLT: info = _GSC_FLT; break; 303 case _C_DBL: info = _GSC_DBL; break; 304#if __GNUC__ > 2 && defined(_C_BOOL) 305 case _C_BOOL: info = _GSC_BOOL; break; 306#endif 307 default: info = _GSC_NONE; break; 308 } 309 310 /* 311 * Simple types can be serialized immediately, more complex ones 312 * are dealt with by our [encodeValueOfObjCType:at:] method. 313 */ 314 if (info == _GSC_NONE) 315 { 316 if (_initialPass == NO) 317 { 318 (*_tagImp)(_dst, tagSel, _GSC_ARY_B); 319 if (12402 == version) 320 { 321 for (i = 0; i < byteCount; i++) 322 { 323 (*_serImp)(_dst, serSel, bytePtr + i, @encode(uint8_t), nil); 324 } 325 } 326 else 327 { 328 (*_serImp)(_dst, serSel, &c, @encode(uint32_t), nil); 329 if (0xffffffff == c) 330 { 331 (*_serImp)(_dst, serSel, &count, @encode(NSUInteger), nil); 332 } 333 } 334 } 335 336 for (i = 0; i < c; i++) 337 { 338 (*_eValImp)(self, eValSel, type, (char*)buf + offset); 339 offset += size; 340 } 341 } 342 else if (_initialPass == NO) 343 { 344 (*_tagImp)(_dst, tagSel, _GSC_ARY_B); 345 if (12402 == version) 346 { 347 for (i = 0; i < byteCount; i++) 348 { 349 (*_serImp)(_dst, serSel, bytePtr + i, @encode(uint8_t), nil); 350 } 351 } 352 else 353 { 354 (*_serImp)(_dst, serSel, &c, @encode(uint32_t), nil); 355 if (0xffffffff == c) 356 { 357 (*_serImp)(_dst, serSel, &count, @encode(NSUInteger), nil); 358 } 359 } 360 361 (*_tagImp)(_dst, tagSel, info); 362 for (i = 0; i < count; i++) 363 { 364 (*_serImp)(_dst, serSel, (char*)buf + offset, type, nil); 365 offset += size; 366 } 367 } 368} 369 370- (void) encodeValueOfObjCType: (const char*)type 371 at: (const void*)buf 372{ 373 type = GSSkipTypeQualifierAndLayoutInfo(type); 374 switch (*type) 375 { 376 case _C_ID: 377 (*_eObjImp)(self, eObjSel, *(void**)buf); 378 return; 379 380 case _C_ARY_B: 381 { 382 unsigned count = atoi(++type); 383 384 while (isdigit(*type)) 385 { 386 type++; 387 } 388 389 if (_initialPass == NO) 390 { 391 (*_tagImp)(_dst, tagSel, _GSC_ARY_B); 392 } 393 394 [self encodeArrayOfObjCType: type count: count at: buf]; 395 } 396 return; 397 398 case _C_STRUCT_B: 399 { 400 struct objc_struct_layout layout; 401 402 if (_initialPass == NO) 403 { 404 (*_tagImp)(_dst, tagSel, _GSC_STRUCT_B); 405 } 406 objc_layout_structure (type, &layout); 407 while (objc_layout_structure_next_member (&layout)) 408 { 409 unsigned offset; 410 unsigned align; 411 const char *ftype; 412 413 objc_layout_structure_get_info (&layout, &offset, &align, &ftype); 414 415 (*_eValImp)(self, eValSel, ftype, (char*)buf + offset); 416 } 417 } 418 return; 419 420 case _C_PTR: 421 if (*(void**)buf == 0) 422 { 423 if (_initialPass == NO) 424 { 425 /* 426 * Special case - a null pointer gets an xref of zero 427 */ 428 (*_tagImp)(_dst, tagSel, _GSC_PTR | _GSC_XREF | _GSC_X_0); 429 } 430 } 431 else 432 { 433 GSIMapNode node; 434 435 node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)*(void**)buf); 436 if (_initialPass == YES) 437 { 438 /* 439 * First pass - add pointer to map and encode item pointed 440 * to in case it is a conditionally encoded object. 441 */ 442 if (node == 0) 443 { 444 GSIMapAddPair(_ptrMap, 445 (GSIMapKey)*(void**)buf, (GSIMapVal)(NSUInteger)0); 446 type++; 447 buf = *(char**)buf; 448 (*_eValImp)(self, eValSel, type, buf); 449 } 450 } 451 else if (node == 0 || node->value.nsu == 0) 452 { 453 /* 454 * Second pass, unwritten pointer - write it. 455 */ 456 if (node == 0) 457 { 458 node = GSIMapAddPair(_ptrMap, 459 (GSIMapKey)*(void**)buf, (GSIMapVal)(NSUInteger)++_xRefP); 460 } 461 else 462 { 463 node->value.nsu = ++_xRefP; 464 } 465 (*_xRefImp)(_dst, xRefSel, _GSC_PTR, node->value.nsu); 466 type++; 467 buf = *(char**)buf; 468 (*_eValImp)(self, eValSel, type, buf); 469 } 470 else 471 { 472 /* 473 * Second pass, write a cross-reference number. 474 */ 475 (*_xRefImp)(_dst, xRefSel, _GSC_PTR|_GSC_XREF, 476 node->value.nsu); 477 } 478 } 479 return; 480 481 default: /* Types that can be ignored in first pass. */ 482 if (_initialPass) 483 { 484 return; 485 } 486 break; 487 } 488 489 switch (*type) 490 { 491 case _C_CLASS: 492 if (*(Class*)buf == 0) 493 { 494 /* 495 * Special case - a null pointer gets an xref of zero 496 */ 497 (*_tagImp)(_dst, tagSel, _GSC_CLASS | _GSC_XREF | _GSC_X_0); 498 } 499 else 500 { 501 Class c = *(Class*)buf; 502 GSIMapNode node; 503 BOOL done = NO; 504 505 node = GSIMapNodeForKey(_clsMap, (GSIMapKey)(void*)c); 506 507 if (node != 0) 508 { 509 (*_xRefImp)(_dst, xRefSel, _GSC_CLASS | _GSC_XREF, 510 node->value.nsu); 511 return; 512 } 513 while (done == NO) 514 { 515 int tmp = class_getVersion(c); 516 unsigned version = tmp; 517 Class s = class_getSuperclass(c); 518 519 if (tmp < 0) 520 { 521 [NSException raise: NSInternalInconsistencyException 522 format: @"negative class version"]; 523 } 524 node = GSIMapAddPair(_clsMap, 525 (GSIMapKey)(void*)c, (GSIMapVal)(NSUInteger)++_xRefC); 526 /* 527 * Encode tag and crossref number. 528 */ 529 (*_xRefImp)(_dst, xRefSel, _GSC_CLASS, node->value.nsu); 530 /* 531 * Encode class, and version. 532 */ 533 (*_serImp)(_dst, serSel, &c, @encode(Class), nil); 534 (*_serImp)(_dst, serSel, &version, @encode(unsigned), nil); 535 /* 536 * If we have a super class that has not been encoded, 537 * we must loop round to encode it here so that its 538 * version information will be available when objects 539 * of its subclasses are decoded and call 540 * [super initWithCoder:ccc] 541 */ 542 if (s == c || s == 0 543 || GSIMapNodeForKey(_clsMap, (GSIMapKey)(void*)s) != 0) 544 { 545 done = YES; 546 } 547 else 548 { 549 c = s; 550 } 551 } 552 /* 553 * Encode an empty tag to terminate the list of classes. 554 */ 555 (*_tagImp)(_dst, tagSel, _GSC_NONE); 556 } 557 return; 558 559 case _C_SEL: 560 if (*(SEL*)buf == 0) 561 { 562 /* 563 * Special case - a null pointer gets an xref of zero 564 */ 565 (*_tagImp)(_dst, tagSel, _GSC_SEL | _GSC_XREF | _GSC_X_0); 566 } 567 else 568 { 569 SEL s = *(SEL*)buf; 570 GSIMapNode node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)(void*)s); 571 572 if (node == 0) 573 { 574 node = GSIMapAddPair(_ptrMap, 575 (GSIMapKey)(void*)s, (GSIMapVal)(NSUInteger)++_xRefP); 576 (*_xRefImp)(_dst, xRefSel, _GSC_SEL, node->value.nsu); 577 /* 578 * Encode selector. 579 */ 580 (*_serImp)(_dst, serSel, buf, @encode(SEL), nil); 581 } 582 else 583 { 584 (*_xRefImp)(_dst, xRefSel, _GSC_SEL|_GSC_XREF, 585 node->value.nsu); 586 } 587 } 588 return; 589 590 case _C_CHARPTR: 591 if (*(char**)buf == 0) 592 { 593 /* 594 * Special case - a null pointer gets an xref of zero 595 */ 596 (*_tagImp)(_dst, tagSel, _GSC_CHARPTR | _GSC_XREF | _GSC_X_0); 597 } 598 else 599 { 600 GSIMapNode node; 601 602 node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)*(char**)buf); 603 if (node == 0) 604 { 605 node = GSIMapAddPair(_ptrMap, 606 (GSIMapKey)*(char**)buf, (GSIMapVal)(NSUInteger)++_xRefP); 607 (*_xRefImp)(_dst, xRefSel, _GSC_CHARPTR, node->value.nsu); 608 (*_serImp)(_dst, serSel, buf, type, nil); 609 } 610 else 611 { 612 (*_xRefImp)(_dst, xRefSel, _GSC_CHARPTR|_GSC_XREF, 613 node->value.nsu); 614 } 615 } 616 return; 617 618 case _C_CHR: 619 (*_tagImp)(_dst, tagSel, _GSC_CHR); 620 (*_serImp)(_dst, serSel, (void*)buf, @encode(signed char), nil); 621 return; 622 623 case _C_UCHR: 624 (*_tagImp)(_dst, tagSel, _GSC_UCHR); 625 (*_serImp)(_dst, serSel, (void*)buf, @encode(unsigned char), nil); 626 return; 627 628 case _C_SHT: 629 (*_tagImp)(_dst, tagSel, _GSC_SHT | _GSC_S_SHT); 630 (*_serImp)(_dst, serSel, (void*)buf, @encode(short), nil); 631 return; 632 633 case _C_USHT: 634 (*_tagImp)(_dst, tagSel, _GSC_USHT | _GSC_S_SHT); 635 (*_serImp)(_dst, serSel, (void*)buf, @encode(unsigned short), nil); 636 return; 637 638 case _C_INT: 639 (*_tagImp)(_dst, tagSel, _GSC_INT | _GSC_S_INT); 640 (*_serImp)(_dst, serSel, (void*)buf, @encode(int), nil); 641 return; 642 643 case _C_UINT: 644 (*_tagImp)(_dst, tagSel, _GSC_UINT | _GSC_S_INT); 645 (*_serImp)(_dst, serSel, (void*)buf, @encode(unsigned int), nil); 646 return; 647 648 case _C_LNG: 649 (*_tagImp)(_dst, tagSel, _GSC_LNG | _GSC_S_LNG); 650 (*_serImp)(_dst, serSel, (void*)buf, @encode(long), nil); 651 return; 652 653 case _C_ULNG: 654 (*_tagImp)(_dst, tagSel, _GSC_ULNG | _GSC_S_LNG); 655 (*_serImp)(_dst, serSel, (void*)buf, @encode(unsigned long), nil); 656 return; 657 658 case _C_LNG_LNG: 659 (*_tagImp)(_dst, tagSel, _GSC_LNG_LNG | _GSC_S_LNG_LNG); 660 (*_serImp)(_dst, serSel, (void*)buf, @encode(long long), nil); 661 return; 662 663 case _C_ULNG_LNG: 664 (*_tagImp)(_dst, tagSel, _GSC_ULNG_LNG | _GSC_S_LNG_LNG); 665 (*_serImp)(_dst, serSel, (void*)buf, @encode(unsigned long long), nil); 666 return; 667 668 case _C_FLT: 669 (*_tagImp)(_dst, tagSel, _GSC_FLT); 670 (*_serImp)(_dst, serSel, (void*)buf, @encode(float), nil); 671 return; 672 673 case _C_DBL: 674 (*_tagImp)(_dst, tagSel, _GSC_DBL); 675 (*_serImp)(_dst, serSel, (void*)buf, @encode(double), nil); 676 return; 677 678#if __GNUC__ > 2 && defined(_C_BOOL) 679 case _C_BOOL: 680 (*_tagImp)(_dst, tagSel, _GSC_BOOL); 681 (*_serImp)(_dst, serSel, (void*)buf, @encode(_Bool), nil); 682 return; 683#endif 684 685 case _C_VOID: 686 [NSException raise: NSInvalidArgumentException 687 format: @"can't encode void item"]; 688 689 default: 690 [NSException raise: NSInvalidArgumentException 691 format: @"item with unknown type - %s", type]; 692 } 693} 694 695- (void) encodeRootObject: (id)rootObject 696{ 697 if (_encodingRoot) 698 { 699 [NSException raise: NSInvalidArgumentException 700 format: @"encoding root object more than once"]; 701 } 702 703 _encodingRoot = YES; 704 705 /* 706 * First pass - find conditional objects. 707 */ 708 _initialPass = YES; 709 (*_eObjImp)(self, eObjSel, rootObject); 710 711 /* 712 * Second pass - write archive. 713 */ 714 _initialPass = NO; 715 (*_eObjImp)(self, eObjSel, rootObject); 716 717 /* 718 * Write sizes of crossref arrays to head of archive. 719 */ 720 [self serializeHeaderAt: _startPos 721 version: [self systemVersion] 722 classes: _clsMap->nodeCount 723 objects: _uIdMap->nodeCount 724 pointers: _ptrMap->nodeCount]; 725 726 _encodingRoot = NO; 727} 728 729- (void) encodeConditionalObject: (id)anObject 730{ 731 if (_encodingRoot == NO) 732 { 733 [NSException raise: NSInvalidArgumentException 734 format: @"conditionally encoding without root object"]; 735 return; 736 } 737 738 if (_initialPass) 739 { 740 GSIMapNode node; 741 742 /* 743 * Conditionally encoding 'nil' is a no-op. 744 */ 745 if (anObject == nil) 746 { 747 return; 748 } 749 750 /* 751 * If we have already conditionally encoded this object, we can 752 * ignore it this time. 753 */ 754 node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject); 755 if (node != 0) 756 { 757 return; 758 } 759 760 /* 761 * If we have unconditionally encoded this object, we can ignore 762 * it now. 763 */ 764 node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)anObject); 765 if (node != 0) 766 { 767 return; 768 } 769 770 GSIMapAddPair(_cIdMap, (GSIMapKey)anObject, (GSIMapVal)(NSUInteger)0); 771 } 772 else if (anObject == nil) 773 { 774 (*_eObjImp)(self, eObjSel, nil); 775 } 776 else 777 { 778 GSIMapNode node; 779 780 if (_repMap->nodeCount) 781 { 782 node = GSIMapNodeForKey(_repMap, (GSIMapKey)anObject); 783 if (node) 784 { 785 anObject = (id)node->value.ptr; 786 } 787 } 788 789 node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject); 790 if (node != 0) 791 { 792 (*_eObjImp)(self, eObjSel, nil); 793 } 794 else 795 { 796 (*_eObjImp)(self, eObjSel, anObject); 797 } 798 } 799} 800 801- (void) encodeDataObject: (NSData*)anObject 802{ 803 unsigned l = [anObject length]; 804 805 (*_eValImp)(self, eValSel, @encode(unsigned int), &l); 806 if (l) 807 { 808 const void *b = [anObject bytes]; 809 unsigned char c = 0; /* Type tag */ 810 811 /* 812 * The type tag 'c' is used to specify an encoding scheme for the 813 * actual data - at present we have '0' meaning raw data. In the 814 * future we might want zipped data for instance. 815 */ 816 (*_eValImp)(self, eValSel, @encode(unsigned char), &c); 817 [self encodeArrayOfObjCType: @encode(unsigned char) 818 count: l 819 at: b]; 820 } 821} 822 823- (void) encodeObject: (id)anObject 824{ 825 if (anObject == nil) 826 { 827 if (_initialPass == NO) 828 { 829 /* 830 * Special case - encode a nil pointer as a crossref of zero. 831 */ 832 (*_tagImp)(_dst, tagSel, _GSC_ID | _GSC_XREF, _GSC_X_0); 833 } 834 } 835 else 836 { 837 GSIMapNode node; 838 839 /* 840 * Substitute replacement object if required. 841 */ 842 node = GSIMapNodeForKey(_repMap, (GSIMapKey)anObject); 843 if (node) 844 { 845 anObject = (id)node->value.ptr; 846 } 847 848 /* 849 * See if the object has already been encoded. 850 */ 851 node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)anObject); 852 853 if (_initialPass) 854 { 855 if (node == 0) 856 { 857 /* 858 * Remove object from map of conditionally encoded objects 859 * and add it to the map of unconditionay encoded ones. 860 */ 861 GSIMapRemoveKey(_cIdMap, (GSIMapKey)anObject); 862 GSIMapAddPair(_uIdMap, 863 (GSIMapKey)anObject, (GSIMapVal)(NSUInteger)0); 864 [anObject encodeWithCoder: self]; 865 } 866 return; 867 } 868 869 if (node == 0 || node->value.nsu == 0) 870 { 871 Class cls; 872 id obj; 873 874 if (node == 0) 875 { 876 node = GSIMapAddPair(_uIdMap, 877 (GSIMapKey)anObject, (GSIMapVal)(NSUInteger)++_xRefO); 878 } 879 else 880 { 881 node->value.nsu = ++_xRefO; 882 } 883 884 obj = [anObject replacementObjectForArchiver: self]; 885 if (GSObjCIsInstance(obj) == NO) 886 { 887 /* 888 * If the object we have been given is actually a class, 889 * we encode it as a special case. 890 */ 891 (*_xRefImp)(_dst, xRefSel, _GSC_CID, node->value.nsu); 892 (*_eValImp)(self, eValSel, @encode(Class), &obj); 893 } 894 else 895 { 896 cls = [obj classForArchiver]; 897 if (_namMap->nodeCount) 898 { 899 GSIMapNode n; 900 901 n = GSIMapNodeForKey(_namMap, (GSIMapKey)cls); 902 903 if (n) 904 { 905 cls = (Class)n->value.ptr; 906 } 907 } 908 (*_xRefImp)(_dst, xRefSel, _GSC_ID, node->value.nsu); 909 (*_eValImp)(self, eValSel, @encode(Class), &cls); 910 [obj encodeWithCoder: self]; 911 } 912 } 913 else 914 { 915 (*_xRefImp)(_dst, xRefSel, _GSC_ID | _GSC_XREF, node->value.nsu); 916 } 917 } 918} 919 920/** 921 * Returns whatever data has been encoded thus far. 922 */ 923- (NSMutableData*) archiverData 924{ 925 return _data; 926} 927 928/** 929 * Returns substitute class used to encode objects of given class. This 930 * would have been set through an earlier call to 931 * [NSArchiver -encodeClassName:intoClassName:]. 932 */ 933- (NSString*) classNameEncodedForTrueClassName: (NSString*)trueName 934{ 935 if (_namMap->nodeCount) 936 { 937 GSIMapNode node; 938 Class c; 939 940 c = objc_lookUpClass([trueName cString]); 941 node = GSIMapNodeForKey(_namMap, (GSIMapKey)c); 942 if (node) 943 { 944 c = (Class)node->value.ptr; 945 return [NSString stringWithUTF8String: class_getName(c)]; 946 } 947 } 948 return trueName; 949} 950 951/** 952 * Specify substitute class used in archiving objects of given class. This 953 * class is written to the archive as the class to use for restoring the 954 * object, instead of what is returned from [NSObject -classForArchiver]. 955 * This can be used to provide backward compatibility across class name 956 * changes. The object is still encoded by calling 957 * <code>encodeWithCoder:</code> as normal. 958 */ 959- (void) encodeClassName: (NSString*)trueName 960 intoClassName: (NSString*)inArchiveName 961{ 962 GSIMapNode node; 963 Class tc; 964 Class ic; 965 966 tc = objc_lookUpClass([trueName cString]); 967 if (tc == 0) 968 { 969 [NSException raise: NSInternalInconsistencyException 970 format: @"Can't find class '%@'.", trueName]; 971 } 972 ic = objc_lookUpClass([inArchiveName cString]); 973 if (ic == 0) 974 { 975 [NSException raise: NSInternalInconsistencyException 976 format: @"Can't find class '%@'.", inArchiveName]; 977 } 978 node = GSIMapNodeForKey(_namMap, (GSIMapKey)tc); 979 if (node == 0) 980 { 981 GSIMapAddPair(_namMap, (GSIMapKey)(void*)tc, (GSIMapVal)(void*)ic); 982 } 983 else 984 { 985 node->value.ptr = (void*)ic; 986 } 987} 988 989/** 990 * Set encoder to write out newObject in place of object. 991 */ 992- (void) replaceObject: (id)object 993 withObject: (id)newObject 994{ 995 GSIMapNode node; 996 997 if (object == 0) 998 { 999 [NSException raise: NSInternalInconsistencyException 1000 format: @"attempt to remap nil"]; 1001 } 1002 if (newObject == 0) 1003 { 1004 [NSException raise: NSInternalInconsistencyException 1005 format: @"attempt to remap object to nil"]; 1006 } 1007 node = GSIMapNodeForKey(_repMap, (GSIMapKey)object); 1008 if (node == 0) 1009 { 1010 GSIMapAddPair(_repMap, (GSIMapKey)object, (GSIMapVal)newObject); 1011 } 1012 else 1013 { 1014 node->value.ptr = (void*)newObject; 1015 } 1016} 1017@end 1018 1019 1020 1021/** 1022 * Category for compatibility with old GNUstep encoding. 1023 */ 1024@implementation NSArchiver (GNUstep) 1025 1026/** 1027 * Allow reuse of archiver (clears class substitution maps, etc.) but 1028 * do not clear out current serialized data. 1029 */ 1030- (void) resetArchiver 1031{ 1032 if (_clsMap) 1033 { 1034 GSIMapCleanMap(_clsMap); 1035 if (_cIdMap) 1036 { 1037 GSIMapCleanMap(_cIdMap); 1038 } 1039 if (_uIdMap) 1040 { 1041 GSIMapCleanMap(_uIdMap); 1042 } 1043 if (_ptrMap) 1044 { 1045 GSIMapCleanMap(_ptrMap); 1046 } 1047 if (_namMap) 1048 { 1049 GSIMapCleanMap(_namMap); 1050 } 1051 if (_repMap) 1052 { 1053 GSIMapCleanMap(_repMap); 1054 } 1055 } 1056 _encodingRoot = NO; 1057 _initialPass = NO; 1058 _xRefC = 0; 1059 _xRefO = 0; 1060 _xRefP = 0; 1061 1062 /* 1063 * Write dummy header 1064 */ 1065 _startPos = [_data length]; 1066 [self serializeHeaderAt: _startPos 1067 version: [self systemVersion] 1068 classes: 0 1069 objects: 0 1070 pointers: 0]; 1071} 1072 1073/** 1074 * Returns YES. 1075 */ 1076- (BOOL) directDataAccess 1077{ 1078 return YES; 1079} 1080 1081/** 1082 * Writes out header for GNUstep archive format. 1083 */ 1084- (void) serializeHeaderAt: (unsigned)positionInData 1085 version: (unsigned)systemVersion 1086 classes: (unsigned)classCount 1087 objects: (unsigned)objectCount 1088 pointers: (unsigned)pointerCount 1089{ 1090 unsigned headerLength = strlen(PREFIX)+36; 1091 char header[headerLength+1]; 1092 unsigned dataLength = [_data length]; 1093 1094 snprintf(header, sizeof(header), "%s%08x:%08x:%08x:%08x:", 1095 PREFIX, systemVersion, classCount, objectCount, pointerCount); 1096 1097 if (positionInData + headerLength <= dataLength) 1098 { 1099 [_data replaceBytesInRange: NSMakeRange(positionInData, headerLength) 1100 withBytes: header]; 1101 } 1102 else if (positionInData == dataLength) 1103 { 1104 [_data appendBytes: header length: headerLength]; 1105 } 1106 else 1107 { 1108 [NSException raise: NSInternalInconsistencyException 1109 format: @"serializeHeader:at: bad location"]; 1110 } 1111} 1112 1113@end 1114 1115