diff --git a/ChangeLog b/ChangeLog index 20df8730e..0ea3bbe14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,22 @@ +2004-01-25 Richard Frith-Macdonald + + * Source/NSPropertyList.m: Fix bug in encoding integers as xml. + * Source/GSPrivate.h: Added internal class for handling old style + arrays of items within keyed coding ... should this go elsewhere? + * Source/NSCoder.m: Added internal class for keyed coding of + arrays of items. + * Source/NSKeyedArchiver.m: Added support for + ([encodeArrayOfobjCType:count:at:]) and the private + ([_encodeArrayOfObjects:forKey:]) method. This completes (afaik) the + implementation for archiving ... and it's somewhat tested for xml + coding. Time to start using and debugging. + * Source/NSKeyedUnarchiver.m: All methods now implemented, but no + proper type checking/coercion done, and minimal testing done. + No attempt to handle OpenStep style property lists either, just xml! + 2004-01-24 Richard Frith-Macdonald - * wHeaders/Foundation/NSKeyedArchiver.h: + * Headers/Foundation/NSKeyedArchiver.h: * Source/NSKeyedArchiver.m: Add keyed version of geometry encoding methods as pointed out by Fred Kiefer. diff --git a/Source/GSPrivate.h b/Source/GSPrivate.h index 94a484c85..38505c93b 100644 --- a/Source/GSPrivate.h +++ b/Source/GSPrivate.h @@ -134,5 +134,28 @@ BOOL GSUserDefaultsFlag(GSUserDefaultFlagType type); */ BOOL GSEnvironmentFlag(const char *name, BOOL def); + + +/** + * This class exists simply as a mechanism for encapsulating arrays + * encoded using [NSKeyedArchiver-encodeArrayOfObjCType:count:at:] + */ +@interface _NSKeyedCoderOldStyleArray : NSObject +{ + char _t[2]; + unsigned _c; + unsigned _s; + const void *_a; + NSData *_d; // Only valid after initWithCoder: +} +- (const void*) bytes; +- (unsigned) count; +- (void) encodeWithCoder: (NSCoder*)aCoder; +- (id) initWithCoder: (NSCoder*)aCoder; +- (id) initWithObjCType: (const char*)t count: (int)c at: (const void*)a; +- (unsigned) size; +- (const char*) type; +@end + #endif /* __GSPrivate_h_ */ diff --git a/Source/NSCoder.m b/Source/NSCoder.m index f28f73755..2610164b4 100644 --- a/Source/NSCoder.m +++ b/Source/NSCoder.m @@ -489,5 +489,85 @@ [self encodeValueOfObjCType: @encode(id) at: anObject withName: name]; } - @end + + + +#include "GSPrivate.h" + +@implementation _NSKeyedCoderOldStyleArray +- (const void*) bytes +{ + return _a; +} +- (unsigned) count +{ + return _c; +} +- (void) dealloc +{ + DESTROY(_d); + [super dealloc]; +} +- (id) initWithCoder: (NSCoder*)aCoder +{ + id o; + void *address; + unsigned i; + + _c = [aCoder decodeIntForKey: @"NS.count"]; + _t[0] = (char)[aCoder decodeIntForKey: @"NS.type"]; + _t[1] = '\0'; + + /* + * We decode the size from the remote end, but discard it as we + * are probably safer to use the local size of the datatype involved. + */ + _s = [aCoder decodeIntForKey: @"NS.size"]; + _s = objc_sizeof_type(_t); + + _d = o = [[NSMutableData alloc] initWithLength: _c * _s]; + _a = address = [o mutableBytes]; + for (i = 0; i < _c; i++) + { + [aCoder decodeValueOfObjCType: _t at: address]; + address += _s; + } + return self; +} + +- (id) initWithObjCType: (const char*)t count: (int)c at: (const void*)a +{ + _t[0] = *t; + _t[1] = '\0'; + _s = objc_sizeof_type(_t); + _c = c; + _a = a; + return self; +} + +- (void) encodeWithCoder: (NSCoder*)aCoder +{ + int i; + + [aCoder encodeInt: _c forKey: @"NS.count"]; + [aCoder encodeInt: *_t forKey: @"NS.type"]; + [aCoder encodeInt: _s forKey: @"NS.size"]; + for (i = 0; i < _c; i++) + { + [aCoder encodeValueOfObjCType: _t at: _a]; + _a += _s; + } +} + +- (unsigned) size +{ + return _s; +} + +- (const char*) type +{ + return _t; +} +@end + diff --git a/Source/NSKeyedArchiver.m b/Source/NSKeyedArchiver.m index d31de9bc1..4b23a0aab 100644 --- a/Source/NSKeyedArchiver.m +++ b/Source/NSKeyedArchiver.m @@ -28,16 +28,20 @@ #include #include +#include "GSPrivate.h" + +@class GSString; + /* * Setup for inline operation of pointer map tables. */ -#define GSI_MAP_RETAIN_KEY(M, X) -#define GSI_MAP_RELEASE_KEY(M, X) +#define GSI_MAP_RETAIN_KEY(M, X) RETAIN(X.obj) +#define GSI_MAP_RELEASE_KEY(M, X) RELEASE(X.obj) #define GSI_MAP_RETAIN_VAL(M, X) #define GSI_MAP_RELEASE_VAL(M, X) #define GSI_MAP_HASH(M, X) ((X).uint) #define GSI_MAP_EQUAL(M, X,Y) ((X).uint == (Y).uint) -#define GSI_MAP_NOCLEAN 1 +#undef GSI_MAP_NOCLEAN #include @@ -70,45 +74,70 @@ static NSMapTable *globalClassMap = 0; NSStringFromClass([self class]), aKey, NSStringFromSelector(_cmd)]; \ } +/* + * Make a dictionary referring to the object at ref in the array of all objects. + */ +static NSDictionary *makeReference(unsigned ref) +{ + NSNumber *n; + NSDictionary *d; + + n = [NSNumber numberWithUnsignedInt: ref]; + d = [NSDictionary dictionaryWithObject: n forKey: @"CF$UID"]; + return d; +} + @interface NSKeyedArchiver (Private) -- (NSDictionary*) _buildObjectReference: (id)anObject; -- (void) _encodeObject: (id)anObject - forKey: (NSString*)aKey - conditional: (BOOL)conditional; +- (void) _encodeArrayOfObjects: (NSArray*)anArray forKey: (NSString*)aKey; +- (id) _encodeObject: (id)anObject conditional: (BOOL)conditional; @end @implementation NSKeyedArchiver (Private) -/* - * Add an object to the table off all encoded objects, and return a reference. - */ -- (NSDictionary*) _buildObjectReference: (id)anObject -{ - unsigned ref = 0; - if (anObject != nil) +/** + * Internal method used to encode an array relatively efficiently.
+ * Some MacOS-X library classes seem to use this. + */ +- (void) _encodeArrayOfObjects: (NSArray*)anArray forKey: (NSString*)aKey +{ + id o; + CHECKKEY + + if (anArray == nil) { - ref = [_obj count]; - [_obj addObject: anObject]; + o = makeReference(0); } - return [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: ref] - forKey: @"CF$UID"]; + else + { + NSMutableArray *m; + unsigned c; + unsigned i; + + c = [anArray count]; + m = [NSMutableArray arrayWithCapacity: c]; + for (i = 0; i < c; i++) + { + o = [self _encodeObject: [anArray objectAtIndex: i] conditional: NO]; + [m addObject: o]; + } + o = m; + } + [_enc setObject: o forKey: aKey]; } /* * The real workhorse of the archiving process ... this deals with all - * archiving of objects. + * archiving of objects. It returns the object to be stored in the + * mapping dictionary (_enc). */ -- (void) _encodeObject: (id)anObject - forKey: (NSString*)aKey - conditional: (BOOL)conditional +- (id) _encodeObject: (id)anObject conditional: (BOOL)conditional { id original = anObject; GSIMapNode node; id objectInfo = nil; // Encoded object NSMutableDictionary *m = nil; - NSNumber *refNum; - NSDictionary *keyDict; - unsigned ref = 0; + NSDictionary *refObject; + unsigned ref = 0; // Reference to nil if (anObject != nil) { @@ -166,8 +195,11 @@ static NSMapTable *globalClassMap = 0; } else { + Class c = [anObject class]; + // FIXME ... exactly what classes are stored directly??? - if ([anObject isKindOfClass: [NSString class]] == YES) + if ([anObject isKindOfClass: [GSString class]] == YES + || c == [@"literal" class]) { // We will store the string object directly. objectInfo = anObject; @@ -209,12 +241,9 @@ static NSMapTable *globalClassMap = 0; } /* - * Store the mapping from aKey to the appropriate entry in _obj + * Build an object to reference the encoded value of anObject */ - refNum = [[NSNumber alloc] initWithInt: ref]; - keyDict = [NSDictionary dictionaryWithObject: refNum forKey: @"CF$UID"]; - [_enc setObject: keyDict forKey: aKey]; - RELEASE(refNum); + refObject = makeReference(ref); /* * objectInfo is a dictionary describing the object. @@ -317,10 +346,7 @@ static NSMapTable *globalClassMap = 0; * Now create a reference to the class information and store it * in the object description dictionary for the object we just encoded. */ - refNum = [[NSNumber alloc] initWithInt: ref]; - keyDict = [NSDictionary dictionaryWithObject: refNum forKey: @"CF$UID"]; - [m setObject: keyDict forKey: @"$class"]; - RELEASE(refNum); + [m setObject: makeReference(ref) forKey: @"$class"]; } /* @@ -330,6 +356,11 @@ static NSMapTable *globalClassMap = 0; { [_delegate archiver: self didEncodeObject: anObject]; } + + /* + * Return the dictionary identifying the encoded object. + */ + return refObject; } @end @@ -445,6 +476,19 @@ static NSMapTable *globalClassMap = 0; return _delegate; } +- (void) encodeArrayOfObjCType: (const char*)aType + count: (unsigned)aCount + at: (const void*)address +{ + id o; + + o = [[_NSKeyedCoderOldStyleArray alloc] initWithObjCType: aType + count: aCount + at: address]; + [self encodeObject: o]; + RELEASE(o); +} + - (void) encodeBool: (BOOL)aBool forKey: (NSString*)aKey { CHECKKEY @@ -462,16 +506,18 @@ static NSMapTable *globalClassMap = 0; - (void) encodeConditionalObject: (id)anObject { - [self _encodeObject: anObject - forKey: [NSString stringWithFormat: @"$%u", _keyNum++] - conditional: YES]; + NSString *aKey = [NSString stringWithFormat: @"$%u", _keyNum++]; + + anObject = [self _encodeObject: anObject conditional: YES]; + [_enc setObject: anObject forKey: aKey]; } - (void) encodeConditionalObject: (id)anObject forKey: (NSString*)aKey { CHECKKEY - [self _encodeObject: anObject forKey: aKey conditional: YES]; + anObject = [self _encodeObject: anObject conditional: YES]; + [_enc setObject: anObject forKey: aKey]; } - (void) encodeDouble: (double)aDouble forKey: (NSString*)aKey @@ -511,16 +557,18 @@ static NSMapTable *globalClassMap = 0; - (void) encodeObject: (id)anObject { - [self _encodeObject: anObject - forKey: [NSString stringWithFormat: @"$%u", _keyNum++] - conditional: NO]; + NSString *aKey = [NSString stringWithFormat: @"$%u", _keyNum++]; + + anObject = [self _encodeObject: anObject conditional: NO]; + [_enc setObject: anObject forKey: aKey]; } - (void) encodeObject: (id)anObject forKey: (NSString*)aKey { CHECKKEY - [self _encodeObject: anObject forKey: aKey conditional: NO]; + anObject = [self _encodeObject: anObject conditional: NO]; + [_enc setObject: anObject forKey: aKey]; } - (void) encodePoint: (NSPoint)p diff --git a/Source/NSKeyedUnarchiver.m b/Source/NSKeyedUnarchiver.m index dcfeebec8..f569db48f 100644 --- a/Source/NSKeyedUnarchiver.m +++ b/Source/NSKeyedUnarchiver.m @@ -29,6 +29,8 @@ #include #include +#include "GSPrivate.h" + /* * Setup for inline operation of arrays. */ @@ -49,7 +51,6 @@ NSString * const NSInvalidUnarchiveOperationException static NSMapTable globalClassMap = 0; #define GETVAL \ - NSString *oldKey = aKey; \ id o; \ \ if ([aKey isKindOfClass: [NSString class]] == NO) \ @@ -62,21 +63,60 @@ static NSMapTable globalClassMap = 0; { \ aKey = [@"$" stringByAppendingString: aKey]; \ } \ - if ([_keyMap objectForKey: aKey] != nil) \ - { \ - [NSException raise: NSInvalidArgumentException \ - format: @"%@, duplicate key '%@' in %@", \ - NSStringFromClass([self class]), aKey, NSStringFromSelector(_cmd)]; \ - } \ o = [_keyMap objectForKey: aKey]; @interface NSKeyedUnarchiver (Private) +- (id) _decodeArrayOfObjectsForKey: (NSString*)aKey; - (id) _decodeObject: (unsigned)index; @end @implementation NSKeyedUnarchiver (Private) +/** + * Internal method used to decode an array relatively efficiently.
+ * Some MacOS-X library classes seem to use this. + */ +- (id) _decodeArrayOfObjectsForKey: (NSString*)aKey +{ + id o = [_keyMap objectForKey: aKey]; + + if (o != nil) + { + if ([o isKindOfClass: [NSArray class]] == YES) + { + unsigned c = [o count]; + NSMutableArray *m = [NSMutableArray arrayWithCapacity: c]; + unsigned i; + + for (i = 0; i < c; i++) + { + unsigned ref; + id val; + + ref = [[[o objectAtIndex: i] objectForKey: @"CF$UID"] + unsignedIntValue]; + val = [self _decodeObject: ref]; + if (val == nil) + { + [NSException raise: + NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: decoded nil in array", + NSStringFromClass([self class]), + NSStringFromSelector(_cmd)]; + } + [m addObject: val]; + } + o = m; + } + else + { + o = nil; + } + } + return o; +} + - (id) _decodeObject: (unsigned)index { id o; @@ -308,8 +348,36 @@ static NSMapTable globalClassMap = 0; [super dealloc]; } +- (void) decodeArrayOfObjCType: (const char*)type + count: (unsigned)expected + at: (void*)buf +{ + id o = [self decodeObject]; + + if ([o isKindOfClass: [_NSKeyedCoderOldStyleArray class]] == NO) + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: value is '%@'", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), o]; + } + if (strcmp([o type], type) != 0) + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: type missmatch", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), o]; + } + if ([o count] != expected) + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: count missmatch", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), o]; + } + memcpy(buf, [o bytes], expected * objc_sizeof_type(type)); +} + - (BOOL) decodeBoolForKey: (NSString*)aKey { + NSString *oldKey = aKey; GETVAL if (o != nil) { @@ -331,6 +399,7 @@ static NSMapTable globalClassMap = 0; - (const uint8_t*) decodeBytesForKey: (NSString*)aKey returnedLength: (unsigned*)length { + NSString *oldKey = aKey; GETVAL if (o != nil) { @@ -353,6 +422,7 @@ static NSMapTable globalClassMap = 0; - (double) decodeDoubleForKey: (NSString*)aKey { + NSString *oldKey = aKey; GETVAL if (o != nil) { @@ -373,6 +443,7 @@ static NSMapTable globalClassMap = 0; - (float) decodeFloatForKey: (NSString*)aKey { + NSString *oldKey = aKey; GETVAL if (o != nil) { @@ -393,6 +464,7 @@ static NSMapTable globalClassMap = 0; - (int) decodeIntForKey: (NSString*)aKey { + NSString *oldKey = aKey; GETVAL if (o != nil) { @@ -415,6 +487,7 @@ static NSMapTable globalClassMap = 0; - (int32_t) decodeInt32ForKey: (NSString*)aKey { + NSString *oldKey = aKey; GETVAL if (o != nil) { @@ -437,6 +510,7 @@ static NSMapTable globalClassMap = 0; - (int64_t) decodeInt64ForKey: (NSString*)aKey { + NSString *oldKey = aKey; GETVAL if (o != nil) { @@ -485,6 +559,7 @@ static NSMapTable globalClassMap = 0; - (id) decodeObjectForKey: (NSString*)aKey { + NSString *oldKey = aKey; GETVAL if (o != nil) { diff --git a/Source/NSPropertyList.m b/Source/NSPropertyList.m index a03c0c735..cd22dc959 100644 --- a/Source/NSPropertyList.m +++ b/Source/NSPropertyList.m @@ -1531,7 +1531,7 @@ OAppend(id obj, NSDictionary *loc, unsigned lev, unsigned step, { if (x == NSPropertyListXMLFormat_v1_0) { - [dest appendBytes: "" length: 8]; + [dest appendBytes: "" length: 9]; XString([obj stringValue], dest); [dest appendBytes: "\n" length: 11]; }