diff --git a/JSONKit.h b/JSONKit.h old mode 100644 new mode 100755 index 71bd0c3..7346805 --- a/JSONKit.h +++ b/JSONKit.h @@ -1,8 +1,7 @@ // // JSONKit.h // http://github.com/johnezang/JSONKit -// Dual licensed under either the terms of the BSD License, or alternatively -// under the terms of the Apache License, Version 2.0, as specified below. +// Licensed under the terms of the BSD License, as specified below. // /* @@ -37,22 +36,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* - Copyright 2011 John Engelhart - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - #include #include #include @@ -126,11 +109,10 @@ enum { typedef JKFlags JKParseOptionFlags; enum { - JKSerializeOptionNone = 0, - JKSerializeOptionPretty = (1 << 0), - JKSerializeOptionEscapeUnicode = (1 << 1), - JKSerializeOptionEscapeForwardSlashes = (1 << 4), - JKSerializeOptionValidFlags = (JKSerializeOptionPretty | JKSerializeOptionEscapeUnicode | JKSerializeOptionEscapeForwardSlashes), + JKSerializeOptionNone = 0, + JKSerializeOptionPretty = (1 << 0), + JKSerializeOptionEscapeUnicode = (1 << 1), + JKSerializeOptionValidFlags = (JKSerializeOptionPretty | JKSerializeOptionEscapeUnicode), }; typedef JKFlags JKSerializeOptionFlags; @@ -212,35 +194,16 @@ typedef struct JKParseState JKParseState; // Opaque internal, private type. @interface NSArray (JSONKitSerializing) - (NSData *)JSONData; - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error; -- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error; - (NSString *)JSONString; - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error; -- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error; @end @interface NSDictionary (JSONKitSerializing) - (NSData *)JSONData; - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error; -- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error; - (NSString *)JSONString; - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error; -- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error; -@end - -#ifdef __BLOCKS__ - -@interface NSArray (JSONKitSerializingBlockAdditions) -- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error; -- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error; -@end - -@interface NSDictionary (JSONKitSerializingBlockAdditions) -- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error; -- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error; @end - -#endif - #endif // __OBJC__ diff --git a/JSONKit.m b/JSONKit.m old mode 100644 new mode 100755 index 0e9331f..e09749f --- a/JSONKit.m +++ b/JSONKit.m @@ -1,8 +1,7 @@ // // JSONKit.m // http://github.com/johnezang/JSONKit -// Dual licensed under either the terms of the BSD License, or alternatively -// under the terms of the Apache License, Version 2.0, as specified below. +// Licensed under the terms of the BSD License, as specified below. // /* @@ -37,22 +36,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* - Copyright 2011 John Engelhart - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - /* Acknowledgments: @@ -124,10 +107,6 @@ The code in isValidCodePoint() is derived from the ICU code in #import #import -#ifndef __has_feature -#define __has_feature(x) 0 -#endif - #ifdef JK_ENABLE_CF_TRANSFER_OWNERSHIP_CALLBACKS #warning As of JSONKit v1.4, JK_ENABLE_CF_TRANSFER_OWNERSHIP_CALLBACKS is no longer required. It is no longer a valid option. #endif @@ -136,10 +115,6 @@ The code in isValidCodePoint() is derived from the ICU code in #error JSONKit does not support Objective-C Garbage Collection #endif -#if __has_feature(objc_arc) -#error JSONKit does not support Objective-C Automatic Reference Counting (ARC) -#endif - // The following checks are really nothing more than sanity checks. // JSONKit technically has a few problems from a "strictly C99 conforming" standpoint, though they are of the pedantic nitpicking variety. // In practice, though, for the compilers and architectures we can reasonably expect this code to be compiled for, these pedantic nitpicks aren't really a problem. @@ -175,7 +150,7 @@ The code in isValidCodePoint() is derived from the ICU code in #define JK_CACHE_SLOTS (1UL << JK_CACHE_SLOTS_BITS) // JK_CACHE_PROBES is the number of probe attempts. #define JK_CACHE_PROBES (4UL) -// JK_INIT_CACHE_AGE must be < (1 << AGE) - 1, where AGE is sizeof(typeof(AGE)) * 8. +// JK_INIT_CACHE_AGE must be (1 << AGE) - 1 #define JK_INIT_CACHE_AGE (0) // JK_TOKENBUFFER_SIZE is the default stack size for the temporary buffer used to hold "non-simple" strings (i.e., contains \ escapes) @@ -353,12 +328,8 @@ The code in isValidCodePoint() is derived from the ICU code in typedef struct JKObjCImpCache JKObjCImpCache; typedef struct JKHashTableEntry JKHashTableEntry; -typedef id (*NSNumberAllocImp)(id receiver, SEL selector); -typedef id (*NSNumberInitWithUnsignedLongLongImp)(id receiver, SEL selector, unsigned long long value); -typedef id (*JKClassFormatterIMP)(id receiver, SEL selector, id object); -#ifdef __BLOCKS__ -typedef id (^JKClassFormatterBlock)(id formatObject); -#endif +typedef id (*NSNumberAllocImp)(id object, SEL selector); +typedef id (*NSNumberInitWithUnsignedLongLongImp)(id object, SEL selector, unsigned long long value); struct JKPtrRange { @@ -475,12 +446,6 @@ The code in isValidCodePoint() is derived from the ICU code in JKEncodeOptionType encodeOption; size_t depth; NSError *error; - id classFormatterDelegate; - SEL classFormatterSelector; - JKClassFormatterIMP classFormatterIMP; -#ifdef __BLOCKS__ - JKClassFormatterBlock classFormatterBlock; -#endif }; // This is a JSONKit private class. @@ -488,14 +453,8 @@ @interface JKSerializer : NSObject { JKEncodeState *encodeState; } -#ifdef __BLOCKS__ -#define JKSERIALIZER_BLOCKS_PROTO id(^)(id object) -#else -#define JKSERIALIZER_BLOCKS_PROTO id -#endif - -+ (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error; -- (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error; ++ (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption error:(NSError **)error; +- (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption error:(NSError **)error; - (void)releaseState; @end @@ -548,7 +507,12 @@ - (void)releaseState; #define JK_END_STRING_PTR(x) (&((x)->stringBuffer.bytes.ptr[(x)->stringBuffer.bytes.length])) +static void jk_swizzleInstanceMethod(Class fromClass, Class toClass, SEL selector); +static void jk_swizzleClassMethod(Class fromClass, Class toClass, SEL selector); + static JKArray *_JKArrayCreate(id *objects, NSUInteger count, BOOL mutableCollection); +static NSUInteger _JKArrayCount(JKArray *array); +static void _JKArrayIncrementMutations(JKArray *array); static void _JKArrayInsertObjectAtIndex(JKArray *array, id newObject, NSUInteger objectIndex); static void _JKArrayReplaceObjectAtIndexWithObject(JKArray *array, NSUInteger objectIndex, id newObject); static void _JKArrayRemoveObjectAtIndex(JKArray *array, NSUInteger objectIndex); @@ -558,10 +522,13 @@ - (void)releaseState; static JKDictionary *_JKDictionaryCreate(id *keys, NSUInteger *keyHashes, id *objects, NSUInteger count, BOOL mutableCollection); static JKHashTableEntry *_JKDictionaryHashEntry(JKDictionary *dictionary); static NSUInteger _JKDictionaryCapacity(JKDictionary *dictionary); +static NSUInteger _JKDictionaryCount(JKDictionary *dictionary); +static void _JKDictionaryIncrementMutations(JKDictionary *dictionary); static void _JKDictionaryResizeIfNeccessary(JKDictionary *dictionary); static void _JKDictionaryRemoveObjectWithEntry(JKDictionary *dictionary, JKHashTableEntry *entry); static void _JKDictionaryAddObject(JKDictionary *dictionary, NSUInteger keyHash, id key, id object); static JKHashTableEntry *_JKDictionaryHashTableEntryForKey(JKDictionary *dictionary, id aKey); +static NSUInteger _JKDictionaryGetKeysAndObjects(JKDictionary *dictionary, NSUInteger arrayLength, id keys[arrayLength], id objects[arrayLength]); static void _JSONDecoderCleanup(JSONDecoder *decoder); @@ -604,61 +571,31 @@ - (void)releaseState; JK_STATIC_INLINE void jk_encode_updateCache(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object); static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *objectPtr); -#define jk_encode_write1(es, dc, f) (JK_EXPECT_F(_jk_encode_prettyPrint) ? jk_encode_write1slow(es, dc, f) : jk_encode_write1fast(es, dc, f)) +#define jk_encode_write1(es, dc, f) (_jk_encode_useWrite1Fast ? jk_encode_write1fast(es, dc, f) : jk_encode_write1slow(es, dc, f)) JK_STATIC_INLINE size_t jk_min(size_t a, size_t b); JK_STATIC_INLINE size_t jk_max(size_t a, size_t b); -JK_STATIC_INLINE JKHash jk_calculateHash(JKHash currentHash, unsigned char c); - -// JSONKit v1.4 used both a JKArray : NSArray and JKMutableArray : NSMutableArray, and the same for the dictionary collection type. -// However, Louis Gerbarg (via cocoa-dev) pointed out that Cocoa / Core Foundation actually implements only a single class that inherits from the -// mutable version, and keeps an ivar bit for whether or not that instance is mutable. This means that the immutable versions of the collection -// classes receive the mutating methods, but this is handled by having those methods throw an exception when the ivar bit is set to immutable. -// We adopt the same strategy here. It's both cleaner and gets rid of the method swizzling hackery used in JSONKit v1.4. - - -// This is a workaround for issue #23 https://github.com/johnezang/JSONKit/pull/23 -// Basically, there seem to be a problem with using +load in static libraries on iOS. However, __attribute__ ((constructor)) does work correctly. -// Since we do not require anything "special" that +load provides, and we can accomplish the same thing using __attribute__ ((constructor)), the +load logic was moved here. - -static Class _JKArrayClass = NULL; -static size_t _JKArrayInstanceSize = 0UL; -static Class _JKDictionaryClass = NULL; -static size_t _JKDictionaryInstanceSize = 0UL; - -// For JSONDecoder... -static Class _jk_NSNumberClass = NULL; -static NSNumberAllocImp _jk_NSNumberAllocImp = NULL; -static NSNumberInitWithUnsignedLongLongImp _jk_NSNumberInitWithUnsignedLongLongImp = NULL; +JK_STATIC_INLINE JKHash calculateHash(JKHash currentHash, unsigned char c); -extern void jk_collectionClassLoadTimeInitialization(void) __attribute__ ((constructor)); +#pragma mark - +#pragma mark ObjC Voodoo -void jk_collectionClassLoadTimeInitialization(void) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Though technically not required, the run time environment at load time initialization may be less than ideal. - - _JKArrayClass = objc_getClass("JKArray"); - _JKArrayInstanceSize = jk_max(16UL, class_getInstanceSize(_JKArrayClass)); - - _JKDictionaryClass = objc_getClass("JKDictionary"); - _JKDictionaryInstanceSize = jk_max(16UL, class_getInstanceSize(_JKDictionaryClass)); - - // For JSONDecoder... - _jk_NSNumberClass = [NSNumber class]; - _jk_NSNumberAllocImp = (NSNumberAllocImp)[NSNumber methodForSelector:@selector(alloc)]; - - // Hacktacular. Need to do it this way due to the nature of class clusters. - id temp_NSNumber = [NSNumber alloc]; - _jk_NSNumberInitWithUnsignedLongLongImp = (NSNumberInitWithUnsignedLongLongImp)[temp_NSNumber methodForSelector:@selector(initWithUnsignedLongLong:)]; - [[temp_NSNumber init] release]; - temp_NSNumber = NULL; - - [pool release]; pool = NULL; +// These two functions are used to perform some ObjC swizzling voodoo to implement our mutable collection classes. +static void jk_swizzleInstanceMethod(Class fromClass, Class toClass, SEL selector) { + fromClass = class_isMetaClass(fromClass) ? objc_getClass(class_getName(fromClass)) : fromClass; + toClass = class_isMetaClass(toClass) ? objc_getClass(class_getName(toClass)) : toClass; + class_replaceMethod(fromClass, selector, method_getImplementation(class_getInstanceMethod(toClass, selector)), method_getTypeEncoding(class_getInstanceMethod(fromClass, selector))); } +static void jk_swizzleClassMethod(Class fromClass, Class toClass, SEL selector) { + fromClass = class_isMetaClass(fromClass) ? fromClass : objc_getMetaClass(class_getName(fromClass)); + toClass = class_isMetaClass(toClass) ? toClass : objc_getMetaClass(class_getName(toClass)); + class_replaceMethod(fromClass, selector, method_getImplementation(class_getClassMethod(toClass, selector)), method_getTypeEncoding(class_getClassMethod(fromClass, selector))); +} #pragma mark - -@interface JKArray : NSMutableArray { +@interface JKArray : NSArray { id *objects; NSUInteger count, capacity, mutations; } @@ -666,6 +603,21 @@ @interface JKArray : NSMutableArray 0UL)); JKArray *array = NULL; if(JK_EXPECT_T((array = (JKArray *)calloc(1UL, _JKArrayInstanceSize)) != NULL)) { // Directly allocate the JKArray instance via calloc. - array->isa = _JKArrayClass; + object_setClass(array, (mutableCollection == NO) ? _JKArrayClass : _JKMutableArrayClass); if((array = [array init]) == NULL) { return(NULL); } array->capacity = count; array->count = count; if(JK_EXPECT_F((array->objects = (id *)malloc(sizeof(id) * array->capacity)) == NULL)) { [array autorelease]; return(NULL); } memcpy(array->objects, objects, array->capacity * sizeof(id)); - array->mutations = (mutableCollection == NO) ? 0UL : 1UL; } return(array); } +static NSUInteger _JKArrayCount(JKArray *array) { + NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity)); + return(array->count); +} + +static void _JKArrayIncrementMutations(JKArray *array) { + NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity)); + array->mutations++; +} + // Note: The caller is responsible for -retaining the object that is to be added. static void _JKArrayInsertObjectAtIndex(JKArray *array, id newObject, NSUInteger objectIndex) { NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity) && (objectIndex <= array->count) && (newObject != NULL)); if(!((array != NULL) && (array->objects != NULL) && (objectIndex <= array->count) && (newObject != NULL))) { [newObject autorelease]; return; } - if((array->count + 1UL) >= array->capacity) { + array->count++; + if(array->count >= array->capacity) { + array->capacity += 16UL; id *newObjects = NULL; - if((newObjects = (id *)realloc(array->objects, sizeof(id) * (array->capacity + 16UL))) == NULL) { [NSException raise:NSMallocException format:@"Unable to resize objects array."]; } + if((newObjects = (id *)realloc(array->objects, sizeof(id) * array->capacity)) == NULL) { [NSException raise:NSMallocException format:@"Unable to resize objects array."]; } array->objects = newObjects; - array->capacity += 16UL; memset(&array->objects[array->count], 0, sizeof(id) * (array->capacity - array->count)); } - array->count++; if((objectIndex + 1UL) < array->count) { memmove(&array->objects[objectIndex + 1UL], &array->objects[objectIndex], sizeof(id) * ((array->count - 1UL) - objectIndex)); array->objects[objectIndex] = NULL; } array->objects[objectIndex] = newObject; } @@ -714,11 +675,11 @@ static void _JKArrayReplaceObjectAtIndexWithObject(JKArray *array, NSUInteger ob } static void _JKArrayRemoveObjectAtIndex(JKArray *array, NSUInteger objectIndex) { - NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count > 0UL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL)); - if(!((array != NULL) && (array->objects != NULL) && (array->count > 0UL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL))) { return; } + NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL)); + if(!((array != NULL) && (array->objects != NULL) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL))) { return; } CFRelease(array->objects[objectIndex]); array->objects[objectIndex] = NULL; - if((objectIndex + 1UL) < array->count) { memmove(&array->objects[objectIndex], &array->objects[objectIndex + 1UL], sizeof(id) * ((array->count - 1UL) - objectIndex)); array->objects[array->count - 1UL] = NULL; } + if((objectIndex + 1UL) < array->count) { memmove(&array->objects[objectIndex], &array->objects[objectIndex + 1UL], sizeof(id) * ((array->count - 1UL) - objectIndex)); array->objects[array->count] = NULL; } array->count--; } @@ -744,9 +705,7 @@ - (void)getObjects:(id *)objectsPtr range:(NSRange)range NSParameterAssert((objects != NULL) && (count <= capacity)); if((objectsPtr == NULL) && (NSMaxRange(range) > 0UL)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: pointer to objects array is NULL but range length is %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)NSMaxRange(range)]; } if((range.location > count) || (NSMaxRange(range) > count)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)NSMaxRange(range), (unsigned long)count]; } -#ifndef __clang_analyzer__ memcpy(objectsPtr, objects + range.location, range.length * sizeof(id)); -#endif } - (id)objectAtIndex:(NSUInteger)objectIndex @@ -768,52 +727,87 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state object return(enumeratedCount); } +- (id)copyWithZone:(NSZone *)zone +{ +#pragma unused(zone) + NSParameterAssert((objects != NULL) && (count <= capacity)); + return([self retain]); +} + +- (id)mutableCopyWithZone:(NSZone *)zone +{ + NSParameterAssert((objects != NULL) && (count <= capacity)); + return([[NSMutableArray allocWithZone:zone] initWithObjects:objects count:count]); +} + +@end + +#pragma mark - +@interface JKMutableArray : NSMutableArray +@end + +@implementation JKMutableArray + ++ (void)load +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Though technically not required, the run time environment at +load time may be less than ideal. + + Class JKMutableArrayClass = objc_getClass("JKMutableArray"); Class JKArrayClass = objc_getClass("JKArray"); + + // We swizzle the methods from JKArray in to this class (JKArrayDictionary). + + jk_swizzleClassMethod(JKMutableArrayClass, JKArrayClass, @selector(allocWithZone:)); + + jk_swizzleInstanceMethod(JKMutableArrayClass, JKArrayClass, @selector(dealloc)); + jk_swizzleInstanceMethod(JKMutableArrayClass, JKArrayClass, @selector(count)); + jk_swizzleInstanceMethod(JKMutableArrayClass, JKArrayClass, @selector(objectAtIndex:)); + jk_swizzleInstanceMethod(JKMutableArrayClass, JKArrayClass, @selector(getObjects:range:)); + jk_swizzleInstanceMethod(JKMutableArrayClass, JKArrayClass, @selector(countByEnumeratingWithState:objects:count:)); + + [pool release]; pool = NULL; +} + - (void)insertObject:(id)anObject atIndex:(NSUInteger)objectIndex { - if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } - if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } - if(objectIndex > count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)(count + 1UL)]; } -#ifdef __clang_analyzer__ - [anObject retain]; // Stupid clang analyzer... Issue #19. -#else + if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } + if(objectIndex > _JKArrayCount((JKArray *)self)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, _JKArrayCount((JKArray *)self) + 1UL]; } anObject = [anObject retain]; -#endif - _JKArrayInsertObjectAtIndex(self, anObject, objectIndex); - mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL; + _JKArrayInsertObjectAtIndex((JKArray *)self, anObject, objectIndex); + _JKArrayIncrementMutations((JKArray *)self); } - (void)removeObjectAtIndex:(NSUInteger)objectIndex { - if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } - if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)count]; } - _JKArrayRemoveObjectAtIndex(self, objectIndex); - mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL; + if(objectIndex >= _JKArrayCount((JKArray *)self)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)_JKArrayCount((JKArray *)self)]; } + _JKArrayRemoveObjectAtIndex((JKArray *)self, objectIndex); + _JKArrayIncrementMutations((JKArray *)self); } - (void)replaceObjectAtIndex:(NSUInteger)objectIndex withObject:(id)anObject { - if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } - if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } - if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)count]; } -#ifdef __clang_analyzer__ - [anObject retain]; // Stupid clang analyzer... Issue #19. -#else + if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } + if(objectIndex >= _JKArrayCount((JKArray *)self)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%ld)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)_JKArrayCount((JKArray *)self)]; } anObject = [anObject retain]; -#endif - _JKArrayReplaceObjectAtIndexWithObject(self, objectIndex, anObject); - mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL; + _JKArrayReplaceObjectAtIndexWithObject((JKArray *)self, objectIndex, anObject); + _JKArrayIncrementMutations((JKArray *)self); } - (id)copyWithZone:(NSZone *)zone { - NSParameterAssert((objects != NULL) && (count <= capacity)); - return((mutations == 0UL) ? [self retain] : [(NSArray *)[NSArray allocWithZone:zone] initWithObjects:objects count:count]); + NSUInteger arrayCount = [self count]; + if(arrayCount == 0UL) { return([[NSArray allocWithZone:zone] init]); } + id stackObjects[arrayCount]; + [self getObjects:stackObjects range:NSMakeRange(0UL, arrayCount)]; + return([[NSArray allocWithZone:zone] initWithObjects:stackObjects count:arrayCount]); } - (id)mutableCopyWithZone:(NSZone *)zone { - NSParameterAssert((objects != NULL) && (count <= capacity)); - return([(NSMutableArray *)[NSMutableArray allocWithZone:zone] initWithObjects:objects count:count]); + NSUInteger arrayCount = [self count]; + if(arrayCount == 0UL) { return([[NSMutableArray allocWithZone:zone] init]); } + id stackObjects[arrayCount]; + [self getObjects:stackObjects range:NSMakeRange(0UL, arrayCount)]; + return([[NSMutableArray allocWithZone:zone] initWithObjects:stackObjects count:arrayCount]); } @end @@ -850,7 +844,7 @@ - (void)dealloc - (NSArray *)allObjects { NSParameterAssert(collection != NULL); - NSUInteger count = [(NSDictionary *)collection count], atObject = 0UL; + NSUInteger count = [collection count], atObject = 0UL; id objects[count]; while((objects[atObject] = [self nextObject]) != NULL) { NSParameterAssert(atObject < count); atObject++; } @@ -873,7 +867,7 @@ - (id)nextObject @end #pragma mark - -@interface JKDictionary : NSMutableDictionary { +@interface JKDictionary : NSDictionary { NSUInteger count, capacity, mutations; JKHashTableEntry *entry; } @@ -881,6 +875,21 @@ @interface JKDictionary : NSMutableDictionary bottom) { mid = (top + bottom) / 2UL; if(jk_dictionaryCapacities[mid] < tableSize) { bottom = mid + 1UL; } else { top = mid; } } return(jk_dictionaryCapacities[bottom]); } @@ -927,8 +936,8 @@ static void _JKDictionaryResizeIfNeccessary(JKDictionary *dictionary) { static JKDictionary *_JKDictionaryCreate(id *keys, NSUInteger *keyHashes, id *objects, NSUInteger count, BOOL mutableCollection) { NSCParameterAssert((keys != NULL) && (keyHashes != NULL) && (objects != NULL) && (_JKDictionaryClass != NULL) && (_JKDictionaryInstanceSize > 0UL)); JKDictionary *dictionary = NULL; - if(JK_EXPECT_T((dictionary = (JKDictionary *)calloc(1UL, _JKDictionaryInstanceSize)) != NULL)) { // Directly allocate the JKDictionary instance via calloc. - dictionary->isa = _JKDictionaryClass; + if(JK_EXPECT_T((dictionary = (JKDictionary *)calloc(1UL, _JKDictionaryInstanceSize)) != NULL)) { // Directly allocate the JKArray instance via calloc. + object_setClass(dictionary, (mutableCollection == NO) ? _JKDictionaryClass : _JKMutableDictionaryClass); if((dictionary = [dictionary init]) == NULL) { return(NULL); } dictionary->capacity = _JKDictionaryCapacityForCount(count); dictionary->count = 0UL; @@ -937,8 +946,6 @@ static void _JKDictionaryResizeIfNeccessary(JKDictionary *dictionary) { NSUInteger idx = 0UL; for(idx = 0UL; idx < count; idx++) { _JKDictionaryAddObject(dictionary, keyHashes[idx], keys[idx], objects[idx]); } - - dictionary->mutations = (mutableCollection == NO) ? 0UL : 1UL; } return(dictionary); } @@ -968,31 +975,22 @@ static NSUInteger _JKDictionaryCapacity(JKDictionary *dictionary) { return(dictionary->capacity); } +static NSUInteger _JKDictionaryCount(JKDictionary *dictionary) { + NSCParameterAssert(dictionary != NULL); + return(dictionary->count); +} + +static void _JKDictionaryIncrementMutations(JKDictionary *dictionary) { + NSCParameterAssert(dictionary != NULL); + if(++dictionary->mutations == 0UL) { dictionary->mutations = 1UL; } +} + static void _JKDictionaryRemoveObjectWithEntry(JKDictionary *dictionary, JKHashTableEntry *entry) { - NSCParameterAssert((dictionary != NULL) && (entry != NULL) && (entry->key != NULL) && (entry->object != NULL) && (dictionary->count > 0UL) && (dictionary->count <= dictionary->capacity)); + NSCParameterAssert((dictionary != NULL) && (entry != NULL) && (entry->key != NULL) && (entry->object != NULL) && (dictionary->count > 0UL)); CFRelease(entry->key); entry->key = NULL; CFRelease(entry->object); entry->object = NULL; entry->keyHash = 0UL; dictionary->count--; - // In order for certain invariants that are used to speed up the search for a particular key, we need to "re-add" all the entries in the hash table following this entry until we hit a NULL entry. - NSUInteger removeIdx = entry - dictionary->entry, idx = 0UL; - NSCParameterAssert((removeIdx < dictionary->capacity)); - for(idx = 0UL; idx < dictionary->capacity; idx++) { - NSUInteger entryIdx = (removeIdx + idx + 1UL) % dictionary->capacity; - JKHashTableEntry *atEntry = &dictionary->entry[entryIdx]; - if(atEntry->key == NULL) { break; } - NSUInteger keyHash = atEntry->keyHash; - id key = atEntry->key, object = atEntry->object; - NSCParameterAssert(object != NULL); - atEntry->keyHash = 0UL; - atEntry->key = NULL; - atEntry->object = NULL; - NSUInteger addKeyEntry = keyHash % dictionary->capacity, addIdx = 0UL; - for(addIdx = 0UL; addIdx < dictionary->capacity; addIdx++) { - JKHashTableEntry *atAddEntry = &dictionary->entry[((addKeyEntry + addIdx) % dictionary->capacity)]; - if(JK_EXPECT_T(atAddEntry->key == NULL)) { NSCParameterAssert((atAddEntry->keyHash == 0UL) && (atAddEntry->object == NULL)); atAddEntry->key = key; atAddEntry->object = object; atAddEntry->keyHash = keyHash; break; } - } - } } static void _JKDictionaryAddObject(JKDictionary *dictionary, NSUInteger keyHash, id key, id object) { @@ -1002,7 +1000,7 @@ static void _JKDictionaryAddObject(JKDictionary *dictionary, NSUInteger keyHash, NSUInteger entryIdx = (keyEntry + idx) % dictionary->capacity; JKHashTableEntry *atEntry = &dictionary->entry[entryIdx]; if(JK_EXPECT_F(atEntry->keyHash == keyHash) && JK_EXPECT_T(atEntry->key != NULL) && (JK_EXPECT_F(key == atEntry->key) || JK_EXPECT_F(CFEqual(atEntry->key, key)))) { _JKDictionaryRemoveObjectWithEntry(dictionary, atEntry); } - if(JK_EXPECT_T(atEntry->key == NULL)) { NSCParameterAssert((atEntry->keyHash == 0UL) && (atEntry->object == NULL)); atEntry->key = key; atEntry->object = object; atEntry->keyHash = keyHash; dictionary->count++; return; } + if(JK_EXPECT_T(atEntry->key == NULL)) { atEntry->key = key; atEntry->object = object; atEntry->keyHash = keyHash; dictionary->count++; return; } } // We should never get here. If we do, we -release the key / object because it's our responsibility. @@ -1017,7 +1015,7 @@ - (NSUInteger)count static JKHashTableEntry *_JKDictionaryHashTableEntryForKey(JKDictionary *dictionary, id aKey) { NSCParameterAssert((dictionary != NULL) && (dictionary->entry != NULL) && (dictionary->count <= dictionary->capacity)); - if((aKey == NULL) || (dictionary->capacity == 0UL)) { return(NULL); } + if(aKey == NULL) { return(NULL); } NSUInteger keyHash = CFHash(aKey), keyEntry = (keyHash % dictionary->capacity), idx = 0UL; JKHashTableEntry *atEntry = NULL; for(idx = 0UL; idx < dictionary->capacity; idx++) { @@ -1031,22 +1029,29 @@ - (NSUInteger)count - (id)objectForKey:(id)aKey { NSParameterAssert((entry != NULL) && (count <= capacity)); - JKHashTableEntry *entryForKey = _JKDictionaryHashTableEntryForKey(self, aKey); - return((entryForKey != NULL) ? entryForKey->object : NULL); + JKHashTableEntry *atEntry = _JKDictionaryHashTableEntryForKey(self, aKey); + return((atEntry != NULL) ? atEntry->object : NULL); } -- (void)getObjects:(id *)objects andKeys:(id *)keys -{ - NSParameterAssert((entry != NULL) && (count <= capacity)); +static NSUInteger _JKDictionaryGetKeysAndObjects(JKDictionary *dictionary, NSUInteger arrayLength, id keys[arrayLength], id objects[arrayLength]) { + NSCParameterAssert((dictionary != NULL) && (dictionary->entry != NULL) && (dictionary->count <= dictionary->capacity)); NSUInteger atEntry = 0UL; NSUInteger arrayIdx = 0UL; - for(atEntry = 0UL; atEntry < capacity; atEntry++) { - if(JK_EXPECT_T(entry[atEntry].key != NULL)) { - NSCParameterAssert((entry[atEntry].object != NULL) && (arrayIdx < count)); - if(JK_EXPECT_T(keys != NULL)) { keys[arrayIdx] = entry[atEntry].key; } - if(JK_EXPECT_T(objects != NULL)) { objects[arrayIdx] = entry[atEntry].object; } + for(atEntry = 0UL; (atEntry < dictionary->capacity) && (arrayIdx < arrayLength); atEntry++) { + if(JK_EXPECT_T(dictionary->entry[atEntry].key != NULL)) { + NSCParameterAssert((dictionary->entry[atEntry].object != NULL) && (arrayIdx < dictionary->count)); + if(JK_EXPECT_T(keys != NULL)) { keys[arrayIdx] = dictionary->entry[atEntry].key; } + if(JK_EXPECT_T(objects != NULL)) { objects[arrayIdx] = dictionary->entry[atEntry].object; } arrayIdx++; } } + NSCParameterAssert(arrayIdx == dictionary->count); + return(arrayIdx); +} + +- (void)getObjects:(id *)objects andKeys:(id *)keys +{ + NSParameterAssert((entry != NULL) && (count <= capacity)); + _JKDictionaryGetKeysAndObjects(self, count, keys, objects); } - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len @@ -1066,42 +1071,91 @@ - (NSEnumerator *)keyEnumerator return([[[JKDictionaryEnumerator alloc] initWithJKDictionary:self] autorelease]); } +- (id)copyWithZone:(NSZone *)zone +{ +#pragma unused(zone) + NSParameterAssert((entry != NULL) && (count <= capacity)); + return([self retain]); +} + +- (id)mutableCopyWithZone:(NSZone *)zone +{ + NSParameterAssert((entry != NULL) && (count <= capacity)); + id stackKeys[count], stackObjects[count]; + NSUInteger gotCount = _JKDictionaryGetKeysAndObjects(self, count, stackKeys, stackObjects); + NSParameterAssert(gotCount == count); + return([[NSMutableDictionary allocWithZone:zone] initWithObjects:stackObjects forKeys:stackKeys count:gotCount]); +} + +@end + + +#pragma mark - +@interface JKMutableDictionary : NSMutableDictionary +@end + +@implementation JKMutableDictionary + ++ (void)load +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Though technically not required, the run time environment at +load time may be less than ideal. + + Class JKMutableDictionaryClass = objc_getClass("JKMutableDictionary"), JKDictionaryClass = objc_getClass("JKDictionary"); + + // We swizzle the methods from JKDictionary in to this class (JKMutableDictionary). + + jk_swizzleClassMethod(JKMutableDictionaryClass, JKDictionaryClass, @selector(allocWithZone:)); + + jk_swizzleInstanceMethod(JKMutableDictionaryClass, JKDictionaryClass, @selector(dealloc)); + jk_swizzleInstanceMethod(JKMutableDictionaryClass, JKDictionaryClass, @selector(count)); + jk_swizzleInstanceMethod(JKMutableDictionaryClass, JKDictionaryClass, @selector(objectForKey:)); + jk_swizzleInstanceMethod(JKMutableDictionaryClass, JKDictionaryClass, @selector(getObjects:andKeys:)); + jk_swizzleInstanceMethod(JKMutableDictionaryClass, JKDictionaryClass, @selector(keyEnumerator)); + jk_swizzleInstanceMethod(JKMutableDictionaryClass, JKDictionaryClass, @selector(countByEnumeratingWithState:objects:count:)); + + [pool release]; pool = NULL; +} + - (void)setObject:(id)anObject forKey:(id)aKey { - if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } - if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } - if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil value (key: %@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey]; } + if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } + if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil value (key: %@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey]; } - _JKDictionaryResizeIfNeccessary(self); + _JKDictionaryResizeIfNeccessary((JKDictionary *)self); #ifndef __clang_analyzer__ aKey = [aKey copy]; // Why on earth would clang complain that this -copy "might leak", anObject = [anObject retain]; // but this -retain doesn't!? #endif // __clang_analyzer__ - _JKDictionaryAddObject(self, CFHash(aKey), aKey, anObject); - mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL; + _JKDictionaryAddObject((JKDictionary *)self, CFHash(aKey), aKey, anObject); + _JKDictionaryIncrementMutations((JKDictionary *)self); } - (void)removeObjectForKey:(id)aKey { - if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } - if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to remove nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } - JKHashTableEntry *entryForKey = _JKDictionaryHashTableEntryForKey(self, aKey); - if(entryForKey != NULL) { - _JKDictionaryRemoveObjectWithEntry(self, entryForKey); - mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL; + if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to remove nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } + JKHashTableEntry *entry = _JKDictionaryHashTableEntryForKey((JKDictionary *)self, aKey); + if(entry != NULL) { + _JKDictionaryRemoveObjectWithEntry((JKDictionary *)self, entry); + _JKDictionaryIncrementMutations((JKDictionary *)self); } } - (id)copyWithZone:(NSZone *)zone { - NSParameterAssert((entry != NULL) && (count <= capacity)); - return((mutations == 0UL) ? [self retain] : [[NSDictionary allocWithZone:zone] initWithDictionary:self]); + NSUInteger dictionaryCount = _JKDictionaryCount((JKDictionary *)self); + id stackKeys[dictionaryCount], stackObjects[dictionaryCount]; + NSUInteger gotCount = _JKDictionaryGetKeysAndObjects((JKDictionary *)self, dictionaryCount, stackKeys, stackObjects); + NSParameterAssert(gotCount == dictionaryCount); + return([[NSDictionary allocWithZone:zone] initWithObjects:stackObjects forKeys:stackKeys count:gotCount]); } - (id)mutableCopyWithZone:(NSZone *)zone { - NSParameterAssert((entry != NULL) && (count <= capacity)); - return([[NSMutableDictionary allocWithZone:zone] initWithDictionary:self]); + NSUInteger dictionaryCount = _JKDictionaryCount((JKDictionary *)self); + id stackKeys[dictionaryCount], stackObjects[dictionaryCount]; + NSUInteger gotCount = _JKDictionaryGetKeysAndObjects((JKDictionary *)self, dictionaryCount, stackKeys, stackObjects); + NSParameterAssert(gotCount == dictionaryCount); + return([[NSMutableDictionary allocWithZone:zone] initWithObjects:stackObjects forKeys:stackKeys count:gotCount]); } @end @@ -1113,8 +1167,7 @@ - (id)mutableCopyWithZone:(NSZone *)zone JK_STATIC_INLINE size_t jk_min(size_t a, size_t b) { return((a < b) ? a : b); } JK_STATIC_INLINE size_t jk_max(size_t a, size_t b) { return((a > b) ? a : b); } -JK_STATIC_INLINE JKHash jk_calculateHash(JKHash currentHash, unsigned char c) { return((((currentHash << 5) + currentHash) + (c - 29)) ^ (currentHash >> 19)); } - +JK_STATIC_INLINE JKHash calculateHash(JKHash currentHash, unsigned char c) { return(((currentHash << 5) + currentHash) + c); } static void jk_error(JKParseState *parseState, NSString *format, ...) { NSCParameterAssert((parseState != NULL) && (format != NULL)); @@ -1411,7 +1464,7 @@ JK_STATIC_INLINE int jk_string_add_unicodeCodePoint(JKParseState *parseState, ui if((result = ConvertUTF32toUTF8(unicodeCodePoint, &u8s, (parseState->token.tokenBuffer.bytes.ptr + parseState->token.tokenBuffer.bytes.length))) != conversionOK) { if(result == targetExhausted) { return(1); } } size_t utf8len = u8s - &parseState->token.tokenBuffer.bytes.ptr[*tokenBufferIdx], nextIdx = (*tokenBufferIdx) + utf8len; - while(*tokenBufferIdx < nextIdx) { *stringHash = jk_calculateHash(*stringHash, parseState->token.tokenBuffer.bytes.ptr[(*tokenBufferIdx)++]); } + while(*tokenBufferIdx < nextIdx) { *stringHash = calculateHash(*stringHash, parseState->token.tokenBuffer.bytes.ptr[(*tokenBufferIdx)++]); } return(0); } @@ -1445,8 +1498,8 @@ static int jk_parse_string(JKParseState *parseState) { ConversionResult result; if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(atStringCharacter - 1, endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { goto switchToSlowPath; } - stringHash = jk_calculateHash(stringHash, currentChar); - while(atStringCharacter < nextValidCharacter) { NSCParameterAssert(JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)); stringHash = jk_calculateHash(stringHash, *atStringCharacter++); } + stringHash = calculateHash(stringHash, currentChar); + while(atStringCharacter < nextValidCharacter) { NSCParameterAssert(JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)); stringHash = calculateHash(stringHash, *atStringCharacter++); } continue; } else { if(JK_EXPECT_F(currentChar == (unsigned long)'"')) { stringState = JSONStringStateFinished; goto finishedParsing; } @@ -1463,7 +1516,7 @@ static int jk_parse_string(JKParseState *parseState) { if(JK_EXPECT_F(currentChar < 0x20UL)) { jk_error(parseState, @"Invalid character < 0x20 found in string: 0x%2.2x.", currentChar); stringState = JSONStringStateError; goto finishedParsing; } - stringHash = jk_calculateHash(stringHash, currentChar); + stringHash = calculateHash(stringHash, currentChar); } } @@ -1481,7 +1534,7 @@ static int jk_parse_string(JKParseState *parseState) { if(JK_EXPECT_T(currentChar < (unsigned long)0x80)) { // Not a UTF8 sequence if(JK_EXPECT_F(currentChar == (unsigned long)'"')) { stringState = JSONStringStateFinished; atStringCharacter++; goto finishedParsing; } if(JK_EXPECT_F(currentChar == (unsigned long)'\\')) { stringState = JSONStringStateEscape; continue; } - stringHash = jk_calculateHash(stringHash, currentChar); + stringHash = calculateHash(stringHash, currentChar); tokenBuffer[tokenBufferIdx++] = currentChar; continue; } else { // UTF8 sequence @@ -1496,7 +1549,7 @@ static int jk_parse_string(JKParseState *parseState) { atStringCharacter = nextValidCharacter - 1; continue; } else { - while(atStringCharacter < nextValidCharacter) { tokenBuffer[tokenBufferIdx++] = *atStringCharacter; stringHash = jk_calculateHash(stringHash, *atStringCharacter++); } + while(atStringCharacter < nextValidCharacter) { tokenBuffer[tokenBufferIdx++] = *atStringCharacter; stringHash = calculateHash(stringHash, *atStringCharacter++); } atStringCharacter--; continue; } @@ -1524,7 +1577,7 @@ static int jk_parse_string(JKParseState *parseState) { parsedEscapedChar: stringState = JSONStringStateParsing; - stringHash = jk_calculateHash(stringHash, escapedChar); + stringHash = calculateHash(stringHash, escapedChar); tokenBuffer[tokenBufferIdx++] = escapedChar; break; @@ -1712,7 +1765,7 @@ static int jk_parse_number(JKParseState *parseState) { if(JK_EXPECT_F(endOfNumber != &numberTempBuf[parseState->token.tokenPtrRange.length]) && JK_EXPECT_F(numberState != JSONNumberStateError)) { numberState = JSONNumberStateError; jk_error(parseState, @"The conversion function did not consume all of the number tokens characters."); } size_t hashIndex = 0UL; - for(hashIndex = 0UL; hashIndex < parseState->token.value.ptrRange.length; hashIndex++) { parseState->token.value.hash = jk_calculateHash(parseState->token.value.hash, parseState->token.value.ptrRange.ptr[hashIndex]); } + for(hashIndex = 0UL; hashIndex < parseState->token.value.ptrRange.length; hashIndex++) { parseState->token.value.hash = calculateHash(parseState->token.value.hash, parseState->token.value.ptrRange.ptr[hashIndex]); } } if(JK_EXPECT_F(numberState != JSONNumberStateFinished)) { jk_error(parseState, @"Invalid number."); } @@ -1975,7 +2028,7 @@ static id json_parse_it(JKParseState *parseState) { #pragma mark Object cache // This uses a Galois Linear Feedback Shift Register (LFSR) PRNG to pick which item in the cache to age. It has a period of (2^32)-1. -// NOTE: A LFSR *MUST* be initialized to a non-zero value and must always have a non-zero value. The LFSR is initalized to 1 in -initWithParseOptions: +// NOTE: A LFSR *MUST* be initialized to a non-zero value and must always have a non-zero value. JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) { NSCParameterAssert((parseState != NULL) && (parseState->cache.prng_lfsr != 0U)); parseState->cache.prng_lfsr = (parseState->cache.prng_lfsr >> 1) ^ ((0U - (parseState->cache.prng_lfsr & 1U)) & 0x80200003U); @@ -1986,8 +2039,9 @@ JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) { // // The hash table is a linear C array of JKTokenCacheItem. The terms "item" and "bucket" are synonymous with the index in to the cache array, i.e. cache.items[bucket]. // -// Items in the cache have an age associated with them. An items age is incremented using saturating unsigned arithmetic and decremeted using unsigned right shifts. -// Thus, an items age is managed using an AIMD policy- additive increase, multiplicative decrease. All age calculations and manipulations are branchless. +// Items in the cache have an age associated with them. The age is the number of rightmost 1 bits, i.e. 0000 = 0, 0001 = 1, 0011 = 2, 0111 = 3, 1111 = 4. +// This allows us to use left and right shifts to add or subtract from an items age. Add = (age << 1) | 1. Subtract = age >> 0. Subtract is synonymous with "age" (i.e., age an item). +// The reason for this is it allows us to perform saturated adds and subtractions and is branchless. // The primitive C type MUST be unsigned. It is currently a "char", which allows (at a minimum and in practice) 8 bits. // // A "useable bucket" is a bucket that is not in use (never populated), or has an age == 0. @@ -2002,12 +2056,12 @@ JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) { void *parsedAtom = NULL; if(JK_EXPECT_F(parseState->token.value.ptrRange.length == 0UL) && JK_EXPECT_T(parseState->token.value.type == JKValueTypeString)) { return(@""); } - + for(x = 0UL; x < JK_CACHE_PROBES; x++) { if(JK_EXPECT_F(parseState->cache.items[bucket].object == NULL)) { setBucket = 1UL; useableBucket = bucket; break; } - if((JK_EXPECT_T(parseState->cache.items[bucket].hash == parseState->token.value.hash)) && (JK_EXPECT_T(parseState->cache.items[bucket].size == parseState->token.value.ptrRange.length)) && (JK_EXPECT_T(parseState->cache.items[bucket].type == parseState->token.value.type)) && (JK_EXPECT_T(parseState->cache.items[bucket].bytes != NULL)) && (JK_EXPECT_T(memcmp(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length) == 0U))) { - parseState->cache.age[bucket] = (((uint32_t)parseState->cache.age[bucket]) + 1U) - (((((uint32_t)parseState->cache.age[bucket]) + 1U) >> 31) ^ 1U); + if((JK_EXPECT_T(parseState->cache.items[bucket].hash == parseState->token.value.hash)) && (JK_EXPECT_T(parseState->cache.items[bucket].size == parseState->token.value.ptrRange.length)) && (JK_EXPECT_T(parseState->cache.items[bucket].type == parseState->token.value.type)) && (JK_EXPECT_T(parseState->cache.items[bucket].bytes != NULL)) && (JK_EXPECT_T(strncmp((const char *)parseState->cache.items[bucket].bytes, (const char *)parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length) == 0U))) { + parseState->cache.age[bucket] = (parseState->cache.age[bucket] << 1) | 1U; parseState->token.value.cacheItem = &parseState->cache.items[bucket]; NSCParameterAssert(parseState->cache.items[bucket].object != NULL); return((void *)CFRetain(parseState->cache.items[bucket].object)); @@ -2077,6 +2131,26 @@ JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) { #pragma mark - @implementation JSONDecoder +static Class _jk_NSNumberClass; +static NSNumberAllocImp _jk_NSNumberAllocImp; +static NSNumberInitWithUnsignedLongLongImp _jk_NSNumberInitWithUnsignedLongLongImp; + ++ (void)load +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Though technically not required, the run time environment at +load time may be less than ideal. + + _jk_NSNumberClass = [NSNumber class]; + _jk_NSNumberAllocImp = (NSNumberAllocImp)[NSNumber methodForSelector:@selector(alloc)]; + + // Hacktacular. Need to do it this way due to the nature of class clusters. + id temp_NSNumber = [NSNumber alloc]; + _jk_NSNumberInitWithUnsignedLongLongImp = (NSNumberInitWithUnsignedLongLongImp)[temp_NSNumber methodForSelector:@selector(initWithUnsignedLongLong:)]; + [[temp_NSNumber init] release]; + temp_NSNumber = NULL; + + [pool release]; pool = NULL; +} + + (id)decoder { return([self decoderWithParseOptions:JKParseOptionStrict]); @@ -2530,7 +2604,7 @@ JK_STATIC_INLINE JKHash jk_encode_object_hash(void *objectPtr) { static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *objectPtr) { NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (objectPtr != NULL)); - id object = (id)objectPtr, encodeCacheObject = object; + id object = (id)objectPtr; int isClass = JKClassUnknown; size_t startingAtIndex = encodeState->atIndex; @@ -2538,15 +2612,13 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object JKEncodeCache *cacheSlot = &encodeState->cache[objectHash % JK_ENCODE_CACHE_SLOTS]; if(JK_EXPECT_T(cacheSlot->object == object)) { - NSCParameterAssert((cacheSlot->object != NULL) && - (cacheSlot->offset < encodeState->atIndex) && ((cacheSlot->offset + cacheSlot->length) < encodeState->atIndex) && + NSCParameterAssert((cacheSlot->offset < encodeState->atIndex) && ((cacheSlot->offset + cacheSlot->length) < encodeState->atIndex) && (cacheSlot->offset < encodeState->stringBuffer.bytes.length) && ((cacheSlot->offset + cacheSlot->length) < encodeState->stringBuffer.bytes.length) && ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) && ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) && ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length))); if(JK_EXPECT_F(((encodeState->atIndex + cacheSlot->length + 256UL) > encodeState->stringBuffer.bytes.length)) && JK_EXPECT_F((jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + cacheSlot->length + 1024UL) == NULL))) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); } - NSCParameterAssert(((encodeState->atIndex + cacheSlot->length) < encodeState->stringBuffer.bytes.length) && - ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) && + NSCParameterAssert(((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) && ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) && ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) && ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) && @@ -2556,77 +2628,24 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object return(0); } - // When we encounter a class that we do not handle, and we have either a delegate or block that the user supplied to format unsupported classes, - // we "re-run" the object check. However, we re-run the object check exactly ONCE. If the user supplies an object that isn't one of the - // supported classes, we fail the second time (i.e., double fault error). - BOOL rerunningAfterClassFormatter = NO; - rerunAfterClassFormatter:; - - // XXX XXX XXX XXX - // - // We need to work around a bug in 10.7, which breaks ABI compatibility with Objective-C going back not just to 10.0, but OpenStep and even NextStep. - // - // It has long been documented that "the very first thing that a pointer to an Objective-C object "points to" is a pointer to that objects class". - // - // This is euphemistically called "tagged pointers". There are a number of highly technical problems with this, most involving long passages from - // the C standard(s). In short, one can make a strong case, couched from the perspective of the C standard(s), that that 10.7 "tagged pointers" are - // fundamentally Wrong and Broken, and should have never been implemented. Assuming those points are glossed over, because the change is very clearly - // breaking ABI compatibility, this should have resulted in a minimum of a "minimum version required" bump in various shared libraries to prevent - // causes code that used to work just fine to suddenly break without warning. - // - // In fact, the C standard says that the hack below is "undefined behavior"- there is no requirement that the 10.7 tagged pointer hack of setting the - // "lower, unused bits" must be preserved when casting the result to an integer type, but this "works" because for most architectures - // `sizeof(long) == sizeof(void *)` and the compiler uses the same representation for both. (note: this is informal, not meant to be - // normative or pedantically correct). - // - // In other words, while this "works" for now, technically the compiler is not obligated to do "what we want", and a later version of the compiler - // is not required in any way to produce the same results or behavior that earlier versions of the compiler did for the statement below. - // - // Fan-fucking-tastic. - // - // Why not just use `object_getClass()`? Because `object->isa` reduces to (typically) a *single* instruction. Calling `object_getClass()` requires - // that the compiler potentially spill registers, establish a function call frame / environment, and finally execute a "jump subroutine" instruction. - // Then, the called subroutine must spend half a dozen instructions in its prolog, however many instructions doing whatever it does, then half a dozen - // instructions in its prolog. One instruction compared to dozens, maybe a hundred instructions. - // - // Yes, that's one to two orders of magnitude difference. Which is compelling in its own right. When going for performance, you're often happy with - // gains in the two to three percent range. - // - // XXX XXX XXX XXX - - - BOOL workAroundMacOSXABIBreakingBug = (JK_EXPECT_F(((NSUInteger)object) & 0x1)) ? YES : NO; - void *objectISA = (JK_EXPECT_F(workAroundMacOSXABIBreakingBug)) ? NULL : *((void **)objectPtr); - if(JK_EXPECT_F(workAroundMacOSXABIBreakingBug)) { goto slowClassLookup; } - - if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.stringClass)) { isClass = JKClassString; } - else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.numberClass)) { isClass = JKClassNumber; } - else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.dictionaryClass)) { isClass = JKClassDictionary; } - else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.arrayClass)) { isClass = JKClassArray; } - else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.nullClass)) { isClass = JKClassNull; } + if(JK_EXPECT_T(object_getClass(object) == encodeState->fastClassLookup.stringClass)) { isClass = JKClassString; } + else if(JK_EXPECT_T(object_getClass(object) == encodeState->fastClassLookup.numberClass)) { isClass = JKClassNumber; } + else if(JK_EXPECT_T(object_getClass(object) == encodeState->fastClassLookup.dictionaryClass)) { isClass = JKClassDictionary; } + else if(JK_EXPECT_T(object_getClass(object) == encodeState->fastClassLookup.arrayClass)) { isClass = JKClassArray; } + else if(JK_EXPECT_T(object_getClass(object) == encodeState->fastClassLookup.nullClass)) { isClass = JKClassNull; } else { - slowClassLookup: - if(JK_EXPECT_T([object isKindOfClass:[NSString class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.stringClass = objectISA; } isClass = JKClassString; } - else if(JK_EXPECT_T([object isKindOfClass:[NSNumber class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.numberClass = objectISA; } isClass = JKClassNumber; } - else if(JK_EXPECT_T([object isKindOfClass:[NSDictionary class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.dictionaryClass = objectISA; } isClass = JKClassDictionary; } - else if(JK_EXPECT_T([object isKindOfClass:[NSArray class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.arrayClass = objectISA; } isClass = JKClassArray; } - else if(JK_EXPECT_T([object isKindOfClass:[NSNull class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.nullClass = objectISA; } isClass = JKClassNull; } - else { - if((rerunningAfterClassFormatter == NO) && ( -#ifdef __BLOCKS__ - ((encodeState->classFormatterBlock) && ((object = encodeState->classFormatterBlock(object)) != NULL)) || -#endif - ((encodeState->classFormatterIMP) && ((object = encodeState->classFormatterIMP(encodeState->classFormatterDelegate, encodeState->classFormatterSelector, object)) != NULL)) )) { rerunningAfterClassFormatter = YES; goto rerunAfterClassFormatter; } - - if(rerunningAfterClassFormatter == NO) { jk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([encodeCacheObject class])); return(1); } - else { jk_encode_error(encodeState, @"Unable to serialize object class %@ that was returned by the unsupported class formatter. Original object class was %@.", (object == NULL) ? @"NULL" : NSStringFromClass([object class]), NSStringFromClass([encodeCacheObject class])); return(1); } - } + if(JK_EXPECT_T([object isKindOfClass:[NSString class]])) { encodeState->fastClassLookup.stringClass = object_getClass(object); isClass = JKClassString; } + else if(JK_EXPECT_T([object isKindOfClass:[NSNumber class]])) { encodeState->fastClassLookup.numberClass = object_getClass(object); isClass = JKClassNumber; } + else if(JK_EXPECT_T([object isKindOfClass:[NSDictionary class]])) { encodeState->fastClassLookup.dictionaryClass = object_getClass(object); isClass = JKClassDictionary; } + else if(JK_EXPECT_T([object isKindOfClass:[NSArray class]])) { encodeState->fastClassLookup.arrayClass = object_getClass(object); isClass = JKClassArray; } + else if(JK_EXPECT_T([object isKindOfClass:[NSNull class]])) { encodeState->fastClassLookup.nullClass = object_getClass(object); isClass = JKClassNull; } + else { jk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([object class])); return(1); } } - // This is here for the benefit of the optimizer. It allows the optimizer to do loop invariant code motion for the JKClassArray - // and JKClassDictionary cases when printing simple, single characters via jk_encode_write(), which is actually a macro: - // #define jk_encode_write1(es, dc, f) (_jk_encode_prettyPrint ? jk_encode_write1slow(es, dc, f) : jk_encode_write1fast(es, dc, f)) - int _jk_encode_prettyPrint = JK_EXPECT_T((encodeState->serializeOptionFlags & JKSerializeOptionPretty) == 0) ? 0 : 1; + // This is here for the benefit of the optimizer. It allows the optimizer to do loop invariant code motion for the JKClassArray + // and JKClassDictionary cases when printing simple, single characters via jk_encode_write(), which is actually a macro: + // #define jk_encode_write1(es, dc, f) (_jk_encode_useWrite1Fast ? jk_encode_write1fast(es, dc, f) : jk_encode_write1slow(es, dc, f)) + int _jk_encode_useWrite1Fast = JK_EXPECT_T((encodeState->serializeOptionFlags & JKSerializeOptionPretty) == 0) ? 1 : 0; switch(isClass) { case JKClassString: @@ -2655,13 +2674,13 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object default: if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break; } } else { - if(JK_EXPECT_F(utf8String[utf8Idx] == '\"') || JK_EXPECT_F(utf8String[utf8Idx] == '\\') || (JK_EXPECT_F(encodeState->serializeOptionFlags & JKSerializeOptionEscapeForwardSlashes) && JK_EXPECT_F(utf8String[utf8Idx] == '/'))) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; } + if(JK_EXPECT_F(utf8String[utf8Idx] == '\"') || JK_EXPECT_F(utf8String[utf8Idx] == '\\')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; } encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx]; } } NSCParameterAssert((encodeState->atIndex + 1UL) < encodeState->stringBuffer.bytes.length); if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; } - jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, encodeCacheObject); + jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object); return(0); } } @@ -2709,14 +2728,14 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object else { if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x\\u%4.4x", (0xd7c0U + (u32ch >> 10)), (0xdc00U + (u32ch & 0x3ffU))))) { return(1); } } } } else { - if(JK_EXPECT_F(utf8String[utf8Idx] == '\"') || JK_EXPECT_F(utf8String[utf8Idx] == '\\') || (JK_EXPECT_F(encodeState->serializeOptionFlags & JKSerializeOptionEscapeForwardSlashes) && JK_EXPECT_F(utf8String[utf8Idx] == '/'))) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; } + if(JK_EXPECT_F(utf8String[utf8Idx] == '\"') || JK_EXPECT_F(utf8String[utf8Idx] == '\\')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; } encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx]; } } } NSCParameterAssert((encodeState->atIndex + 1UL) < encodeState->stringBuffer.bytes.length); if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; } - jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, encodeCacheObject); + jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object); return(0); } } @@ -2724,8 +2743,8 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object case JKClassNumber: { - if(object == (id)kCFBooleanTrue) { return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "true", 4UL)); } - else if(object == (id)kCFBooleanFalse) { return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "false", 5UL)); } + if(object == (id)kCFBooleanTrue) { return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, object, "true", 4UL)); } + else if(object == (id)kCFBooleanFalse) { return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, object, "false", 5UL)); } const char *objCType = [object objCType]; char anum[256], *aptr = &anum[255]; @@ -2748,7 +2767,7 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object if(JK_EXPECT_F(ullv < 10ULL)) { *--aptr = ullv + '0'; } else { while(JK_EXPECT_T(ullv > 0ULL)) { *--aptr = (ullv % 10ULL) + '0'; ullv /= 10ULL; NSCParameterAssert(aptr > anum); } } if(isNegative) { *--aptr = '-'; } NSCParameterAssert(aptr > anum); - return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, aptr, &anum[255] - aptr)); + return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, object, aptr, &anum[255] - aptr)); } else { jk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); } break; case 'f': case 'd': @@ -2756,7 +2775,7 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object double dv; if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberDoubleType, &dv))) { if(JK_EXPECT_F(!isfinite(dv))) { jk_encode_error(encodeState, @"Floating point values must be finite. JSON does not support NaN or Infinity."); return(1); } - return(jk_encode_printf(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "%.17g", dv)); + return(jk_encode_printf(encodeState, cacheSlot, startingAtIndex, object, "%.17g", dv)); } else { jk_encode_error(encodeState, @"Unable to get floating point value from number object."); return(1); } } break; @@ -2785,15 +2804,13 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object { int printComma = 0; CFIndex dictionaryCount = CFDictionaryGetCount((CFDictionaryRef)object), idx = 0L; - id enumerateObject = JK_EXPECT_F(_jk_encode_prettyPrint) ? [[object allKeys] sortedArrayUsingSelector:@selector(compare:)] : object; if(JK_EXPECT_F(jk_encode_write1(encodeState, 1L, "{"))) { return(1); } - if(JK_EXPECT_F(_jk_encode_prettyPrint) || JK_EXPECT_F(dictionaryCount > 1020L)) { - for(id keyObject in enumerateObject) { + if(JK_EXPECT_F(dictionaryCount > 1020L)) { + for(id keyObject in object) { if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } } printComma = 1; - void *keyObjectISA = *((void **)keyObject); - if(JK_EXPECT_F((keyObjectISA != encodeState->fastClassLookup.stringClass)) && JK_EXPECT_F(([keyObject isKindOfClass:[NSString class]] == NO))) { jk_encode_error(encodeState, @"Key must be a string object."); return(1); } + if(JK_EXPECT_F((object_getClass(keyObject) != encodeState->fastClassLookup.stringClass)) && JK_EXPECT_F(([keyObject isKindOfClass:[NSString class]] == NO))) { jk_encode_error(encodeState, @"Key must be a string object."); return(1); } if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, keyObject))) { return(1); } if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ":"))) { return(1); } if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, (void *)CFDictionaryGetValue((CFDictionaryRef)object, keyObject)))) { return(1); } @@ -2804,8 +2821,7 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object for(idx = 0L; idx < dictionaryCount; idx++) { if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } } printComma = 1; - void *keyObjectISA = *((void **)keys[idx]); - if(JK_EXPECT_F(keyObjectISA != encodeState->fastClassLookup.stringClass) && JK_EXPECT_F([(id)keys[idx] isKindOfClass:[NSString class]] == NO)) { jk_encode_error(encodeState, @"Key must be a string object."); return(1); } + if(JK_EXPECT_F(object_getClass(((id)keys[idx])) != encodeState->fastClassLookup.stringClass) && JK_EXPECT_F([(id)keys[idx] isKindOfClass:[NSString class]] == NO)) { jk_encode_error(encodeState, @"Key must be a string object."); return(1); } if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, keys[idx]))) { return(1); } if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ":"))) { return(1); } if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, objects[idx]))) { return(1); } @@ -2815,7 +2831,7 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object } break; - case JKClassNull: return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "null", 4UL)); break; + case JKClassNull: return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, object, "null", 4UL)); break; default: jk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([object class])); return(1); break; } @@ -2826,44 +2842,29 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object @implementation JKSerializer -+ (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error ++ (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption error:(NSError **)error { - return([[[[self alloc] init] autorelease] serializeObject:object options:optionFlags encodeOption:encodeOption block:block delegate:delegate selector:selector error:error]); + return([[[[self alloc] init] autorelease] serializeObject:object options:optionFlags encodeOption:encodeOption error:error]); } -- (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error +- (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption error:(NSError **)error { -#ifndef __BLOCKS__ -#pragma unused(block) -#endif - NSParameterAssert((object != NULL) && (encodeState == NULL) && ((delegate != NULL) ? (block == NULL) : 1) && ((block != NULL) ? (delegate == NULL) : 1) && + NSParameterAssert((object != NULL) && (encodeState == NULL) && (((encodeOption & JKEncodeOptionCollectionObj) != 0UL) ? (((encodeOption & JKEncodeOptionStringObj) == 0UL) && ((encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) : 1) && (((encodeOption & JKEncodeOptionStringObj) != 0UL) ? ((encodeOption & JKEncodeOptionCollectionObj) == 0UL) : 1)); id returnObject = NULL; if(encodeState != NULL) { [self releaseState]; } - if((encodeState = (struct JKEncodeState *)calloc(1UL, sizeof(JKEncodeState))) == NULL) { [NSException raise:NSMallocException format:@"Unable to allocate state structure."]; return(NULL); } + if((encodeState = (struct JKEncodeState *)calloc(1UL, sizeof(JKEncodeState))) == NULL) { [NSException exceptionWithName:NSMallocException reason:@"Unable to allocate state structure." userInfo:NULL]; } if((error != NULL) && (*error != NULL)) { *error = NULL; } - if(delegate != NULL) { - if(selector == NULL) { [NSException raise:NSInvalidArgumentException format:@"The delegate argument is not NULL, but the selector argument is NULL."]; } - if([delegate respondsToSelector:selector] == NO) { [NSException raise:NSInvalidArgumentException format:@"The serializeUnsupportedClassesUsingDelegate: delegate does not respond to the selector argument."]; } - encodeState->classFormatterDelegate = delegate; - encodeState->classFormatterSelector = selector; - encodeState->classFormatterIMP = (JKClassFormatterIMP)[delegate methodForSelector:selector]; - NSCParameterAssert(encodeState->classFormatterIMP != NULL); - } - -#ifdef __BLOCKS__ - encodeState->classFormatterBlock = block; -#endif encodeState->serializeOptionFlags = optionFlags; encodeState->encodeOption = encodeOption; encodeState->stringBuffer.roundSizeUpToMultipleOf = (1024UL * 32UL); encodeState->utf8ConversionBuffer.roundSizeUpToMultipleOf = 4096UL; - + unsigned char stackJSONBuffer[JK_JSONBUFFER_SIZE] JK_ALIGNED(64); jk_managedBuffer_setToStackBuffer(&encodeState->stringBuffer, stackJSONBuffer, sizeof(stackJSONBuffer)); @@ -2928,8 +2929,6 @@ @implementation NSString (JSONKitSerializing) // Useful for those who need to serialize just a NSString. Otherwise you would have to do something like [NSArray arrayWithObject:stringToBeJSONSerialized], serializing the array, and then chopping of the extra ^\[.*\]$ square brackets. -// NSData returning methods... - - (NSData *)JSONData { return([self JSONDataWithOptions:JKSerializeOptionNone includeQuotes:YES error:NULL]); @@ -2937,11 +2936,9 @@ - (NSData *)JSONData - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error { - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | ((includeQuotes == NO) ? JKEncodeOptionStringObjTrimQuotes : 0UL) | JKEncodeOptionStringObj) block:NULL delegate:NULL selector:NULL error:error]); + return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | ((includeQuotes == NO) ? JKEncodeOptionStringObjTrimQuotes : 0UL) | JKEncodeOptionStringObj) error:error]); } -// NSString returning methods... - - (NSString *)JSONString { return([self JSONStringWithOptions:JKSerializeOptionNone includeQuotes:YES error:NULL]); @@ -2949,117 +2946,55 @@ - (NSString *)JSONString - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error { - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | ((includeQuotes == NO) ? JKEncodeOptionStringObjTrimQuotes : 0UL) | JKEncodeOptionStringObj) block:NULL delegate:NULL selector:NULL error:error]); + return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | ((includeQuotes == NO) ? JKEncodeOptionStringObjTrimQuotes : 0UL) | JKEncodeOptionStringObj) error:error]); } @end @implementation NSArray (JSONKitSerializing) -// NSData returning methods... - - (NSData *)JSONData { - return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]); + return([self JSONDataWithOptions:JKSerializeOptionNone error:NULL]); } - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error { - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]); + return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) error:error]); } -- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error -{ - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]); -} - -// NSString returning methods... - - (NSString *)JSONString { - return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]); + return([self JSONStringWithOptions:JKSerializeOptionNone error:NULL]); } - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error { - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]); -} - -- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error -{ - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]); + return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) error:error]); } @end @implementation NSDictionary (JSONKitSerializing) -// NSData returning methods... - - (NSData *)JSONData { - return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]); + return([self JSONDataWithOptions:JKSerializeOptionNone error:NULL]); } - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error { - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]); -} - -- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error -{ - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]); + return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) error:error]); } -// NSString returning methods... - - (NSString *)JSONString { - return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]); + return([self JSONStringWithOptions:JKSerializeOptionNone error:NULL]); } - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error { - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]); -} - -- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error -{ - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]); -} - -@end - - -#ifdef __BLOCKS__ - -@implementation NSArray (JSONKitSerializingBlockAdditions) - -- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error -{ - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]); -} - -- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error -{ - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]); + return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) error:error]); } @end - -@implementation NSDictionary (JSONKitSerializingBlockAdditions) - -- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error -{ - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]); -} - -- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error -{ - return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]); -} - -@end - -#endif // __BLOCKS__ -