From 862fa4dbd0362bad859d0fd460577cdbebc3a9a0 Mon Sep 17 00:00:00 2001 From: John Engelhart Date: Fri, 22 Apr 2011 02:14:32 -0400 Subject: [PATCH 1/3] Fixes a bug when removing items from a JKDictionary. Since JKDictionary is implemented using a hash table that uses linear probing, the removal function needs to "re-add" items that follow the removed item so that linear probe hash collisions are not "lost". Closes #17 --- JSONKit.m | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/JSONKit.m b/JSONKit.m index e7bef74..cf32dd3 100644 --- a/JSONKit.m +++ b/JSONKit.m @@ -937,11 +937,30 @@ static NSUInteger _JKDictionaryCapacity(JKDictionary *dictionary) { } static void _JKDictionaryRemoveObjectWithEntry(JKDictionary *dictionary, JKHashTableEntry *entry) { - NSCParameterAssert((dictionary != NULL) && (entry != NULL) && (entry->key != NULL) && (entry->object != NULL) && (dictionary->count > 0UL)); + NSCParameterAssert((dictionary != NULL) && (entry != NULL) && (entry->key != NULL) && (entry->object != NULL) && (dictionary->count > 0UL) && (dictionary->count <= dictionary->capacity)); 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) { @@ -951,7 +970,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)) { atEntry->key = key; atEntry->object = object; atEntry->keyHash = keyHash; dictionary->count++; return; } + 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; } } // We should never get here. If we do, we -release the key / object because it's our responsibility. From e1cb174753ae0a5e4a549350c6f697a912216437 Mon Sep 17 00:00:00 2001 From: John Engelhart Date: Mon, 25 Apr 2011 16:14:55 -0400 Subject: [PATCH 2/3] Change a NSException from exceptionWithName: to raise:. Closes #20. --- JSONKit.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JSONKit.m b/JSONKit.m index cf32dd3..4b5b4dd 100644 --- a/JSONKit.m +++ b/JSONKit.m @@ -2790,7 +2790,7 @@ - (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags enc id returnObject = NULL; if(encodeState != NULL) { [self releaseState]; } - if((encodeState = (struct JKEncodeState *)calloc(1UL, sizeof(JKEncodeState))) == NULL) { [NSException exceptionWithName:NSMallocException reason:@"Unable to allocate state structure." userInfo:NULL]; return(NULL); } + if((encodeState = (struct JKEncodeState *)calloc(1UL, sizeof(JKEncodeState))) == NULL) { [NSException raise:NSMallocException format:@"Unable to allocate state structure."]; return(NULL); } if((error != NULL) && (*error != NULL)) { *error = NULL; } From b8359c61d82416fb8db55d263f68c26d8d929d59 Mon Sep 17 00:00:00 2001 From: John Engelhart Date: Mon, 2 May 2011 15:11:41 -0400 Subject: [PATCH 3/3] Adds a serializing option to backslash escape forward slashes. This is for issue #21. --- JSONKit.h | 9 +++++---- JSONKit.m | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/JSONKit.h b/JSONKit.h index 6e179ca..71bd0c3 100644 --- a/JSONKit.h +++ b/JSONKit.h @@ -126,10 +126,11 @@ enum { typedef JKFlags JKParseOptionFlags; enum { - JKSerializeOptionNone = 0, - JKSerializeOptionPretty = (1 << 0), - JKSerializeOptionEscapeUnicode = (1 << 1), - JKSerializeOptionValidFlags = (JKSerializeOptionPretty | JKSerializeOptionEscapeUnicode), + JKSerializeOptionNone = 0, + JKSerializeOptionPretty = (1 << 0), + JKSerializeOptionEscapeUnicode = (1 << 1), + JKSerializeOptionEscapeForwardSlashes = (1 << 4), + JKSerializeOptionValidFlags = (JKSerializeOptionPretty | JKSerializeOptionEscapeUnicode | JKSerializeOptionEscapeForwardSlashes), }; typedef JKFlags JKSerializeOptionFlags; diff --git a/JSONKit.m b/JSONKit.m index 4b5b4dd..4aba2e1 100644 --- a/JSONKit.m +++ b/JSONKit.m @@ -2604,7 +2604,7 @@ 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] == '\\')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; } + 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++] = '\\'; } encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx]; } } @@ -2658,7 +2658,7 @@ 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] == '\\')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; } + 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++] = '\\'; } encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx]; } }