Skip to content

Commit f520db7

Browse files
committed
Adds support for JKSerializeOptionPretty. Silences a clang analyzer warning.
1 parent 7054853 commit f520db7

2 files changed

Lines changed: 69 additions & 36 deletions

File tree

JSONKit.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ typedef JKFlags JKParseOptionFlags;
110110

111111
enum {
112112
JKSerializeOptionNone = 0,
113-
JKSerializeOptionPretty = (1 << 0), // Not implemented yet...
113+
JKSerializeOptionPretty = (1 << 0),
114114
JKSerializeOptionEscapeUnicode = (1 << 1),
115115
JKSerializeOptionValidFlags = (JKSerializeOptionPretty | JKSerializeOptionEscapeUnicode),
116116
};

JSONKit.m

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,6 @@ The code in isValidCodePoint() is derived from the ICU code in
306306

307307
typedef NSUInteger JKHash;
308308

309-
typedef id (*NSNumberAllocImp)(id object, SEL selector);
310-
typedef id (*NSNumberInitWithUnsignedLongLongImp)(id object, SEL selector, unsigned long long value);
311-
312309
typedef struct JKTokenCacheItem JKTokenCacheItem;
313310
typedef struct JKTokenCache JKTokenCache;
314311
typedef struct JKTokenValue JKTokenValue;
@@ -326,6 +323,10 @@ The code in isValidCodePoint() is derived from the ICU code in
326323
typedef struct JKObjCImpCache JKObjCImpCache;
327324
typedef struct JKHashTableEntry JKHashTableEntry;
328325

326+
typedef id (*NSNumberAllocImp)(id object, SEL selector);
327+
typedef id (*NSNumberInitWithUnsignedLongLongImp)(id object, SEL selector, unsigned long long value);
328+
typedef int (*_jk_encode_write1)(JKEncodeState *encodeState, ssize_t depthChange, const char *format);
329+
329330

330331
struct JKPtrRange {
331332
unsigned char *ptr;
@@ -438,6 +439,8 @@ The code in isValidCodePoint() is derived from the ICU code in
438439
JKFastClassLookup fastClassLookup;
439440
JKEncodeCache cache[JK_ENCODE_CACHE_SLOTS];
440441
JKSerializeOptionFlags serializeOptionFlags;
442+
size_t depth;
443+
_jk_encode_write1 write1;
441444
NSError *error;
442445
};
443446

@@ -545,7 +548,9 @@ The code in isValidCodePoint() is derived from the ICU code in
545548
static void jk_encode_error(JKEncodeState *encodeState, NSString *format, ...);
546549
static int jk_encode_printf(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, ...);
547550
static int jk_encode_write(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format);
548-
static int jk_encode_write1(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format);
551+
static int jk_encode_write1fast(JKEncodeState *encodeState, ssize_t depthChange JK_UNUSED_ARG, const char *format);
552+
static int jk_encode_writePrettyPrintWhiteSpace(JKEncodeState *encodeState);
553+
static int jk_encode_write1slow(JKEncodeState *encodeState, ssize_t depthChange, const char *format);
549554
static int jk_encode_writen(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, size_t length);
550555
JK_STATIC_INLINE JKHash jk_encode_object_hash(void *objectPtr);
551556
JK_STATIC_INLINE void jk_encode_updateCache(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object);
@@ -921,7 +926,7 @@ static void _JKDictionaryResizeIfNeccessary(JKDictionary *dictionary) {
921926
for(idx = 0UL; idx < oldCapacity; idx++) { if(oldEntry[idx].key != NULL) { _JKDictionaryAddObject(dictionary, oldEntry[idx].keyHash, oldEntry[idx].key, oldEntry[idx].object); oldEntry[idx].keyHash = 0UL; oldEntry[idx].key = NULL; oldEntry[idx].object = NULL; } }
922927
NSCParameterAssert((oldCount == dictionary->count));
923928
free(oldEntry); oldEntry = NULL;
924-
}
929+
}
925930
}
926931

927932
static JKDictionary *_JKDictionaryCreate(id *keys, NSUInteger *keyHashes, id *objects, NSUInteger count, BOOL mutableCollection) {
@@ -1111,8 +1116,10 @@ - (void)setObject:(id)anObject forKey:(id)aKey
11111116
if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil value (key: %@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey]; }
11121117

11131118
_JKDictionaryResizeIfNeccessary((JKDictionary *)self);
1114-
aKey = [aKey copy];
1115-
anObject = [anObject retain];
1119+
#ifndef __clang_analyzer__
1120+
aKey = [aKey copy]; // Why on earth would clang complain that this -copy "might leak",
1121+
anObject = [anObject retain]; // but this -retain doesn't!?
1122+
#endif // __clang_analyzer__
11161123
_JKDictionaryAddObject((JKDictionary *)self, CFHash(aKey), aKey, anObject);
11171124
_JKDictionaryIncrementMutations((JKDictionary *)self);
11181125
}
@@ -2556,11 +2563,38 @@ static int jk_encode_write(JKEncodeState *encodeState, JKEncodeCache *cacheSlot,
25562563
return(0);
25572564
}
25582565

2559-
static int jk_encode_write1(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format) {
2560-
NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex));
2566+
static int jk_encode_write1fast(JKEncodeState *encodeState, ssize_t depthChange JK_UNUSED_ARG, const char *format) {
2567+
NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length));
25612568
if((encodeState->atIndex + 4UL) < encodeState->stringBuffer.bytes.length) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; }
2562-
else { if(JK_EXPECT_F(jk_encode_write(encodeState, cacheSlot, startingAtIndex, object, format))) { return(1); } }
2563-
jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object);
2569+
else { return(jk_encode_write(encodeState, NULL, 0UL, NULL, format)); }
2570+
return(0);
2571+
}
2572+
2573+
static int jk_encode_writePrettyPrintWhiteSpace(JKEncodeState *encodeState) {
2574+
NSCParameterAssert((encodeState != NULL) && ((encodeState->serializeOptionFlags & JKSerializeOptionPretty) != 0UL));
2575+
if(JK_EXPECT_F((encodeState->atIndex + (encodeState->depth * 2UL) + 16UL) > encodeState->stringBuffer.bytes.length) && JK_EXPECT_T(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (encodeState->depth * 2UL) + 4096UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
2576+
encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\n';
2577+
size_t depthWhiteSpace = 0UL;
2578+
for(depthWhiteSpace = 0UL; depthWhiteSpace < (encodeState->depth * 2UL); depthWhiteSpace++) { NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length); encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = ' '; }
2579+
return(0);
2580+
}
2581+
2582+
static int jk_encode_write1slow(JKEncodeState *encodeState, ssize_t depthChange, const char *format) {
2583+
NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (format != NULL) && ((depthChange >= -1L) && (depthChange <= 1L)));
2584+
if(JK_EXPECT_T((encodeState->serializeOptionFlags & JKSerializeOptionPretty) == 0UL)) {
2585+
if(JK_EXPECT_T((encodeState->atIndex + 4UL) < encodeState->stringBuffer.bytes.length)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; return(0); }
2586+
else { return(jk_encode_write(encodeState, NULL, 0UL, NULL, format)); }
2587+
} else {
2588+
if(JK_EXPECT_F((encodeState->atIndex + (encodeState->depth * 2UL) + 16UL) > encodeState->stringBuffer.bytes.length) && JK_EXPECT_F(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (encodeState->depth * 2UL) + 4096UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
2589+
encodeState->depth += depthChange;
2590+
if(format[0] == ':') { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = ' '; }
2591+
else {
2592+
if(depthChange == -1L) { if(jk_encode_writePrettyPrintWhiteSpace(encodeState)) { return(1); } }
2593+
encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0];
2594+
if(depthChange != -1L) { if(jk_encode_writePrettyPrintWhiteSpace(encodeState)) { return(1); } }
2595+
}
2596+
NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
2597+
}
25642598
return(0);
25652599
}
25662600

@@ -2588,8 +2622,7 @@ JK_STATIC_INLINE void jk_encode_updateCache(JKEncodeState *encodeState, JKEncode
25882622
}
25892623

25902624
static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *objectPtr) {
2591-
NSCParameterAssert((encodeState != NULL) && (objectPtr != NULL));
2592-
NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
2625+
NSCParameterAssert((encodeState != NULL) && (encodeState->write1 != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (objectPtr != NULL));
25932626

25942627
id object = (id)objectPtr;
25952628
int isClass = JKClassUnknown;
@@ -2653,7 +2686,7 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object
26532686
case '\n': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'n'; break;
26542687
case '\r': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'r'; break;
26552688
case '\t': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 't'; break;
2656-
default: if(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx])) { return(1); } break;
2689+
default: if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break;
26572690
}
26582691
} else {
26592692
if(JK_EXPECT_F(utf8String[utf8Idx] == '\"') || JK_EXPECT_F(utf8String[utf8Idx] == '\\')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; }
@@ -2695,7 +2728,7 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object
26952728
case '\n': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'n'; break;
26962729
case '\r': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'r'; break;
26972730
case '\t': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 't'; break;
2698-
default: if(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx])) { return(1); } break;
2731+
default: if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break;
26992732
}
27002733
} else {
27012734
if(JK_EXPECT_F(utf8String[utf8Idx] >= 0x80U) && (encodeState->serializeOptionFlags & JKSerializeOptionEscapeUnicode)) {
@@ -2706,11 +2739,11 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object
27062739
if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(&utf8String[utf8Idx], &utf8String[usedBytes], (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { jk_encode_error(encodeState, @"Error converting UTF8."); return(1); }
27072740
else {
27082741
utf8Idx = (nextValidCharacter - utf8String) - 1UL;
2709-
if(u32ch <= 0xffffU) { if(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", u32ch)) { return(1); } }
2710-
else { if(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x\\u%4.4x", (0xd7c0U + (u32ch >> 10)), (0xdc00U + (u32ch & 0x3ffU)))) { return(1); } }
2742+
if(JK_EXPECT_T(u32ch <= 0xffffU)) { if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", u32ch))) { return(1); } }
2743+
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); } }
27112744
}
27122745
} else {
2713-
if((utf8String[utf8Idx] == '\"') || (utf8String[utf8Idx] == '\\')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; }
2746+
if(JK_EXPECT_F(utf8String[utf8Idx] == '\"') || JK_EXPECT_F(utf8String[utf8Idx] == '\\')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; }
27142747
encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx];
27152748
}
27162749
}
@@ -2735,18 +2768,17 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object
27352768
long long llv;
27362769

27372770
switch(objCType[0]) {
2738-
case 'c': case 'i': case 's': case 'l': case 'q':
2771+
case 'c': case 'i': case 's': case 'l': case 'q':
27392772
if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberLongLongType, &llv))) {
2740-
if(llv < 0LL) { llv = -llv; isNegative = 1; }
2741-
if(JK_EXPECT_F(llv < 10LL)) { *--aptr = llv + '0'; } else { while(JK_EXPECT_T(llv > 0LL)) { *--aptr = (llv % 10LL) + '0'; llv /= 10LL; NSCParameterAssert(aptr > anum); } }
2742-
if(isNegative) { *--aptr = '-'; }
2743-
NSCParameterAssert(aptr > anum);
2744-
return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, object, aptr, &anum[255] - aptr));
2773+
if(llv < 0LL) { ullv = -llv; isNegative = 1; } else { ullv = llv; isNegative = 0; }
2774+
goto convertNumber;
27452775
} else { jk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); }
27462776
break;
27472777
case 'C': case 'I': case 'S': case 'L': case 'Q': case 'B':
27482778
if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberLongLongType, &ullv))) {
2779+
convertNumber:
27492780
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); } }
2781+
if(isNegative) { *--aptr = '-'; }
27502782
NSCParameterAssert(aptr > anum);
27512783
return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, object, aptr, &anum[255] - aptr));
27522784
} else { jk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); }
@@ -2769,15 +2801,15 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object
27692801
{
27702802
int printComma = 0;
27712803
CFIndex arrayCount = CFArrayGetCount((CFArrayRef)object), idx = 0L;
2772-
if(jk_encode_write1(encodeState, NULL, 0UL, NULL, "[")) { return(1); }
2773-
if(arrayCount > 1020L) {
2774-
for(id arrayObject in object) { if(printComma) { if(jk_encode_write1(encodeState, NULL, 0UL, NULL, ",")) { return(1); } } printComma = 1; if(jk_encode_add_atom_to_buffer(encodeState, arrayObject)) { return(1); } }
2804+
if(JK_EXPECT_F(encodeState->write1(encodeState, 1L, "["))) { return(1); }
2805+
if(JK_EXPECT_F(arrayCount > 1020L)) {
2806+
for(id arrayObject in object) { if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(encodeState->write1(encodeState, 0L, ","))) { return(1); } } printComma = 1; if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, arrayObject))) { return(1); } }
27752807
} else {
27762808
void *objects[1024];
27772809
CFArrayGetValues((CFArrayRef)object, CFRangeMake(0L, arrayCount), (const void **)objects);
2778-
for(idx = 0L; idx < arrayCount; idx++) { if(printComma) { if(jk_encode_write1(encodeState, NULL, 0UL, NULL, ",")) { return(1); } } printComma = 1; if(jk_encode_add_atom_to_buffer(encodeState, objects[idx])) { return(1); } }
2810+
for(idx = 0L; idx < arrayCount; idx++) { if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(encodeState->write1(encodeState, 0L, ","))) { return(1); } } printComma = 1; if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, objects[idx]))) { return(1); } }
27792811
}
2780-
if(jk_encode_write1(encodeState, NULL, 0UL, NULL, "]")) { return(1); }
2812+
return(encodeState->write1(encodeState, -1L, "]"));
27812813
}
27822814
break;
27832815

@@ -2786,29 +2818,29 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object
27862818
int printComma = 0;
27872819
CFIndex dictionaryCount = CFDictionaryGetCount((CFDictionaryRef)object), idx = 0L;
27882820

2789-
if(JK_EXPECT_F(jk_encode_write1(encodeState, NULL, 0UL, NULL, "{"))) { return(1); }
2821+
if(JK_EXPECT_F(encodeState->write1(encodeState, 1L, "{"))) { return(1); }
27902822
if(JK_EXPECT_F(dictionaryCount > 1020L)) {
27912823
for(id keyObject in object) {
2792-
if(printComma) { if(JK_EXPECT_F(jk_encode_write1(encodeState, NULL, 0UL, NULL, ","))) { return(1); } }
2824+
if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(encodeState->write1(encodeState, 0L, ","))) { return(1); } }
27932825
printComma = 1;
27942826
if(JK_EXPECT_F((keyObject->isa != encodeState->fastClassLookup.stringClass)) && JK_EXPECT_F(([keyObject isKindOfClass:[NSString class]] == NO))) { jk_encode_error(encodeState, @"Key must be a string object."); return(1); }
27952827
if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, keyObject))) { return(1); }
2796-
if(JK_EXPECT_F(jk_encode_write1(encodeState, NULL, 0UL, NULL, ":"))) { return(1); }
2828+
if(JK_EXPECT_F(encodeState->write1(encodeState, 0L, ":"))) { return(1); }
27972829
if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, (void *)CFDictionaryGetValue((CFDictionaryRef)object, keyObject)))) { return(1); }
27982830
}
27992831
} else {
28002832
void *keys[1024], *objects[1024];
28012833
CFDictionaryGetKeysAndValues((CFDictionaryRef)object, (const void **)keys, (const void **)objects);
28022834
for(idx = 0L; idx < dictionaryCount; idx++) {
2803-
if(JK_EXPECT_F(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, NULL, 0UL, NULL, ","))) { return(1); } }
2835+
if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(encodeState->write1(encodeState, 0L, ","))) { return(1); } }
28042836
printComma = 1;
28052837
if(JK_EXPECT_F(((id)keys[idx])->isa != 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); }
28062838
if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, keys[idx]))) { return(1); }
2807-
if(JK_EXPECT_F(jk_encode_write1(encodeState, NULL, 0UL, NULL, ":"))) { return(1); }
2839+
if(JK_EXPECT_F(encodeState->write1(encodeState, 0L, ":"))) { return(1); }
28082840
if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, objects[idx]))) { return(1); }
28092841
}
28102842
}
2811-
if(JK_EXPECT_F(jk_encode_write1(encodeState, NULL, 0UL, NULL, "}"))) { return(1); }
2843+
return(encodeState->write1(encodeState, -1L, "}"));
28122844
}
28132845
break;
28142846

@@ -2831,6 +2863,7 @@ static id jk_encode(void *object, JKSerializeOptionFlags optionFlags, JKEncodeAs
28312863
memset(&encodeState, 0, sizeof(JKEncodeState));
28322864

28332865
encodeState.serializeOptionFlags = optionFlags;
2866+
encodeState.write1 = ((encodeState.serializeOptionFlags & JKSerializeOptionPretty) == 0UL) ? jk_encode_write1fast : jk_encode_write1slow;
28342867

28352868
encodeState.stringBuffer.roundSizeUpToMultipleOf = (1024UL * 32UL);
28362869
encodeState.utf8ConversionBuffer.roundSizeUpToMultipleOf = 4096UL;

0 commit comments

Comments
 (0)