1
/** Implementation for NSKeyedArchiver for GNUstep
2
Copyright (C) 2004 Free Software Foundation, Inc.
4
Written by: Richard Frith-Macdonald <rfm@gnu.org>
7
This file is part of the GNUstep Base Library.
9
This library is free software; you can redistribute it and/or
10
modify it under the terms of the GNU Library 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.
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
Library General Public License for more details.
19
You should have received a copy of the GNU Library General Public
20
License along with this library; if not, write to the Free
21
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
25
#include <Foundation/NSAutoreleasePool.h>
26
#include <Foundation/NSObject.h>
27
#include <Foundation/NSData.h>
28
#include <Foundation/NSException.h>
29
#include <Foundation/NSScanner.h>
30
#include <Foundation/NSValue.h>
32
#include "GSPrivate.h"
37
* Setup for inline operation of pointer map tables.
39
#define GSI_MAP_RETAIN_KEY(M, X) RETAIN(X.obj)
40
#define GSI_MAP_RELEASE_KEY(M, X) RELEASE(X.obj)
41
#define GSI_MAP_RETAIN_VAL(M, X)
42
#define GSI_MAP_RELEASE_VAL(M, X)
43
#define GSI_MAP_HASH(M, X) ((X).uint)
44
#define GSI_MAP_EQUAL(M, X,Y) ((X).uint == (Y).uint)
45
#undef GSI_MAP_NOCLEAN
47
#include "GNUstepBase/GSIMap.h"
50
#define _IN_NSKEYEDARCHIVER_M 1
51
#include <Foundation/NSKeyedArchiver.h>
52
#undef _IN_NSKEYEDARCHIVER_M
57
* An archiving error has occurred.
59
NSString * const NSInvalidArchiveOperationException
60
= @"NSInvalidArchiveOperationException";
62
static NSMapTable *globalClassMap = 0;
64
static Class NSStringClass = 0;
65
static Class NSScannerClass = 0;
66
static SEL scanFloatSel;
67
static SEL scanStringSel;
68
static SEL scannerSel;
69
static BOOL (*scanFloatImp)(NSScanner*, SEL, float*);
70
static BOOL (*scanStringImp)(NSScanner*, SEL, NSString*, NSString**);
71
static id (*scannerImp)(Class, SEL, NSString*);
76
if (NSStringClass == 0)
78
NSStringClass = [NSString class];
79
NSScannerClass = [NSScanner class];
80
scanFloatSel = @selector(scanFloat:);
81
scanStringSel = @selector(scanString:intoString:);
82
scannerSel = @selector(scannerWithString:);
83
scanFloatImp = (BOOL (*)(NSScanner*, SEL, float*))
84
[NSScannerClass instanceMethodForSelector: scanFloatSel];
85
scanStringImp = (BOOL (*)(NSScanner*, SEL, NSString*, NSString**))
86
[NSScannerClass instanceMethodForSelector: scanStringSel];
87
scannerImp = (id (*)(Class, SEL, NSString*))
88
[NSScannerClass methodForSelector: scannerSel];
93
if ([aKey isKindOfClass: [NSString class]] == NO) \
95
[NSException raise: NSInvalidArgumentException \
96
format: @"%@, bad key '%@' in %@", \
97
NSStringFromClass([self class]), aKey, NSStringFromSelector(_cmd)]; \
99
if ([aKey hasPrefix: @"$"] == YES) \
101
aKey = [@"$" stringByAppendingString: aKey]; \
103
if ([_enc objectForKey: aKey] != nil) \
105
[NSException raise: NSInvalidArgumentException \
106
format: @"%@, duplicate key '%@' in %@", \
107
NSStringFromClass([self class]), aKey, NSStringFromSelector(_cmd)]; \
111
* Make a dictionary referring to the object at ref in the array of all objects.
113
static NSDictionary *makeReference(unsigned ref)
118
n = [NSNumber numberWithUnsignedInt: ref];
119
d = [NSDictionary dictionaryWithObject: n forKey: @"CF$UID"];
123
@interface NSKeyedArchiver (Private)
124
- (id) _encodeObject: (id)anObject conditional: (BOOL)conditional;
127
@implementation NSKeyedArchiver (Internal)
129
* Internal method used to encode an array relatively efficiently.<br />
130
* Some MacOS-X library classes seem to use this.
132
- (void) _encodeArrayOfObjects: (NSArray*)anArray forKey: (NSString*)aKey
139
o = makeReference(0);
148
m = [NSMutableArray arrayWithCapacity: c];
149
for (i = 0; i < c; i++)
151
o = [self _encodeObject: [anArray objectAtIndex: i] conditional: NO];
156
[_enc setObject: o forKey: aKey];
159
- (void) _encodePropertyList: (id)anObject forKey: (NSString*)aKey
162
[_enc setObject: anObject forKey: aKey];
166
@implementation NSKeyedArchiver (Private)
168
* The real workhorse of the archiving process ... this deals with all
169
* archiving of objects. It returns the object to be stored in the
170
* mapping dictionary (_enc).
172
- (id) _encodeObject: (id)anObject conditional: (BOOL)conditional
174
id original = anObject;
176
id objectInfo = nil; // Encoded object
177
NSMutableDictionary *m = nil;
178
NSDictionary *refObject;
179
unsigned ref = 0; // Reference to nil
184
* Obtain replacement object for the value being encoded.
185
* Notify delegate of progress and set up new mapping if necessary.
187
node = GSIMapNodeForKey(_repMap, (GSIMapKey)anObject);
190
anObject = [original replacementObjectForKeyedArchiver: self];
191
if (_delegate != nil)
195
anObject = [_delegate archiver: self
196
willEncodeObject: anObject];
198
if (original != anObject)
200
[_delegate archiver: self
201
willReplaceObject: original
202
withObject: anObject];
205
GSIMapAddPair(_repMap, (GSIMapKey)original, (GSIMapVal)anObject);
211
node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)anObject);
214
if (conditional == YES)
216
node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject);
220
GSIMapAddPair(_cIdMap, (GSIMapKey)anObject, (GSIMapVal)ref);
222
* Use the null object as a placeholder for a conditionally
225
[_obj addObject: [_obj objectAtIndex: 0]];
230
* This object has already been conditionally encoded.
232
ref = node->value.uint;
237
Class c = [anObject class];
239
// FIXME ... exactly what classes are stored directly???
240
if ([anObject isKindOfClass: [GSString class]] == YES
241
|| c == [@"literal" class])
243
// We will store the string object directly.
244
objectInfo = anObject;
248
// We store a dictionary describing the object.
249
m = [NSMutableDictionary new];
253
node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject);
257
* Not encoded ... create dictionary for it.
260
GSIMapAddPair(_uIdMap, (GSIMapKey)anObject, (GSIMapVal)ref);
261
[_obj addObject: objectInfo];
266
* Conditionally encoded ... replace with actual value.
268
ref = node->value.uint;
269
GSIMapAddPair(_uIdMap, (GSIMapKey)anObject, (GSIMapVal)ref);
270
GSIMapRemoveKey(_cIdMap, (GSIMapKey)anObject);
271
[_obj replaceObjectAtIndex: ref withObject: objectInfo];
278
ref = node->value.uint;
283
* Build an object to reference the encoded value of anObject
285
refObject = makeReference(ref);
288
* objectInfo is a dictionary describing the object.
290
if (objectInfo != nil && m == objectInfo)
292
NSMutableDictionary *savedEnc = _enc;
293
unsigned savedKeyNum = _keyNum;
294
Class c = [anObject class];
299
* Map the class of the object to the actual class it is encoded as.
300
* First ask the object, then apply any name mappings to that value.
302
mapped = [anObject classForKeyedArchiver];
308
classname = [self classNameForClass: c];
309
if (classname == nil)
311
classname = [[self class] classNameForClass: c];
313
if (classname == nil)
315
classname = NSStringFromClass(c);
319
c = NSClassFromString(classname);
323
* At last, get the object to encode itself. Save and restore the
324
* current object scope of course.
328
[anObject encodeWithCoder: self];
329
_keyNum = savedKeyNum;
333
* This is ugly, but it seems to be the way MacOS-X does it ...
334
* We create class information by storing it directly into the
335
* table of all objects, and making a reference so we can look
336
* up the table entry by class pointer.
337
* A much cleaner way to do it would be by encoding the class
338
* normally, but we are trying to be compatible.
340
* Also ... we encode the class *after* encoding the instance,
341
* simply because that seems to be the way MacOS-X does it and
342
* we want to maximise compatibility (perhaps they had good reason?)
344
node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)c);
347
NSMutableDictionary *cDict;
348
NSMutableArray *hierarchy;
351
GSIMapAddPair(_uIdMap, (GSIMapKey)c, (GSIMapVal)ref);
352
cDict = [[NSMutableDictionary alloc] initWithCapacity: 2];
357
[cDict setObject: classname forKey: @"$classname"];
360
* Record the class hierarchy for this object.
362
hierarchy = [NSMutableArray new];
365
Class next = [c superClass];
367
[hierarchy addObject: NSStringFromClass(c)];
374
[cDict setObject: hierarchy forKey: @"$classes"];
376
[_obj addObject: cDict];
381
ref = node->value.uint;
385
* Now create a reference to the class information and store it
386
* in the object description dictionary for the object we just encoded.
388
[m setObject: makeReference(ref) forKey: @"$class"];
392
* If we have encoded the object information, tell the delegaate.
394
if (objectInfo != nil && _delegate != nil)
396
[_delegate archiver: self didEncodeObject: anObject];
400
* Return the dictionary identifying the encoded object.
406
@implementation NSKeyedArchiver
409
* When I tried this on MacOS 10.3 it encoded the object with the key 'root',
410
* so this implementation does the same.
412
+ (NSData*) archivedDataWithRootObject: (id)anObject
414
NSMutableData *m = nil;
415
NSKeyedArchiver *a = nil;
420
m = [[NSMutableData alloc] initWithCapacity: 10240];
421
a = [[NSKeyedArchiver alloc] initForWritingWithMutableData: m];
422
[a encodeObject: anObject forKey: @"root"];
432
[localException raise];
435
return AUTORELEASE(d);
438
+ (BOOL) archiveRootObject: (id)anObject toFile: (NSString*)aPath
440
CREATE_AUTORELEASE_POOL(pool);
444
d = [self archivedDataWithRootObject: anObject];
445
result = [d writeToFile: aPath atomically: YES];
450
+ (NSString*) classNameForClass: (Class)aClass
452
return (NSString*)NSMapGet(globalClassMap, (void*)aClass);
457
if (globalClassMap == 0)
460
NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
461
NSObjectMapValueCallBacks, 0);
465
+ (void) setClassName: (NSString*)aString forClass: (Class)aClass
469
NSMapRemove(globalClassMap, (void*)aClass);
473
NSMapInsert(globalClassMap, (void*)aClass, aString);
477
- (BOOL) allowsKeyedCoding
482
- (NSString*) classNameForClass: (Class)aClass
484
return (NSString*)NSMapGet(_clsMap, (void*)aClass);
494
NSFreeMapTable(_clsMap);
499
GSIMapEmptyMap(_cIdMap);
502
GSIMapEmptyMap(_uIdMap);
506
GSIMapEmptyMap(_repMap);
508
NSZoneFree(_cIdMap->zone, (void*)_cIdMap);
518
- (void) encodeArrayOfObjCType: (const char*)aType
519
count: (unsigned)aCount
520
at: (const void*)address
524
o = [[_NSKeyedCoderOldStyleArray alloc] initWithObjCType: aType
527
[self encodeObject: o];
531
- (void) encodeBool: (BOOL)aBool forKey: (NSString*)aKey
535
[_enc setObject: [NSNumber numberWithBool: aBool] forKey: aKey];
538
- (void) encodeBytes: (const uint8_t*)aPointer length: (unsigned)length forKey: (NSString*)aKey
542
[_enc setObject: [NSData dataWithBytes: aPointer length: length]
546
- (void) encodeConditionalObject: (id)anObject
548
NSString *aKey = [NSString stringWithFormat: @"$%u", _keyNum++];
550
anObject = [self _encodeObject: anObject conditional: YES];
551
[_enc setObject: anObject forKey: aKey];
554
- (void) encodeConditionalObject: (id)anObject forKey: (NSString*)aKey
558
anObject = [self _encodeObject: anObject conditional: YES];
559
[_enc setObject: anObject forKey: aKey];
562
- (void) encodeDouble: (double)aDouble forKey: (NSString*)aKey
566
[_enc setObject: [NSNumber numberWithDouble: aDouble] forKey: aKey];
569
- (void) encodeFloat: (float)aFloat forKey: (NSString*)aKey
573
[_enc setObject: [NSNumber numberWithFloat: aFloat] forKey: aKey];
576
- (void) encodeInt: (int)anInteger forKey: (NSString*)aKey
580
[_enc setObject: [NSNumber numberWithInt: anInteger] forKey: aKey];
583
- (void) encodeInt32: (int32_t)anInteger forKey: (NSString*)aKey
587
[_enc setObject: [NSNumber numberWithLong: anInteger] forKey: aKey];
590
- (void) encodeInt64: (int64_t)anInteger forKey: (NSString*)aKey
594
[_enc setObject: [NSNumber numberWithLongLong: anInteger] forKey: aKey];
597
- (void) encodeObject: (id)anObject
599
NSString *aKey = [NSString stringWithFormat: @"$%u", _keyNum++];
601
anObject = [self _encodeObject: anObject conditional: NO];
602
[_enc setObject: anObject forKey: aKey];
605
- (void) encodeObject: (id)anObject forKey: (NSString*)aKey
609
anObject = [self _encodeObject: anObject conditional: NO];
610
[_enc setObject: anObject forKey: aKey];
613
- (void) encodePoint: (NSPoint)p
615
[self encodeValueOfObjCType: @encode(float) at: &p.x];
616
[self encodeValueOfObjCType: @encode(float) at: &p.y];
619
- (void) encodeRect: (NSRect)r
621
[self encodeValueOfObjCType: @encode(float) at: &r.origin.x];
622
[self encodeValueOfObjCType: @encode(float) at: &r.origin.y];
623
[self encodeValueOfObjCType: @encode(float) at: &r.size.width];
624
[self encodeValueOfObjCType: @encode(float) at: &r.size.height];
627
- (void) encodeSize: (NSSize)s
629
[self encodeValueOfObjCType: @encode(float) at: &s.width];
630
[self encodeValueOfObjCType: @encode(float) at: &s.height];
633
- (void) encodeValueOfObjCType: (const char*)type
634
at: (const void*)address
639
if (*type == _C_ID || *type == _C_CLASS)
641
[self encodeObject: *(id*)address];
645
aKey = [NSString stringWithFormat: @"$%u", _keyNum++];
650
// Selectors are encoded by name as strings.
651
o = NSStringFromSelector(*(SEL*)address);
652
[self encodeObject: o];
659
* Bizzarely MacOS-X seems to encode char* values by creating
660
* string objects and encoding those objects!
662
o = [NSString stringWithCString: (char*)address];
663
[self encodeObject: o];
668
o = [NSNumber numberWithInt: (int)*(char*)address];
669
[_enc setObject: o forKey: aKey];
673
o = [NSNumber numberWithInt: (int)*(unsigned char*)address];
674
[_enc setObject: o forKey: aKey];
678
o = [NSNumber numberWithInt: (int)*(short*)address];
679
[_enc setObject: o forKey: aKey];
683
o = [NSNumber numberWithLong: (long)*(unsigned short*)address];
684
[_enc setObject: o forKey: aKey];
688
o = [NSNumber numberWithInt: *(int*)address];
689
[_enc setObject: o forKey: aKey];
693
o = [NSNumber numberWithUnsignedInt: *(unsigned int*)address];
694
[_enc setObject: o forKey: aKey];
698
o = [NSNumber numberWithLong: *(long*)address];
699
[_enc setObject: o forKey: aKey];
703
o = [NSNumber numberWithUnsignedLong: *(unsigned long*)address];
704
[_enc setObject: o forKey: aKey];
708
o = [NSNumber numberWithLongLong: *(long long*)address];
709
[_enc setObject: o forKey: aKey];
713
o = [NSNumber numberWithUnsignedLongLong:
714
*(unsigned long long*)address];
715
[_enc setObject: o forKey: aKey];
719
o = [NSNumber numberWithFloat: *(float*)address];
720
[_enc setObject: o forKey: aKey];
724
o = [NSNumber numberWithDouble: *(double*)address];
725
[_enc setObject: o forKey: aKey];
729
[NSException raise: NSInvalidArgumentException
730
format: @"-[%@ %@]: this archiver cannote encode structs",
731
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
734
default: /* Types that can be ignored in first pass. */
735
[NSException raise: NSInvalidArgumentException
736
format: @"-[%@ %@]: unknown type encoding ('%c')",
737
NSStringFromClass([self class]), NSStringFromSelector(_cmd), *type];
742
- (void) finishEncoding
744
NSMutableDictionary *final;
748
[_delegate archiverWillFinish: self];
750
final = [NSMutableDictionary new];
751
[final setObject: NSStringFromClass([self class]) forKey: @"$archiver"];
752
[final setObject: [NSNumber numberWithInt: 100000] forKey: @"$version"];
753
[final setObject: _enc forKey: @"$top"];
754
[final setObject: _obj forKey: @"$objects"];
755
data = [NSPropertyListSerialization dataFromPropertyList: final
757
errorDescription: &error];
759
[_data setData: data];
760
[_delegate archiverDidFinish: self];
763
- (id) initForWritingWithMutableData: (NSMutableData*)data
768
NSZone *zone = [self zone];
771
_data = RETAIN(data);
773
_clsMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
774
NSObjectMapValueCallBacks, 0);
778
_cIdMap = (GSIMapTable)NSZoneMalloc(zone, sizeof(GSIMapTable_t)*5);
779
_uIdMap = &_cIdMap[1];
780
_repMap = &_cIdMap[2];
781
GSIMapInitWithZoneAndCapacity(_cIdMap, zone, 10);
782
GSIMapInitWithZoneAndCapacity(_uIdMap, zone, 200);
783
GSIMapInitWithZoneAndCapacity(_repMap, zone, 1);
785
_enc = [NSMutableDictionary new]; // Top level mapping dict
786
_obj = [NSMutableArray new]; // Array of objects.
787
[_obj addObject: @"$null"]; // Placeholder.
789
_format = NSPropertyListXMLFormat_v1_0; // FIXME ... should be binary.
794
- (NSPropertyListFormat) outputFormat
799
- (void) setClassName: (NSString*)aString forClass: (Class)aClass
803
NSMapRemove(_clsMap, (void*)aClass);
807
NSMapInsert(_clsMap, (void*)aClass, aString);
811
- (void) setDelegate: (id)anObject
813
_delegate = anObject; // Not retained.
816
- (void) setOutputFormat: (NSPropertyListFormat)format
823
@implementation NSObject (NSKeyedArchiverDelegate)
824
- (void) archiver: (NSKeyedArchiver*)anArchiver didEncodeObject: (id)anObject
827
- (id) archiver: (NSKeyedArchiver*)anArchiver willEncodeObject: (id)anObject
831
- (void) archiverDidFinish: (NSKeyedArchiver*)anArchiver
834
- (void) archiverWillFinish: (NSKeyedArchiver*)anArchiver
837
- (void) archiver: (NSKeyedArchiver*)anArchiver
838
willReplaceObject: (id)anObject
839
withObject: (id)newObject
844
@implementation NSObject (NSKeyedArchiverObjectSubstitution)
845
- (Class) classForKeyedArchiver
847
return [self classForArchiver];
849
- (id) replacementObjectForKeyedArchiver: (NSKeyedArchiver*)archiver
851
return [self replacementObjectForArchiver: nil];
857
@implementation NSCoder (NSGeometryKeyedCoding)
858
- (void) encodePoint: (NSPoint)aPoint forKey: (NSString*)aKey
862
val = [NSString stringWithFormat: @"{%g, %g}", aPoint.x, aPoint.y];
863
[self encodeObject: val forKey: aKey];
866
- (void) encodeRect: (NSRect)aRect forKey: (NSString*)aKey
870
val = [NSString stringWithFormat: @"{{%g, %g}, {%g, %g}}",
871
aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height];
872
[self encodeObject: val forKey: aKey];
875
- (void) encodeSize: (NSSize)aSize forKey: (NSString*)aKey
879
val = [NSString stringWithFormat: @"{%g, %g}", aSize.width, aSize.height];
880
[self encodeObject: val forKey: aKey];
883
- (NSPoint) decodePointForKey: (NSString*)aKey
885
NSString *val = [self decodeObjectForKey: aKey];
890
aPoint = NSMakePoint(0, 0);
897
scanner = (*scannerImp)(NSScannerClass, scannerSel, val);
898
if (!(*scanStringImp)(scanner, scanStringSel, @"{", NULL)
899
|| !(*scanFloatImp)(scanner, scanFloatSel, &aPoint.x)
900
|| !(*scanStringImp)(scanner, scanStringSel, @",", NULL)
901
|| !(*scanFloatImp)(scanner, scanFloatSel, &aPoint.y)
902
|| !(*scanStringImp)(scanner, scanStringSel, @"}", NULL))
904
[NSException raise: NSInvalidArgumentException
905
format: @"[%@ -%@]: bad value - '%@'",
906
NSStringFromClass([self class]), NSStringFromSelector(_cmd), val];
912
- (NSRect) decodeRectForKey: (NSString*)aKey
914
NSString *val = [self decodeObjectForKey: aKey];
919
aRect = NSMakeRect(0, 0, 0, 0);
926
scanner = (*scannerImp)(NSScannerClass, scannerSel, val);
927
if (!(*scanStringImp)(scanner, scanStringSel, @"{", NULL)
928
|| !(*scanStringImp)(scanner, scanStringSel, @"{", NULL)
929
|| !(*scanFloatImp)(scanner, scanFloatSel, &aRect.origin.x)
930
|| !(*scanStringImp)(scanner, scanStringSel, @",", NULL)
931
|| !(*scanFloatImp)(scanner, scanFloatSel, &aRect.origin.y)
932
|| !(*scanStringImp)(scanner, scanStringSel, @"}", NULL)
933
|| !(*scanStringImp)(scanner, scanStringSel, @",", NULL)
934
|| !(*scanStringImp)(scanner, scanStringSel, @"{", NULL)
935
|| !(*scanFloatImp)(scanner, scanFloatSel, &aRect.size.width)
936
|| !(*scanStringImp)(scanner, scanStringSel, @",", NULL)
937
|| !(*scanFloatImp)(scanner, scanFloatSel, &aRect.size.height)
938
|| !(*scanStringImp)(scanner, scanStringSel, @"}", NULL)
939
|| !(*scanStringImp)(scanner, scanStringSel, @"}", NULL))
941
[NSException raise: NSInvalidArgumentException
942
format: @"[%@ -%@]: bad value - '%@'",
943
NSStringFromClass([self class]), NSStringFromSelector(_cmd), val];
949
- (NSSize) decodeSizeForKey: (NSString*)aKey
951
NSString *val = [self decodeObjectForKey: aKey];
956
aSize = NSMakeSize(0, 0);
963
scanner = (*scannerImp)(NSScannerClass, scannerSel, val);
964
if (!(*scanStringImp)(scanner, scanStringSel, @"{", NULL)
965
|| !(*scanFloatImp)(scanner, scanFloatSel, &aSize.width)
966
|| !(*scanStringImp)(scanner, scanStringSel, @",", NULL)
967
|| !(*scanFloatImp)(scanner, scanFloatSel, &aSize.height)
968
|| !(*scanStringImp)(scanner, scanStringSel, @"}", NULL))
970
[NSException raise: NSInvalidArgumentException
971
format: @"[%@ -%@]: bad value - '%@'",
972
NSStringFromClass([self class]), NSStringFromSelector(_cmd), val];