Permalink
Browse files

Fixed a serious crashing bug. Fixed a memory leak. Fixed an off by on…

…e error. Updated the README.md file. Added CHANGELOG.md
  • Loading branch information...
1 parent c9ffd8f commit 8339b2716e557a9a1305355ff7f6b7c87b1def07 @johnezang committed Jan 9, 2011
Showing with 241 additions and 172 deletions.
  1. +20 −0 CHANGELOG.md
  2. +4 −1 JSONKit.h
  3. +86 −70 JSONKit.m
  4. +131 −101 README.md
View
20 CHANGELOG.md
@@ -0,0 +1,20 @@
+# JSONKit Changelog
+
+## Version 1.2 2011/01/08
+
+### Bug Fixes
+
+* When JSONKit attempted to parse and decode JSON that contained `{"key": value}` dictionaries that contained the same key more than once would likely result in a crash. This was a serious bug.
+* Under some conditions, JSONKit could potentially leak memory.
+* There was an off by one error in the code that checked whether or not the parser was at the end of the UTF8 buffer. This could result in JSONKit reading one by past the buffer bounds in some cases.
+
+### Other Changes
+
+* Some of the methods were missing `NULL` pointer checks for some of their arguments. This was fixed. In generally, when JSONKit encounters invalid arguments, it throws a `NSInvalidArgumentException` exception.
+* Various other minor changes such as tightening up numeric literals with `UL` or `L` qualification, assertion check tweaks and additions, etc.
+* The README.md file was updated with additional information.
+
+### Version 1.1
+
+No change log information was kept for versions prior to 1.2.
+
View
5 JSONKit.h
@@ -5,7 +5,7 @@
//
/*
- Copyright (c) 2010, John Engelhart
+ Copyright (c) 2011, John Engelhart
All rights reserved.
@@ -78,6 +78,9 @@ typedef unsigned int NSUInteger;
#ifndef _JSONKIT_H_
#define _JSONKIT_H_
+#define JSONKIT_VERSION_MAJOR 1
+#define JSONKIT_VERSION_MINOR 2
+
typedef NSUInteger JKHash;
typedef NSUInteger JKFlags;
typedef NSUInteger JKTokenType;
View
156 JSONKit.m
@@ -5,7 +5,7 @@
//
/*
- Copyright (c) 2010, John Engelhart
+ Copyright (c) 2011, John Engelhart
All rights reserved.
@@ -121,7 +121,7 @@ The code in isValidCodePoint() is derived from the ICU code in
// JK_TOKENBUFFER_SIZE is the default stack size for the temporary buffer used to hold "non-simple" strings (i.e., contains \ escapes)
#define JK_TOKENBUFFER_SIZE (1024UL * 2UL)
-// JK_STACK_OBJS is the default number of spaces reserved on the stack for temporarily storing pointers to Obj-C objects before they can be transfered to a NSArray / NSDictionary.
+// JK_STACK_OBJS is the default number of spaces reserved on the stack for temporarily storing pointers to Obj-C objects before they can be transferred to a NSArray / NSDictionary.
#define JK_STACK_OBJS (1024UL * 1UL)
#define JK_JSONBUFFER_SIZE (1024UL * 64UL)
@@ -330,9 +330,9 @@ The code in isValidCodePoint() is derived from the ICU code in
static int jk_parse_string(JKParseState *parseState);
static int jk_parse_number(JKParseState *parseState);
static size_t jk_parse_is_newline(JKParseState *parseState, const unsigned char *atCharacterPtr);
+JK_STATIC_INLINE int jk_parse_skip_newline(JKParseState *parseState);
JK_STATIC_INLINE void jk_parse_skip_whitespace(JKParseState *parseState);
static int jk_parse_next_token(JKParseState *parseState);
-static void jk_set_parsed_token(JKParseState *parseState, const unsigned char *ptr, size_t length, JKTokenType type, size_t advanceBy);
static void jk_error_parse_accept_or3(JKParseState *parseState, int state, NSString *or1String, NSString *or2String, NSString *or3String);
static void *jk_create_dictionary(JKParseState *parseState, size_t startingObjectIndex);
static void *jk_parse_dictionary(JKParseState *parseState);
@@ -382,10 +382,10 @@ static void jk_error(JKParseState *parseState, NSString *format, ...) {
const unsigned char *lineEnd = lineStart;
const unsigned char *atCharacterPtr = NULL;
- for(atCharacterPtr = lineStart; atCharacterPtr <= JK_END_STRING_PTR(parseState); atCharacterPtr++) { lineEnd = atCharacterPtr; if(jk_parse_is_newline(parseState, atCharacterPtr)) { break; } }
+ for(atCharacterPtr = lineStart; atCharacterPtr < JK_END_STRING_PTR(parseState); atCharacterPtr++) { lineEnd = atCharacterPtr; if(jk_parse_is_newline(parseState, atCharacterPtr)) { break; } }
NSString *lineString = @"", *carretString = @"";
- if(lineStart <= JK_END_STRING_PTR(parseState)) {
+ if(lineStart < JK_END_STRING_PTR(parseState)) {
lineString = [[[NSString alloc] initWithBytes:lineStart length:(lineEnd - lineStart) encoding:NSUTF8StringEncoding] autorelease];
carretString = [NSString stringWithFormat:@"%*.*s^", (int)(parseState->atIndex - parseState->lineStartIndex), (int)(parseState->atIndex - parseState->lineStartIndex), " "];
}
@@ -464,11 +464,16 @@ static void jk_objectStack_release(JKObjectStack *objectStack) {
NSCParameterAssert((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnHeap);
if(objectStack->objects != NULL) { free(objectStack->objects); objectStack->objects = NULL; }
if(objectStack->keys != NULL) { free(objectStack->keys); objectStack->keys = NULL; }
+ if(objectStack->hashes != NULL) { free(objectStack->hashes); objectStack->hashes = NULL; }
+ if(objectStack->sizes != NULL) { free(objectStack->sizes); objectStack->sizes = NULL; }
objectStack->flags &= ~JKObjectStackMustFree;
}
objectStack->objects = NULL;
objectStack->keys = NULL;
+ objectStack->hashes = NULL;
+ objectStack->sizes = NULL;
+
objectStack->count = 0UL;
objectStack->flags &= ~JKObjectStackLocationMask;
}
@@ -502,14 +507,14 @@ static int jk_objectStack_resize(JKObjectStack *objectStack, size_t newCount) {
if((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnStack) {
NSCParameterAssert((objectStack->flags & JKObjectStackMustFree) == 0);
- if((newObjects = (void **)calloc(1, roundedUpNewCount * sizeof(void *))) == NULL) { returnCode = 1; goto errorExit; }
+ if((newObjects = (void **)calloc(1UL, roundedUpNewCount * sizeof(void *))) == NULL) { returnCode = 1; goto errorExit; }
memcpy(newObjects, objectStack->objects, jk_min(objectStack->count, roundedUpNewCount) * sizeof(void *));
- if((newKeys = (void **)calloc(1, roundedUpNewCount * sizeof(void *))) == NULL) { returnCode = 1; goto errorExit; }
+ if((newKeys = (void **)calloc(1UL, roundedUpNewCount * sizeof(void *))) == NULL) { returnCode = 1; goto errorExit; }
memcpy(newKeys, objectStack->keys, jk_min(objectStack->count, roundedUpNewCount) * sizeof(void *));
- if((newHashes = (JKHash *)calloc(1, roundedUpNewCount * sizeof(JKHash))) == NULL) { returnCode = 1; goto errorExit; }
+ if((newHashes = (JKHash *)calloc(1UL, roundedUpNewCount * sizeof(JKHash))) == NULL) { returnCode = 1; goto errorExit; }
memcpy(newHashes, objectStack->hashes, jk_min(objectStack->count, roundedUpNewCount) * sizeof(JKHash));
- if((newSizes = (size_t *)calloc(1, roundedUpNewCount * sizeof(size_t))) == NULL) { returnCode = 1; goto errorExit; }
+ if((newSizes = (size_t *)calloc(1UL, roundedUpNewCount * sizeof(size_t))) == NULL) { returnCode = 1; goto errorExit; }
memcpy(newSizes, objectStack->sizes, jk_min(objectStack->count, roundedUpNewCount) * sizeof(size_t));
objectStack->flags = (objectStack->flags & ~JKObjectStackLocationMask) | (JKObjectStackOnHeap | JKObjectStackMustFree);
@@ -548,9 +553,9 @@ JK_STATIC_INLINE ConversionResult isValidCodePoint(UTF32 *u32CodePoint) {
ConversionResult result = conversionOK;
UTF32 ch = *u32CodePoint;
- if((ch >= UNI_SUR_HIGH_START) && (JK_EXPECTED(ch <= UNI_SUR_LOW_END, 1u))) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
- if((ch >= 0xFDD0UL) && ((JK_EXPECTED(ch <= 0xFDEFUL, 0U)) || JK_EXPECTED(((ch & 0xFFFEUL) == 0xFFFEUL), 0U)) && (JK_EXPECTED(ch <= 0x10FFFFUL, 1U))) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
- if(JK_EXPECTED(ch == 0UL, 0U)) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
+ if((ch >= UNI_SUR_HIGH_START) && (JK_EXPECTED(ch <= UNI_SUR_LOW_END, 1U))) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
+ if((ch >= 0xFDD0U) && ((JK_EXPECTED(ch <= 0xFDEFU, 0U)) || JK_EXPECTED(((ch & 0xFFFEU) == 0xFFFEU), 0U)) && (JK_EXPECTED(ch <= 0x10FFFFU, 1U))) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
+ if(JK_EXPECTED(ch == 0U, 0U)) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
finished:
*u32CodePoint = ch;
@@ -599,7 +604,7 @@ static ConversionResult ConvertSingleCodePointInUTF8(const UTF8 *sourceStart, co
source++;
while((source < sourceEnd) && (((*source) & 0xc0) == 0x80) && ((source - sourceStart) < (extraBytesToRead + 1))) { source++; }
NSCParameterAssert(source <= sourceEnd);
- result = ((source + extraBytesToRead + 1) > sourceEnd) ? sourceExhausted : sourceIllegal;
+ result = ((source < sourceEnd) && (((*source) & 0xc0) != 0x80)) ? sourceIllegal : ((sourceStart + extraBytesToRead + 1) > sourceEnd) ? sourceExhausted : sourceIllegal;
ch = UNI_REPLACEMENT_CHAR;
goto finished;
}
@@ -671,6 +676,7 @@ JK_STATIC_INLINE int jk_string_add_unicodeCodePoint(JKParseState *parseState, ui
static int jk_parse_string(JKParseState *parseState) {
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
const unsigned char *stringStart = JK_AT_STRING_PTR(parseState) + 1;
const unsigned char *endOfBuffer = JK_END_STRING_PTR(parseState);
const unsigned char *atStringCharacter = stringStart;
@@ -705,7 +711,7 @@ static int jk_parse_string(JKParseState *parseState) {
onlySimpleString = 0;
stringState = JSONStringStateParsing;
tokenBufferIdx = (atStringCharacter - stringStart) - 1L;
- if(JK_EXPECTED((tokenBufferIdx + 16UL) > parseState->token.tokenBuffer.bytes.length, 0U)) { if((tokenBuffer = jk_managedBuffer_resize(&parseState->token.tokenBuffer, tokenBufferIdx + 1024UL)) == NULL) { jk_error(parseState, @"Internal error: Unable to resize temporary buffer."); stringState = JSONStringStateError; goto finishedParsing; } }
+ if(JK_EXPECTED((tokenBufferIdx + 16UL) > parseState->token.tokenBuffer.bytes.length, 0U)) { if((tokenBuffer = jk_managedBuffer_resize(&parseState->token.tokenBuffer, tokenBufferIdx + 1024UL)) == NULL) { jk_error(parseState, @"Internal error: Unable to resize temporary buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; } }
memcpy(tokenBuffer, stringStart, tokenBufferIdx);
goto slowMatch;
}
@@ -719,7 +725,7 @@ static int jk_parse_string(JKParseState *parseState) {
slowMatch:
for(atStringCharacter = (stringStart + ((atStringCharacter - stringStart) - 1L)); (atStringCharacter < endOfBuffer) && (tokenBufferIdx < parseState->token.tokenBuffer.bytes.length); atStringCharacter++) {
- if((tokenBufferIdx + 16UL) > parseState->token.tokenBuffer.bytes.length) { if((tokenBuffer = jk_managedBuffer_resize(&parseState->token.tokenBuffer, tokenBufferIdx + 1024UL)) == NULL) { jk_error(parseState, @"Internal error: Unable to resize temporary buffer."); stringState = JSONStringStateError; goto finishedParsing; } }
+ if((tokenBufferIdx + 16UL) > parseState->token.tokenBuffer.bytes.length) { if((tokenBuffer = jk_managedBuffer_resize(&parseState->token.tokenBuffer, tokenBufferIdx + 1024UL)) == NULL) { jk_error(parseState, @"Internal error: Unable to resize temporary buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; } }
NSCParameterAssert(tokenBufferIdx < parseState->token.tokenBuffer.bytes.length);
@@ -740,9 +746,9 @@ static int jk_parse_string(JKParseState *parseState) {
ConversionResult result;
if(JK_EXPECTED((result = ConvertSingleCodePointInUTF8(atStringCharacter, endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK, 0U)) {
- if((result == sourceIllegal) && ((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0)) { jk_error(parseState, @"Illegal UTF8 sequence found in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; }
- if(result == sourceExhausted) { jk_error(parseState, @"End of buffer reached while parsing \"\" string. line: %ld", (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; }
- if(jk_string_add_unicodeCodePoint(parseState, u32ch, &tokenBufferIdx, &stringHash)) { jk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer."); stringState = JSONStringStateError; goto finishedParsing; }
+ if((result == sourceIllegal) && ((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0)) { jk_error(parseState, @"Illegal UTF8 sequence found in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; }
+ if(result == sourceExhausted) { jk_error(parseState, @"End of buffer reached while parsing UTF8 in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; }
+ if(jk_string_add_unicodeCodePoint(parseState, u32ch, &tokenBufferIdx, &stringHash)) { jk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; }
atStringCharacter = nextValidCharacter - 1;
continue;
} else {
@@ -825,7 +831,7 @@ static int jk_parse_string(JKParseState *parseState) {
if(isValidCodePoint(&cp) == sourceIllegal) { jk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
}
stringState = JSONStringStateParsing;
- if(jk_string_add_unicodeCodePoint(parseState, escapedUnicodeCodePoint, &tokenBufferIdx, &stringHash)) { jk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer."); stringState = JSONStringStateError; goto finishedParsing; }
+ if(jk_string_add_unicodeCodePoint(parseState, escapedUnicodeCodePoint, &tokenBufferIdx, &stringHash)) { jk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; }
}
else if((stringState >= JSONStringStateEscapedUnicode1) && (stringState <= JSONStringStateEscapedUnicodeSurrogate4)) { stringState++; }
break;
@@ -847,23 +853,23 @@ static int jk_parse_string(JKParseState *parseState) {
else { jk_error(parseState, @"Required a second \\u Unicode escape sequence following a surrogate \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
break;
- default: jk_error(parseState, @"Internal error: Unknown stringState."); stringState = JSONStringStateError; goto finishedParsing; break;
+ default: jk_error(parseState, @"Internal error: Unknown stringState. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; break;
}
}
}
finishedParsing:
if(stringState == JSONStringStateFinished) {
- NSCParameterAssert((parseState->stringBuffer.bytes.ptr + tokenStartIndex) <= atStringCharacter);
+ NSCParameterAssert((parseState->stringBuffer.bytes.ptr + tokenStartIndex) < atStringCharacter);
parseState->token.tokenPtrRange.ptr = parseState->stringBuffer.bytes.ptr + tokenStartIndex;
parseState->token.tokenPtrRange.length = (atStringCharacter - parseState->token.tokenPtrRange.ptr);
if(onlySimpleString) {
- NSCParameterAssert(((parseState->token.tokenPtrRange.ptr + 1) <= endOfBuffer) && (parseState->token.tokenPtrRange.length >= 2UL) && (((parseState->token.tokenPtrRange.ptr + 1) + (parseState->token.tokenPtrRange.length - 2)) <= endOfBuffer));
+ NSCParameterAssert(((parseState->token.tokenPtrRange.ptr + 1) < endOfBuffer) && (parseState->token.tokenPtrRange.length >= 2UL) && (((parseState->token.tokenPtrRange.ptr + 1) + (parseState->token.tokenPtrRange.length - 2)) < endOfBuffer));
parseState->token.value.ptrRange.ptr = parseState->token.tokenPtrRange.ptr + 1;
- parseState->token.value.ptrRange.length = parseState->token.tokenPtrRange.length - 2;
+ parseState->token.value.ptrRange.length = parseState->token.tokenPtrRange.length - 2UL;
} else {
parseState->token.value.ptrRange.ptr = parseState->token.tokenBuffer.bytes.ptr;
parseState->token.value.ptrRange.length = tokenBufferIdx;
@@ -879,6 +885,7 @@ static int jk_parse_string(JKParseState *parseState) {
}
static int jk_parse_number(JKParseState *parseState) {
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
const unsigned char *numberStart = JK_AT_STRING_PTR(parseState);
const unsigned char *endOfBuffer = JK_END_STRING_PTR(parseState);
const unsigned char *atNumberCharacter = NULL;
@@ -921,7 +928,7 @@ static int jk_parse_number(JKParseState *parseState) {
errno = 0;
// Treat "-0" as a floating point number, which is capable of representing negative zeros.
- if(isNegative && (parseState->token.tokenPtrRange.length == 2) && (numberTempBuf[1] == '0')) { isFloatingPoint = 1; }
+ if(isNegative && (parseState->token.tokenPtrRange.length == 2UL) && (numberTempBuf[1] == '0')) { isFloatingPoint = 1; }
if(isFloatingPoint) {
parseState->token.value.number.doubleValue = strtod((const char *)numberTempBuf, (char **)&endOfNumber);
@@ -935,35 +942,35 @@ static int jk_parse_number(JKParseState *parseState) {
parseState->token.value.type = JKValueTypeLongLong;
parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.longLongValue;
parseState->token.value.ptrRange.length = sizeof(long long);
- parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type) + parseState->token.value.number.longLongValue;
+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type) + (JKHash)parseState->token.value.number.longLongValue;
} else {
parseState->token.value.number.unsignedLongLongValue = strtoull((const char *)numberTempBuf, (char **)&endOfNumber, 10);
parseState->token.value.type = JKValueTypeUnsignedLongLong;
parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.unsignedLongLongValue;
parseState->token.value.ptrRange.length = sizeof(unsigned long long);
- parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type) + parseState->token.value.number.unsignedLongLongValue;
+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type) + (JKHash)parseState->token.value.number.unsignedLongLongValue;
}
}
- if(errno != 0) {
+ if(JK_EXPECTED(errno != 0, 0U)) {
numberState = JSONNumberStateError;
if(errno == ERANGE) {
switch(parseState->token.value.type) {
case JKValueTypeDouble: jk_error(parseState, @"The value '%s' could not be represented as a 'double' due to %s.", numberTempBuf, (parseState->token.value.number.doubleValue == 0.0) ? "underflow" : "overflow"); break;
case JKValueTypeLongLong: jk_error(parseState, @"The value '%s' exceeded the minimum value that could be represented: %lld.", numberTempBuf, parseState->token.value.number.longLongValue); break;
case JKValueTypeUnsignedLongLong: jk_error(parseState, @"The value '%s' exceeded the maximum value that could be represented: %llu.", numberTempBuf, parseState->token.value.number.unsignedLongLongValue); break;
- default: jk_error(parseState, @"Internal error: Unnkown token value type."); break;
+ default: jk_error(parseState, @"Internal error: Unnkown token value type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
}
}
}
- if(endOfNumber != &numberTempBuf[parseState->token.tokenPtrRange.length]) { numberState = JSONNumberStateError; jk_error(parseState, @"The conversion function did not consume all of the number tokens characters."); }
+ if(JK_EXPECTED(endOfNumber != &numberTempBuf[parseState->token.tokenPtrRange.length], 0U) && JK_EXPECTED(numberState != JSONNumberStateError, 0U)) { 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 = calculateNumberHash(parseState->token.value.hash, parseState->token.value.ptrRange.ptr[hashIndex]); }
}
- if(numberState != JSONNumberStateFinished) { jk_error(parseState, @"Invalid number."); }
- return((numberState == JSONNumberStateFinished) ? 0 : 1);
+ if(JK_EXPECTED(numberState != JSONNumberStateFinished, 0U)) { jk_error(parseState, @"Invalid number."); }
+ return(JK_EXPECTED((numberState == JSONNumberStateFinished), 1U) ? 0 : 1);
}
JK_STATIC_INLINE void jk_set_parsed_token(JKParseState *parseState, const unsigned char *ptr, size_t length, JKTokenType type, size_t advanceBy) {
@@ -974,47 +981,47 @@ JK_STATIC_INLINE void jk_set_parsed_token(JKParseState *parseState, const unsign
}
static size_t jk_parse_is_newline(JKParseState *parseState, const unsigned char *atCharacterPtr) {
- NSCParameterAssert((parseState != NULL) && (atCharacterPtr != NULL) && (atCharacterPtr >= parseState->stringBuffer.bytes.ptr) && (atCharacterPtr <= JK_END_STRING_PTR(parseState)));
+ NSCParameterAssert((parseState != NULL) && (atCharacterPtr != NULL) && (atCharacterPtr >= parseState->stringBuffer.bytes.ptr) && (atCharacterPtr < JK_END_STRING_PTR(parseState)));
const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
- if(JK_EXPECTED(atCharacterPtr > endOfStringPtr, 0U)) { return(0UL); }
+ if(JK_EXPECTED(atCharacterPtr >= endOfStringPtr, 0U)) { return(0UL); }
if(JK_EXPECTED((*(atCharacterPtr + 0)) == '\n', 0U)) { return(1UL); }
- if(JK_EXPECTED((*(atCharacterPtr + 0)) == '\r', 0U)) { if((JK_EXPECTED((atCharacterPtr + 1) <= endOfStringPtr, 1U)) && ((*(atCharacterPtr + 1)) == '\n')) { return(2UL); } return(1UL); }
+ if(JK_EXPECTED((*(atCharacterPtr + 0)) == '\r', 0U)) { if((JK_EXPECTED((atCharacterPtr + 1) < endOfStringPtr, 1U)) && ((*(atCharacterPtr + 1)) == '\n')) { return(2UL); } return(1UL); }
if(parseState->parseOptionFlags & JKParseOptionUnicodeNewlines) {
- if((JK_EXPECTED((*(atCharacterPtr + 0)) == 0xc2, 0U)) && (((atCharacterPtr + 1) <= endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x85))) { return(2UL); }
- if((JK_EXPECTED((*(atCharacterPtr + 0)) == 0xe2, 0U)) && (((atCharacterPtr + 1) <= endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x80)) && (((atCharacterPtr + 2) <= endOfStringPtr) && (((*(atCharacterPtr + 2)) == 0xa8) || ((*(atCharacterPtr + 2)) == 0xa9)))) { return(3UL); }
+ if((JK_EXPECTED((*(atCharacterPtr + 0)) == 0xc2, 0U)) && (((atCharacterPtr + 1) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x85))) { return(2UL); }
+ if((JK_EXPECTED((*(atCharacterPtr + 0)) == 0xe2, 0U)) && (((atCharacterPtr + 2) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x80) && (((*(atCharacterPtr + 2)) == 0xa8) || ((*(atCharacterPtr + 2)) == 0xa9)))) { return(3UL); }
}
return(0UL);
}
-static int jk_parse_skip_newline(JKParseState *parseState) {
+JK_STATIC_INLINE int jk_parse_skip_newline(JKParseState *parseState) {
size_t newlineAdvanceAtIndex = 0UL;
if(JK_EXPECTED((newlineAdvanceAtIndex = jk_parse_is_newline(parseState, JK_AT_STRING_PTR(parseState))) > 0UL, 0U)) { parseState->lineNumber++; parseState->atIndex += (newlineAdvanceAtIndex - 1UL); parseState->lineStartIndex = parseState->atIndex + 1UL; return(1); }
return(0);
}
JK_STATIC_INLINE void jk_parse_skip_whitespace(JKParseState *parseState) {
- NSCParameterAssert((parseState != NULL));
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
const unsigned char *atCharacterPtr = NULL;
const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
- for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECTED((atCharacterPtr = JK_AT_STRING_PTR(parseState)) <= endOfStringPtr, 1U)); parseState->atIndex++) {
+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECTED((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr, 1U)); parseState->atIndex++) {
if(((*(atCharacterPtr + 0)) == ' ') || ((*(atCharacterPtr + 0)) == '\t')) { continue; }
if(jk_parse_skip_newline(parseState)) { continue; }
if(parseState->parseOptionFlags & JKParseOptionComments) {
- if((JK_EXPECTED((*(atCharacterPtr + 0)) == '/', 0U)) && (JK_EXPECTED((atCharacterPtr + 1) <= endOfStringPtr, 1U))) {
+ if((JK_EXPECTED((*(atCharacterPtr + 0)) == '/', 0U)) && (JK_EXPECTED((atCharacterPtr + 1) < endOfStringPtr, 1U))) {
if((*(atCharacterPtr + 1)) == '/') {
parseState->atIndex++;
- for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECTED((atCharacterPtr = JK_AT_STRING_PTR(parseState)) <= endOfStringPtr, 1U)); parseState->atIndex++) { if(jk_parse_skip_newline(parseState)) { break; } }
+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECTED((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr, 1U)); parseState->atIndex++) { if(jk_parse_skip_newline(parseState)) { break; } }
continue;
}
if((*(atCharacterPtr + 1)) == '*') {
parseState->atIndex++;
- for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECTED((atCharacterPtr = JK_AT_STRING_PTR(parseState)) <= endOfStringPtr, 1U)); parseState->atIndex++) {
+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECTED((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr, 1U)); parseState->atIndex++) {
if(jk_parse_skip_newline(parseState)) { continue; }
- if(((*(atCharacterPtr + 0)) == '*') && (((atCharacterPtr + 1) <= endOfStringPtr) && ((*(atCharacterPtr + 1)) == '/'))) { parseState->atIndex++; break; }
+ if(((*(atCharacterPtr + 0)) == '*') && (((atCharacterPtr + 1) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == '/'))) { parseState->atIndex++; break; }
}
continue;
}
@@ -1025,7 +1032,7 @@ JK_STATIC_INLINE void jk_parse_skip_whitespace(JKParseState *parseState) {
}
static int jk_parse_next_token(JKParseState *parseState) {
- NSCParameterAssert((parseState != NULL));
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
const unsigned char *atCharacterPtr = NULL;
const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
unsigned char currentCharacter = 0U;
@@ -1037,7 +1044,7 @@ static int jk_parse_next_token(JKParseState *parseState) {
jk_parse_skip_whitespace(parseState);
- if((JK_EXPECTED(stopParsing == 0, 1U)) && (JK_EXPECTED((atCharacterPtr = JK_AT_STRING_PTR(parseState)) <= endOfStringPtr, 1U))) {
+ if((JK_EXPECTED(stopParsing == 0, 1U)) && (JK_EXPECTED((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr, 1U))) {
currentCharacter = *atCharacterPtr;
switch(currentCharacter) {
@@ -1048,9 +1055,9 @@ static int jk_parse_next_token(JKParseState *parseState) {
case ',': jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeComma, 1UL); break;
case ':': jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeSeparator, 1UL); break;
- case 't': if(!((JK_EXPECTED((atCharacterPtr + 4UL) <= endOfStringPtr, 1U)) && (JK_EXPECTED(atCharacterPtr[1] == 'r', 1U)) && (JK_EXPECTED(atCharacterPtr[2] == 'u', 1U)) && (JK_EXPECTED(atCharacterPtr[3] == 'e', 1U)))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 4UL, JKTokenTypeTrue, 4UL); } break;
- case 'f': if(!((JK_EXPECTED((atCharacterPtr + 5UL) <= endOfStringPtr, 1U)) && (JK_EXPECTED(atCharacterPtr[1] == 'a', 1U)) && (JK_EXPECTED(atCharacterPtr[2] == 'l', 1U)) && (JK_EXPECTED(atCharacterPtr[3] == 's', 1U)) && (JK_EXPECTED(atCharacterPtr[4] == 'e', 1U)))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 5UL, JKTokenTypeFalse, 5UL); } break;
- case 'n': if(!((JK_EXPECTED((atCharacterPtr + 4UL) <= endOfStringPtr, 1U)) && (JK_EXPECTED(atCharacterPtr[1] == 'u', 1U)) && (JK_EXPECTED(atCharacterPtr[2] == 'l', 1U)) && (JK_EXPECTED(atCharacterPtr[3] == 'l', 1U)))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 4UL, JKTokenTypeNull, 4UL); } break;
+ case 't': if(!((JK_EXPECTED((atCharacterPtr + 4UL) < endOfStringPtr, 1U)) && (JK_EXPECTED(atCharacterPtr[1] == 'r', 1U)) && (JK_EXPECTED(atCharacterPtr[2] == 'u', 1U)) && (JK_EXPECTED(atCharacterPtr[3] == 'e', 1U)))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 4UL, JKTokenTypeTrue, 4UL); } break;
+ case 'f': if(!((JK_EXPECTED((atCharacterPtr + 5UL) < endOfStringPtr, 1U)) && (JK_EXPECTED(atCharacterPtr[1] == 'a', 1U)) && (JK_EXPECTED(atCharacterPtr[2] == 'l', 1U)) && (JK_EXPECTED(atCharacterPtr[3] == 's', 1U)) && (JK_EXPECTED(atCharacterPtr[4] == 'e', 1U)))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 5UL, JKTokenTypeFalse, 5UL); } break;
+ case 'n': if(!((JK_EXPECTED((atCharacterPtr + 4UL) < endOfStringPtr, 1U)) && (JK_EXPECTED(atCharacterPtr[1] == 'u', 1U)) && (JK_EXPECTED(atCharacterPtr[2] == 'l', 1U)) && (JK_EXPECTED(atCharacterPtr[3] == 'l', 1U)))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 4UL, JKTokenTypeNull, 4UL); } break;
case '"': if(JK_EXPECTED((stopParsing = jk_parse_string(parseState)) == 0, 1U)) { jk_set_parsed_token(parseState, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length, JKTokenTypeString, 0UL); } break;
@@ -1067,17 +1074,17 @@ static int jk_parse_next_token(JKParseState *parseState) {
JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) {
- parseState->cache.clockIdx = (parseState->cache.clockIdx + 19UL) & (parseState->cache.count - 1);
+ parseState->cache.clockIdx = (parseState->cache.clockIdx + 19UL) & (parseState->cache.count - 1UL);
parseState->cache.items[parseState->cache.clockIdx].age = (parseState->cache.items[parseState->cache.clockIdx].age >> 1);
}
static void *jk_cachedObjects(JKParseState *parseState) {
- unsigned long bucket = parseState->token.value.hash & (parseState->cache.count - 1), setBucket = 0UL, useableBucket = 0UL, x = 0UL;
+ unsigned long bucket = parseState->token.value.hash & (parseState->cache.count - 1UL), setBucket = 0UL, useableBucket = 0UL, x = 0UL;
void *parsedAtom = NULL;
jk_cache_age(parseState);
- if((parseState->token.value.type == JKValueTypeString) && (parseState->token.value.ptrRange.length == 0)) { return(@""); }
+ if(JK_EXPECTED(parseState->token.value.ptrRange.length == 0, 0U) && JK_EXPECTED(parseState->token.value.type == JKValueTypeString, 1U)) { return(@""); }
for(x = 0UL; x < JK_CACHE_PROBES; x++) {
if(JK_EXPECTED(parseState->cache.items[bucket].object == NULL, 0U)) { setBucket = 1UL; useableBucket = bucket; break; }
@@ -1101,16 +1108,15 @@ JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) {
else { parsedAtom = (void *)parseState->objCImpCache.NSNumberInitWithUnsignedLongLong(parseState->objCImpCache.NSNumberAlloc(parseState->objCImpCache.NSNumberClass, @selector(alloc)), @selector(initWithUnsignedLongLong:), parseState->token.value.number.unsignedLongLongValue); }
break;
case JKValueTypeDouble: parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberDoubleType, &parseState->token.value.number.doubleValue); break;
- default: jk_error(parseState, @"Internal error: Unknown token value type. line #%ld", (long)__LINE__); break;
+ default: jk_error(parseState, @"Internal error: Unknown token value type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
}
if(JK_EXPECTED(setBucket, 1U) && (JK_EXPECTED(parsedAtom != NULL, 1U))) {
bucket = useableBucket;
if(parseState->cache.items[bucket].object != NULL) { CFRelease(parseState->cache.items[bucket].object); parseState->cache.items[bucket].object = NULL; }
- if(JK_EXPECTED((parseState->cache.items[bucket].bytes = (unsigned char *)reallocf(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.length + (parseState->token.value.type == JKValueTypeString ? 1UL : 0UL))) != NULL, 1U)) {
+ if(JK_EXPECTED((parseState->cache.items[bucket].bytes = (unsigned char *)reallocf(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.length)) != NULL, 1U)) {
memcpy(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length);
- if(parseState->token.value.type == JKValueTypeString) { parseState->cache.items[bucket].bytes[parseState->token.value.ptrRange.length] = 0; }
parseState->cache.items[bucket].object = (void *)CFRetain(parsedAtom);
parseState->cache.items[bucket].hash = parseState->token.value.hash;
parseState->cache.items[bucket].size = parseState->token.value.ptrRange.length;
@@ -1133,7 +1139,7 @@ JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) {
case JKTokenTypeTrue: parsedAtom = (void *)kCFBooleanTrue; break;
case JKTokenTypeFalse: parsedAtom = (void *)kCFBooleanFalse; break;
case JKTokenTypeNull: parsedAtom = (void *)kCFNull; break;
- default: jk_error(parseState, @"Internal error: Unknown token type. line #%ld", (long)__LINE__); break;
+ default: jk_error(parseState, @"Internal error: Unknown token type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
}
return(parsedAtom);
@@ -1156,7 +1162,7 @@ static void jk_error_parse_accept_or3(JKParseState *parseState, int state, NSStr
void *parsedArray = NULL;
while(JK_EXPECTED((JK_EXPECTED(stopParsing == 0, 1U)) && (JK_EXPECTED(parseState->atIndex < parseState->stringBuffer.bytes.length, 1U)), 1U)) {
- if(JK_EXPECTED(parseState->objectStack.index > (parseState->objectStack.count - 4), 0U)) { if(jk_objectStack_resize(&parseState->objectStack, parseState->objectStack.count + 128UL)) { jk_error(parseState, @"Internal error: [array] objectsIndex > %zu, resize failed?\n", (parseState->objectStack.count - 4)); break; } }
+ if(JK_EXPECTED(parseState->objectStack.index > (parseState->objectStack.count - 4UL), 0U)) { if(jk_objectStack_resize(&parseState->objectStack, parseState->objectStack.count + 128UL)) { jk_error(parseState, @"Internal error: [array] objectsIndex > %zu, resize failed? %@ line %#ld", (parseState->objectStack.count - 4UL), [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break; } }
if(JK_EXPECTED((stopParsing = jk_parse_next_token(parseState)) == 0, 1U)) {
void *object = NULL;
@@ -1191,28 +1197,37 @@ static void jk_error_parse_accept_or3(JKParseState *parseState, int state, NSStr
return(parsedArray);
}
-static void * jk_create_dictionary(JKParseState *parseState, size_t startingObjectIndex) {
+static void *jk_create_dictionary(JKParseState *parseState, size_t startingObjectIndex) {
void *parsedDictionary = NULL;
parseState->objectStack.index--;
ssize_t numberOfDuplicateKeys = 0L, atIndex = 0L, cmpIndex = 0L;
- for(atIndex = parseState->objectStack.index; atIndex >= (ssize_t)startingObjectIndex; atIndex--) {
+ for(atIndex = parseState->objectStack.index - 1L; atIndex >= (ssize_t)startingObjectIndex; atIndex--) {
for(cmpIndex = atIndex - 1L; cmpIndex >= (ssize_t)startingObjectIndex; cmpIndex--) {
- if((parseState->objectStack.keys[atIndex] != NULL) && (parseState->objectStack.keys[cmpIndex] != NULL) && (parseState->objectStack.hashes[atIndex] == parseState->objectStack.hashes[cmpIndex]) && (parseState->objectStack.sizes[atIndex] == parseState->objectStack.sizes[cmpIndex]) && ((parseState->objectStack.keys[atIndex] == parseState->objectStack.keys[cmpIndex]) || CFEqual(parseState->objectStack.keys[atIndex], parseState->objectStack.keys[cmpIndex]))) {
+ NSCParameterAssert(((parseState->objectStack.keys[atIndex] != NULL) || (parseState->objectStack.objects[atIndex] != NULL)) ? ((parseState->objectStack.keys[atIndex] != NULL) && (parseState->objectStack.objects[atIndex] != NULL)) : 1);
+ if(JK_EXPECTED((parseState->objectStack.hashes[atIndex] == parseState->objectStack.hashes[cmpIndex]), 0U) && (parseState->objectStack.sizes[atIndex] == parseState->objectStack.sizes[cmpIndex]) && ((parseState->objectStack.keys[atIndex] != NULL) && ((parseState->objectStack.keys[atIndex] == parseState->objectStack.keys[cmpIndex]) || (CFEqual(parseState->objectStack.keys[atIndex], parseState->objectStack.keys[cmpIndex]))))) {
numberOfDuplicateKeys++;
CFRelease(parseState->objectStack.keys[cmpIndex]); parseState->objectStack.keys[cmpIndex] = NULL;
CFRelease(parseState->objectStack.objects[cmpIndex]); parseState->objectStack.objects[cmpIndex] = NULL;
+ parseState->objectStack.hashes[cmpIndex] = 0UL;
+ parseState->objectStack.sizes[cmpIndex] = 0UL;
}
}
}
- if(numberOfDuplicateKeys) {
+ if(JK_EXPECTED(numberOfDuplicateKeys, 0U)) {
atIndex = startingObjectIndex;
for(cmpIndex = startingObjectIndex; cmpIndex < (ssize_t)parseState->objectStack.index; cmpIndex++) {
- if((parseState->objectStack.keys[cmpIndex] != NULL) && (cmpIndex != atIndex)) {
- parseState->objectStack.keys[atIndex] = parseState->objectStack.keys[cmpIndex];
- parseState->objectStack.objects[atIndex] = parseState->objectStack.objects[cmpIndex];
+ NSCParameterAssert(((parseState->objectStack.keys[cmpIndex] != NULL) || (parseState->objectStack.objects[cmpIndex] != NULL)) ? ((parseState->objectStack.keys[cmpIndex] != NULL) && (parseState->objectStack.objects[cmpIndex] != NULL)) : 1);
+ if((parseState->objectStack.keys[cmpIndex] != NULL)) {
+ if(cmpIndex != atIndex) {
+ parseState->objectStack.keys[atIndex] = parseState->objectStack.keys[cmpIndex];
+ parseState->objectStack.objects[atIndex] = parseState->objectStack.objects[cmpIndex];
+ parseState->objectStack.keys[cmpIndex] = NULL;
+ parseState->objectStack.objects[cmpIndex] = NULL;
+ }
+ atIndex++;
}
}
@@ -1230,7 +1245,7 @@ static void jk_error_parse_accept_or3(JKParseState *parseState, int state, NSStr
void *parsedDictionary = NULL;
while(JK_EXPECTED((JK_EXPECTED(stopParsing == 0, 1U)) && (JK_EXPECTED(parseState->atIndex < parseState->stringBuffer.bytes.length, 1U)), 1U)) {
- if(JK_EXPECTED(parseState->objectStack.index > (parseState->objectStack.count - 4), 0U)) { if(jk_objectStack_resize(&parseState->objectStack, parseState->objectStack.count + 128UL)) { jk_error(parseState, @"Internal error: [dictionary] objectsIndex > %zu, resize failed?\n", (parseState->objectStack.count - 4)); break; } }
+ if(JK_EXPECTED(parseState->objectStack.index > (parseState->objectStack.count - 4UL), 0U)) { if(jk_objectStack_resize(&parseState->objectStack, parseState->objectStack.count + 128UL)) { jk_error(parseState, @"Internal error: [dictionary] objectsIndex > %zu, resize failed? %@ line #%ld", (parseState->objectStack.count - 4UL), [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break; } }
size_t objectStackIndex = parseState->objectStack.index++;
parseState->objectStack.keys[objectStackIndex] = NULL;
@@ -1295,7 +1310,7 @@ static id json_parse_it(JKParseState *parseState) {
}
}
- NSCParameterAssert(parseState->objectStack.index == 0);
+ NSCParameterAssert((parseState->objectStack.index == 0) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
if((parsedObject == NULL) && (JK_AT_STRING_PTR(parseState) == JK_END_STRING_PTR(parseState))) { jk_error(parseState, @"Reached the end of the buffer."); }
if(parsedObject == NULL) { jk_error(parseState, @"Unable to parse JSON."); }
@@ -1348,7 +1363,7 @@ - (id)initWithParseOptions:(JKParseOptionFlags)parseOptionFlags
temp_NSNumber = NULL;
parseState.cache.count = JK_CACHE_SLOTS;
- if((parseState.cache.items = (JKTokenCacheItem *)calloc(1, sizeof(JKTokenCacheItem) * parseState.cache.count)) == NULL) { goto errorExit; }
+ if((parseState.cache.items = (JKTokenCacheItem *)calloc(1UL, sizeof(JKTokenCacheItem) * parseState.cache.count)) == NULL) { goto errorExit; }
return(self);
@@ -1441,7 +1456,8 @@ - (id)parseJSONData:(NSData *)jsonData
- (id)parseJSONData:(NSData *)jsonData error:(NSError **)error
{
- return([self parseUTF8String:[jsonData bytes] length:[jsonData length] error:error]);
+ if(jsonData == NULL) { [NSException raise:NSInvalidArgumentException format:@"The jsonData argument is NULL."]; }
+ return([self parseUTF8String:(const unsigned char *)[jsonData bytes] length:[jsonData length] error:error]);
}
@end
@@ -1513,7 +1529,7 @@ static int jk_encode_printf(JKEncodeState *encodeState, const char *format, ...)
va_start(varArgsList, format);
va_end(varArgsList);
- if(encodeState->stringBuffer.bytes.length < encodeState->atIndex) { jk_encode_error(encodeState, @"Internal inconsistency error: atIndex > buffer length."); return(1); }
+ if(encodeState->stringBuffer.bytes.length < encodeState->atIndex) { jk_encode_error(encodeState, @"Internal inconsistency error: atIndex > buffer length. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); return(1); }
if((encodeState->stringBuffer.bytes.length - encodeState->atIndex) < 1024L) { if(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + 4096UL) == NULL) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); } }
char *atPtr = (char *)encodeState->stringBuffer.bytes.ptr + encodeState->atIndex;
@@ -1531,7 +1547,7 @@ static int jk_encode_printf(JKEncodeState *encodeState, const char *format, ...)
}
static int jk_encode_write(JKEncodeState *encodeState, const char *format) {
- if(JK_EXPECTED(encodeState->stringBuffer.bytes.length < encodeState->atIndex, 0U)) { jk_encode_error(encodeState, @"Internal inconsistency error: atIndex > buffer length."); return(1); }
+ if(JK_EXPECTED(encodeState->stringBuffer.bytes.length < encodeState->atIndex, 0U)) { jk_encode_error(encodeState, @"Internal inconsistency error: atIndex > buffer length. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); return(1); }
if(JK_EXPECTED((encodeState->stringBuffer.bytes.length - encodeState->atIndex) < 1024L, 0U)) { if(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + 4096UL) == NULL) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); } }
char *atPtr = (char *)encodeState->stringBuffer.bytes.ptr + encodeState->atIndex;
@@ -1556,7 +1572,7 @@ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *object
NSCParameterAssert((encodeState != NULL) && (objectPtr != NULL));
NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
- id object = objectPtr;
+ id object = (id)objectPtr;
if(((encodeState->atIndex + 256UL) > encodeState->stringBuffer.bytes.length) && (jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + 256UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
View
232 README.md
@@ -2,11 +2,9 @@
### A Very High Performance Objective-C JSON Library
-JSONKit is licensed under the terms of the BSD License.
+JSONKit is licensed under the terms of the BSD License. Copyright &copy; 2011, John Engelhart.
-JavaScript Object Notation, or [JSON][JSON], is a lightweight,
-text-based, serialization format for structured data that is used by many
-web-based services and API's. It is defined by [RFC 4627][RFC 4627]
+JavaScript Object Notation, or [JSON][], is a lightweight, text-based, serialization format for structured data that is used by many web-based services and API's. It is defined by [RFC 4627][].
JSON provides the following primitive types:
@@ -26,120 +24,134 @@ These primitive types are mapped to the following Objective-C Foundation classes
<tr><td>Number</td><td><a href="http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/index.html"><code>NSNumber</code></a></td></tr>
<tr><td>String</td><td><a href="http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html"><code>NSString</code></a></td></tr>
<tr><td>Array</td><td><a href="http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/index.html"><code>NSArray</code></a></td></tr>
-<tr><td>Associative Arrays</td><td><a href="http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/index.html"<code>NSDictionary</code></a></td></tr>
+<tr><td>Associative Arrays</td><td><a href="http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/index.html"><code>NSDictionary</code></a></td></tr>
</table>
-JSONKit uses Core Foundation internally, and it is assumed that Core
-Foundation &equiv; Foundation for every equivalent base type, i.e. `CFString`
-&equiv; `NSString`.
+JSONKit uses Core Foundation internally, and it is assumed that Core Foundation &equiv; Foundation for every equivalent base type, i.e. [`CFString`][CFString] &equiv; [`NSString`][NSString].
### JSON To Objective-C Primitive Mapping Details
-* While the [JSON specification][RFC 4627] specifies that the serialized
- JSON must be encoded in Unicode, it does not specify how Unicode encoding
- errors should be handled. In general, JSONKit will not accept JSON that
- contains ill-formed Unicode.
-
- When the `JKParseOptionLooseUnicode` option is used, JSONKit follows the
- specifications and recommendations given in
- [The Unicode 5.2 standard, Chapter 3][Unicode_CH3], section 3.9 *Unicode
- Encoding Forms*. As a general rule of thumb, the Unicode code point
- `U+FFFD` is substituted for any ill-formed Unicode encountered.
- JSONKit attempts to follow the recommended *Best Practice for Using
- U+FFFD*: ***Replace each maximal subpart of an ill-formed subsequence by
- a single U+FFFD.*** Additionally, the following Unicode code points are
- treated as ill-formed Unicode, and if `JKParseOptionLooseUnicode` is
- enabled, cause `U+FFFD` to be substituted in their place:
+* While the [JSON specification][RFC 4627] specifies that the serialized JSON must be encoded in Unicode, it does not specify how Unicode encoding errors should be handled. In general, JSONKit will not accept JSON that contains ill-formed Unicode.
+ When the `JKParseOptionLooseUnicode` option is used, JSONKit follows the specifications and recommendations given in [The Unicode 5.2 standard, Chapter 3][Unicode_CH3], section 3.9 *Unicode Encoding Forms*. As a general rule of thumb, the Unicode code point `U+FFFD` is substituted for any ill-formed Unicode encountered. JSONKit attempts to follow the recommended *Best Practice for Using U+FFFD*: ***Replace each maximal subpart of an ill-formed subsequence by a single U+FFFD.*** Additionally, the following Unicode code points are treated as ill-formed Unicode, and if `JKParseOptionLooseUnicode` is enabled, cause `U+FFFD` to be substituted in their place:
+
`U+0000`.<br>
`U+D800` thru `U+DFFF`, inclusive.<br>
`U+FDD0` thru `U+FDEF`, inclusive.<br>
`U+`*n*`FFFE` and `U+`*n*`FFFF`, where *n* is from `0x0` to `0x10`
+
+ The code points `U+FDD0` thru `U+FDEF`, `U+`*n*`FFFE`, and `U+`*n*`FFFF` (where *n* is from `0x0` to `0x10`), are defined as ***Noncharacters*** by the Unicode standard and "should never be interchanged".
+
+ [RFC 4627][] allows for these limitations under section 4, Parsers: `An implementation may set limits on the length and character contents of strings.`
+
+ The [`NSString`][NSString] class may place additional restrictions or otherwise transform the JSON String in such a way so that the JSON String is not bijective with the instantiated [`NSString`][NSString] object. In other words, JSONKit can not guarantee that when you round trip a JSON String to a [`NSString`][NSString] and then back to a JSON String that the two JSON Strings will be exactly the same, even though in practice they are. For clarity, "exactly" in this case means bit for bit identical. JSONKit can not even guarantee that the two JSON Strings will be [Unicode equivalent][Unicode_equivalence], even though in practice they will be and would be the most likely cause for the two round tripped JSON Strings to no longer be bit for bit identical.
+
+* JSONKit maps `true` and `false` to the [`CFBoolean`][CFBoolean] values [`kCFBooleanTrue`][kCFBooleanTrue] and [`kCFBooleanFalse`][kCFBooleanFalse], respectively. Conceptually, [`CFBoolean`][CFBoolean] values can be thought of, and treated as, [`NSNumber`][NSNumber] class objects. The benefit to using [`CFBoolean`][CFBoolean] is that `true` and `false` JSON values can be round trip deserialized and serialized without conversion or promotion to a [`NSNumber`][NSNumber] with a value of `0` or `1`.
+
+* The [JSON specification][RFC 4627] does not specify the details or requirements for JSON Number values, nor does it specify how errors due to conversion should be handled. In general, JSONKit will not accept JSON that contains JSON Number values that it can not convert with out error or loss of precision.
+
+ For non-floating-point numbers (i.e., JSON Number values that do not include a `.` or `e|E`), JSONKit uses a 64-bit C primitive type internally, regardless of whether the target architecture is 32-bit or 64-bit. For unsigned values (i.e., those that do not begin with a `-`), this allows for values up to <code>2<sup>64</sup>-1</code> and up to <code>-2<sup>63</sup></code> for negative values. As a special case, the JSON Number `-0` is treated as a floating-point number since the underlying floating-point primitive type is capable of representing a negative zero, whereas the underlying twos-complement non-floating-point primitive type can not. JSON that contains Number values that exceed these limits will fail to parse and optionally return a [`NSError`][NSError] object. The functions [`strtoll()`][strtoll] and [`strtoull()`][strtoull] are used to perform the conversions.
+
+ The C `double` primitive type, or IEEE 754 Double 64-bit floating-point, is used to represent floating-point JSON Number values. JSON that contains floating-point Number values that can not be represented as a `double` (i.e., due to over or underflow) will fail to parse and optionally return a [`NSError`][NSError] object. The function [`strtod()`][strtod] is used to perform the conversion. Note that the JSON standard does not allow for infinities or `NaN` (Not a Number).
- The code points `U+FDD0` thru `U+FDEF`, `U+`*n*`FFFE`, and `U+`*n*`FFFF`
- (where *n* is from `0x0` to `0x10`), are defined as
- ***Noncharacters*** by the Unicode standard and "should never be
- interchanged".
-
- [RFC 4627][] allows for these limitations under section 4, Parsers:
- `An implementation may set limits on the length and character contents
- of strings.`
-
- The `NSString` class may place additional restrictions or otherwise
- transform the JSON String in such a way so that the JSON String is not
- bijective with the instantiated `NSString` object. In other words, JSONKit
- can not guarantee that when you round trip a JSON String to a `NSString`
- and then back to a JSON String that the two JSON Strings will be exactly
- the same, even though in practice they are. For clarity, "exactly" in
- this case means bit for bit identical. JSONKit can not even guarantee
- that the two JSON Strings will be
- [Unicode equivalent][Unicode_equivalence], even though in practice they
- will be and would be the most likely cause for the two round tripped JSON
- Strings to no longer be bit for bit identical.
-
-* JSONKit maps `true` and `false` to the [`CFBoolean`][CFBoolean] values
- [`kCFBooleanTrue`][kCFBooleanTrue] and
- [`kCFBooleanFalse`][kCFBooleanFalse], respectively. Conceptually,
- `CFBoolean` values can be thought of, and treated as, `NSNumber` class
- objects. The benefit to using `CFBoolean` is that `true` and `false`
- JSON values can be round trip deserialized and serialized without
- conversion or promotion to a `NSNumber` with a value of `0` or `1`.
-
-* The [JSON specification][RFC 4627] does not specify the details or
- requirements for JSON Number values, nor does it specify how errors
- due to conversion should be handled. In general, JSONKit will not accept
- JSON that contains JSON Number values that it can not convert with out
- error or loss of precision.
-
- For non-floating-point numbers (i.e., JSON Number values that do not
- include a `.` or `e|E`), JSONKit uses a 64-bit C primitive type
- internally, regardless of whether the target architecture is 32-bit or
- 64-bit. For unsigned values (i.e., those that do not begin with a
- `-`), this allows for values up to <code>2<sup>64</sup>-1</code>, and up
- to <code>-2<sup>63</sup></code> for negative values. As a special case,
- the JSON Number `-0` is treated as a floating-point number since the
- underlying floating-point primitive type is capable of representing a
- negative zero, whereas the underlying twos-complement non-floating-point
- primitive type can not. JSON that contains Number values that exceed
- these limits will fail to parse and optionally return a `NSError` object.
- The functions `strtoll()` and `strtoull()` are used to perform the
- conversions.
-
- The C `double` primitive type, or IEEE 754 Double 64-bit floating-point,
- is used to represent floating-point JSON Number values. JSON that
- contains floating-point Number values that can not be represented as a
- `double` (i.e., due to over or underflow) will fail to parse and
- optionally return a `NSError` object. The function `strtod()` is used to
- perform the conversion. Note that the JSON standard does not allow for
- infinities or `NaN` (Not a Number).
-
-* For JSON Associative Arrays (or `object` in [RFC 4627][] nomenclature),
- [RFC 4627][] says `The names within an object SHOULD be unique`
- (note: `name` is a `key` in JSONKit nomenclature). At this time the
- JSONKit behavior is `undefined` for JSON that contains names within an
- object that are not unique. However, JSONKit currently tries to
- follow a "the last key / value pair parsed is the one chosen" policy.
- This behavior is not finalized and should not be depended on.
+* For JSON Associative Arrays (or `object` in [RFC 4627][] nomenclature), [RFC 4627][] says `The names within an object SHOULD be unique` (note: `name` is a `key` in JSONKit nomenclature). At this time the JSONKit behavior is `undefined` for JSON that contains names within an object that are not unique. However, JSONKit currently tries to follow a "the last key / value pair parsed is the one chosen" policy. This behavior is not finalized and should not be depended on.
### Objective-C To JSON Primitive Mapping Details
-* The `NSDictionary` class allows for any object, which can be of any class,
- to be used as a `key`. JSON, however, only permits Strings to be used as
- `keys`. Therefore JSONKit will fail with an error if it encounters a
- `NSDictionary` that contains keys that are not `NSString` objects during
- serialization.
+* The [`NSDictionary`][NSDictionary] class allows for any object, which can be of any class, to be used as a `key`. JSON, however, only permits Strings to be used as `keys`. Therefore JSONKit will fail with an error if it encounters a [`NSDictionary`][NSDictionary] that contains keys that are not [`NSString`][NSString] objects during serialization.
+
+* JSON does not allow for Numbers that are <code>&plusmn;Infinity</code> or <code>&plusmn;NaN</code>. Therefore JSONKit will fail with an error if it encounters a [`NSNumber`][NSNumber] that contains such a value during serialization.
+
+* JSONKit will fail with an error if it encounters an object that is not a [`NSNull`][NSNull], [`NSNumber`][NSNumber], [`NSString`][NSString], [`NSArray`][NSArray], or [`NSDictionary`][NSDictionary] class object during serialization.
+
+* Objects created with [`[NSNumber numberWithBool:YES]`][NSNumber_numberWithBool] and [`[NSNumber numberWithBool:NO]`][NSNumber_numberWithBool] will be mapped to the JSON values of `true` and `false`, respectively.
+
+### Important Details
+
+* JSONKit is not designed to be used with the Mac OS X Garbage Collection. The behavior of JSONKit when compiled with `-fobj-gc` is `undefined`. It is extremely unlikely that Mac OS X Garbage Collection will ever be supported.
+
+* The JSON to be parsed by JSONKit must be encoded as Unicode. In the unlikely event you end up with JSON that is not encoded as Unicode, you must first convert the JSON to Unicode, preferably as `UTF8`. One way to accomplish this is with the [`NSString`][NSString] methods [`-initWithBytes:length:encoding:`][NSString_initWithBytes] and [`-initWithData:encoding:`][NSString_initWithData].
+
+* Internally, the low level parsing engine uses `UTF8` exclusively. The `JSONDecoder` method `-parseJSONData:` takes a [`NSData`][NSData] object as its argument and it is assumed that the raw bytes contained in the [`NSData`][NSData] is `UTF8` encoded, otherwise the behavior is `undefined`.
+
+* It is not safe to use the same instantiated `JSONDecoder` object from multiple threads at the same time. If you wish to share a `JSONDecoder` between threads, you are responsible for adding mutex barriers to ensure that only one thread is decoding JSON using the shared `JSONDecoder` object at a time.
+
+### Tips for speed
+
+* Enable the `NS_BLOCK_ASSERTIONS` pre-processor flag. JSONKit makes heavy use of [`NSCParameterAssert()`][NSCParameterAssert] internally to ensure that various arguments, variables, and other state contains only legal and expected values. If an assertion check fails it causes a run time exception that will normally cause a program to terminate. These checks and assertions come with a price: they take time to execute and do not contribute to the work being performed. While your mileage may vary, the author has found that adding `-DNS_BLOCK_ASSERTIONS` to an `-O2` optimization setting can generally result in an approximate <span style="white-space: nowrap;">7-12%</span> increase in performance.
+
+* Since the very low level parsing engine works exclusively with `UTF8` byte streams, anything that is not already encoded as `UTF8` must first be converted to `UTF8`. While JSONKit provides additions to the [`NSString`][NSString] class which allows you to conveniently convert JSON contained in a [`NSString`][NSString], this convenience does come with a price. JSONKit uses the [`-UTF8String`][NSString_UTF8String] method to obtain a `UTF8` encoded version of a [`NSString`][NSString], and while the details of how a strings performs that conversion are an internal implementation detail, it is likely that this conversion carries a cost both in terms of time and the memory needed to store the conversion result. Therefore, if speed is a priority, you should avoid using the [`NSString`][NSString] convenience methods if possible.
+
+* If you are receiving JSON data from a web server, and you are able to determine that the raw bytes returned by the web server is JSON encoded as `UTF8`, you should use the `JSONDecoder` method `-parseUTF8String:length:` which immediately begins parsing the pointers bytes. In practice, every JSONKit method that converts JSON to an Objective-C object eventually calls this method to perform the conversion.
-* JSON does not allow for Numbers that are <code>&plusmn;Infinity</code> or
- <code>&plusmn;NaN</code>. Therefore JSONKit will fail with an error if
- it encounters a `NSNumber` that contains such a value during serialization.
+* If you are using one of the various ways provided by the `NSURL` family of classes to receive JSON results from a web server, which typically return the results in the form of a [`NSData`][NSData] object, and you are able to determine that the raw bytes contained in the [`NSData`][NSData] are encoded as `UTF8`, then you should use either the `JSONDecoder` method `parseJSONData:` or the [`NSData`][NSData] method `-objectFromJSONData`. If are going to be converting a lot of JSON, the better choice is to instantiate a `JSONDecoder` object once and use the same instantiated object to perform all your conversions. This has two benefits:
+ 1. The [`NSData`][NSData] method `-objectFromJSONData` creates an autoreleased `JSONDecoder` object to perform the one time conversion. By instantiating a `JSONDecoder` object once and using the `parseJSONData:` method repeatedly, you can avoid this overhead.
+ 2. The instantiated object cache from the previous JSON conversion is reused. This can result in both better performance and a reduction in memory usage if the JSON your are converting is very similar. A typical example might be if you are converting JSON at periodic intervals that consists of real time status updates.
-* JSONKit will fail with an error if it encounters an object that is not a
- `NSNull`, `NSNumber`, `NSString`, `NSArray`, or `NSDictionary` class
- object during serialization.
+### Parsing Interface
-* Objects created with `[NSNumber numberWithBool:YES]` and
- `[NSNumber numberWithBool:NO]` will be mapped to the JSON values of
- `true` and `false`, respectively.
+#### JSONDecoder Interface
+
+**Note:** The bytes contained in a [`NSData`][NSData] object ***must*** be `UTF8` encoded.
+
+**Important:** Methods will raise [`NSInvalidArgumentException`][NSInvalidArgumentException] if `parseOptionFlags` is not valid.
+**Important:** `parseUTF8String:` will raise [`NSInvalidArgumentException`][NSInvalidArgumentException] if `parseUTF8String` is NULL.
+**Important:** `parseJSONData:` will raise [`NSInvalidArgumentException`][NSInvalidArgumentException] if `jsonData` is NULL.
+
+<pre>+ (id)decoder;
++ (id)decoderWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
+- (id)initWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
+
+- (void)clearCache;
+
+- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length;
+- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length error:(NSError **)error;
+
+- (id)parseJSONData:(NSData *)jsonData;
+- (id)parseJSONData:(NSData *)jsonData error:(NSError **)error;</pre>
+
+#### NSString Interface
+
+<pre>- (id)objectFromJSONString;
+- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
+- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;</pre>
+
+#### NSData Interface
+
+<pre>- (id)objectFromJSONData;
+- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
+- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;</pre>
+
+#### JKParseOptionFlags
+
+<table>
+ <tr><th>Parsing Option</th><th>Description</th></tr>
+ <tr><td><code>JKParseOptionNone</code></td><td>This is the default if no other other parse option flags are specified, and the option used when a convenience method does not provide an argument for explicitly specifying the parse options to use. Synonymous with <code>JKParseOptionStrict</code>.</td></tr>
+ <tr><td><code>JKParseOptionStrict</code></td><td>The JSON will be parsed in strict accordance with the <a href="http://tools.ietf.org/html/rfc4627">RFC 4627</a> specification.</td></tr>
+ <tr><td><code>JKParseOptionComments</code></td><td>Allow C style <code>//</code> and <code>/* &hellip; */</code> comments in JSON. This is a fairly common extension to JSON, but JSON that contains C style comments is not strictly conforming JSON.</td></tr>
+ <tr><td><code>JKParseOptionUnicodeNewlines</code></td><td>Allow Unicode recommended <code>(?:\r\n|[\n\v\f\r\x85\p{Zl}\p{Zp}])</code> newlines in JSON. The <a href="http://tools.ietf.org/html/rfc4627">JSON specification</a> only allows the newline characters <code>\r</code> and <code>\n</code>, but this option allows JSON that contains the <a href="http://en.wikipedia.org/wiki/Newline#Unicode">Unicode recommended newline characters</a> to be parsed. JSON that contains these additional newline characters is not strictly conforming JSON.</td></tr>
+ <tr><td><code>JKParseOptionLooseUnicode</code></td><td>Normally the decoder will stop with an error at any malformed Unicode. This option allows JSON with malformed Unicode to be parsed without reporting an error. Any malformed Unicode is replaced with <code>\uFFFD</code>, or <code>REPLACEMENT CHARACTER</code>, as specified in <a href="http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf">The Unicode 5.2 standard, Chapter 3</a>, section 3.9 <em>Unicode Encoding Forms</em>.</td></tr>
+ <tr><td><code>JKParseOptionPermitTextAfterValidJSON</code></td><td>Normally, <code>white-space</code> that follows the JSON is interpreted as a parsing failure. This option allows for any trailing <code>white-space</code> to be ignored and not cause a parsing error.</td></tr>
+</table>
+
+### Serializing Interface
+
+**Note:** The bytes contained in the returned [`NSData`][NSData] object is `UTF8` encoded.
+
+#### NSArray and NSDictionary Interface
+
+<pre>- (NSData *)JSONData;
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
+- (NSString *)JSONString;
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;</pre>
+
+#### JKSerializeOptionFlags
+
+<table>
+ <tr><th>Serializing Option</th><th>Description</th></tr>
+ <tr><td><code>JKSerializeOptionNone</code></td><td>This is the default if no other other serialize option flags are specified, and the option used when a convenience method does not provide an argument for explicitly specifying the serialize options to use.</td></tr>
+ <tr><td><code>JKSerializeOptionEscapeUnicode</code></td><td>When JSONKit encounters Unicode characters in <a href="http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html"><code>NSString</code></a> objects, the default behavior is to encode those Unicode characters as <code>UTF8</code>. This option causes JSONKit to encode those characters as <code>\uXXXX</code>. For example,<br/><code>["w&isin;L&#10234;y(&#8739;y&#8739;&le;&#8739;w&#8739;)"]</code><br/>becomes:<br/><code>["w\u2208L\u27fa\u2203y(\u2223y\u2223\u2264\u2223w\u2223)"]</code></td></tr>
+</table>
[JSON]: http://www.json.org/
[RFC 4627]: http://tools.ietf.org/html/rfc4627
@@ -148,3 +160,21 @@ Foundation &equiv; Foundation for every equivalent base type, i.e. `CFString`
[kCFBooleanFalse]: http://developer.apple.com/mac/library/documentation/CoreFoundation/Reference/CFBooleanRef/Reference/reference.html#//apple_ref/doc/c_ref/kCFBooleanFalse
[Unicode_CH3]: http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf
[Unicode_equivalence]: http://en.wikipedia.org/wiki/Unicode_equivalence
+[NSNull]: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSNull_Class/index.html
+[NSNumber]: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/index.html
+[NSNumber_numberWithBool]: http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/Reference/Reference.html#//apple_ref/occ/clm/NSNumber/numberWithBool:
+[NSString]: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html
+[NSString_initWithBytes]: http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/initWithBytes:length:encoding:
+[NSString_initWithData]: http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/initWithData:encoding:
+[NSString_UTF8String]: http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/UTF8String
+[NSArray]: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/index.html
+[NSDictionary]: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/index.html
+[NSError]: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/index.html
+[NSData]: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/index.html
+[NSInvalidArgumentException]: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/Reference/reference.html#//apple_ref/doc/c_ref/NSInvalidArgumentException
+[CFString]: http://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFStringRef/Reference/reference.html
+[strtoll]: http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/strtoll.3.html
+[strtod]: http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/strtod.3.html
+[strtoull]: http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/strtoull.3.html
+[NSCParameterAssert]: http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/macro/NSCParameterAssert
+[UnicodeNewline]: http://en.wikipedia.org/wiki/Newline#Unicode

0 comments on commit 8339b27

Please sign in to comment.