@@ -0,0 +1,3065 @@
1+//
2+// JSONKit.m
3+// http://github.com/johnezang/JSONKit
4+// Dual licensed under either the terms of the BSD License, or alternatively
5+// under the terms of the Apache License, Version 2.0, as specified below.
6+//
7+
8+/*
9+ Copyright (c) 2011, John Engelhart
10+
11+ All rights reserved.
12+
13+ Redistribution and use in source and binary forms, with or without
14+ modification, are permitted provided that the following conditions are met:
15+
16+ * Redistributions of source code must retain the above copyright
17+ notice, this list of conditions and the following disclaimer.
18+
19+ * Redistributions in binary form must reproduce the above copyright
20+ notice, this list of conditions and the following disclaimer in the
21+ documentation and/or other materials provided with the distribution.
22+
23+ * Neither the name of the Zang Industries nor the names of its
24+ contributors may be used to endorse or promote products derived from
25+ this software without specific prior written permission.
26+
27+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
33+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38+*/
39+
40+/*
41+ Copyright 2011 John Engelhart
42+
43+ Licensed under the Apache License, Version 2.0 (the "License");
44+ you may not use this file except in compliance with the License.
45+ You may obtain a copy of the License at
46+
47+ http://www.apache.org/licenses/LICENSE-2.0
48+
49+ Unless required by applicable law or agreed to in writing, software
50+ distributed under the License is distributed on an "AS IS" BASIS,
51+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
52+ See the License for the specific language governing permissions and
53+ limitations under the License.
54+*/
55+
56+
57+/*
58+ Acknowledgments:
59+
60+ The bulk of the UTF8 / UTF32 conversion and verification comes
61+ from ConvertUTF.[hc]. It has been modified from the original sources.
62+
63+ The original sources were obtained from http://www.unicode.org/.
64+ However, the web site no longer seems to host the files. Instead,
65+ the Unicode FAQ http://www.unicode.org/faq//utf_bom.html#gen4
66+ points to International Components for Unicode (ICU)
67+ http://site.icu-project.org/ as an example of how to write a UTF
68+ converter.
69+
70+ The decision to use the ConvertUTF.[ch] code was made to leverage
71+ "proven" code. Hopefully the local modifications are bug free.
72+
73+ The code in isValidCodePoint() is derived from the ICU code in
74+ utf.h for the macros U_IS_UNICODE_NONCHAR and U_IS_UNICODE_CHAR.
75+
76+ From the original ConvertUTF.[ch]:
77+
78+ * Copyright 2001-2004 Unicode, Inc.
79+ *
80+ * Disclaimer
81+ *
82+ * This source code is provided as is by Unicode, Inc. No claims are
83+ * made as to fitness for any particular purpose. No warranties of any
84+ * kind are expressed or implied. The recipient agrees to determine
85+ * applicability of information provided. If this file has been
86+ * purchased on magnetic or optical media from Unicode, Inc., the
87+ * sole remedy for any claim will be exchange of defective media
88+ * within 90 days of receipt.
89+ *
90+ * Limitations on Rights to Redistribute This Code
91+ *
92+ * Unicode, Inc. hereby grants the right to freely use the information
93+ * supplied in this file in the creation of products supporting the
94+ * Unicode Standard, and to make copies of this file in any form
95+ * for internal or external distribution as long as this notice
96+ * remains attached.
97+
98+*/
99+
100+#include <stdio.h>
101+#include <stdlib.h>
102+#include <stdint.h>
103+#include <string.h>
104+#include <assert.h>
105+#include <sys/errno.h>
106+#include <math.h>
107+#include <limits.h>
108+#include <objc/runtime.h>
109+
110+#import "JSONKit.h"
111+
112+//#include <CoreFoundation/CoreFoundation.h>
113+#include <CoreFoundation/CFString.h>
114+#include <CoreFoundation/CFArray.h>
115+#include <CoreFoundation/CFDictionary.h>
116+#include <CoreFoundation/CFNumber.h>
117+
118+//#import <Foundation/Foundation.h>
119+#import <Foundation/NSArray.h>
120+#import <Foundation/NSAutoreleasePool.h>
121+#import <Foundation/NSData.h>
122+#import <Foundation/NSDictionary.h>
123+#import <Foundation/NSException.h>
124+#import <Foundation/NSNull.h>
125+#import <Foundation/NSObjCRuntime.h>
126+
127+#ifndef __has_feature
128+#define __has_feature(x) 0
129+#endif
130+
131+#ifdef JK_ENABLE_CF_TRANSFER_OWNERSHIP_CALLBACKS
132+#warning As of JSONKit v1.4, JK_ENABLE_CF_TRANSFER_OWNERSHIP_CALLBACKS is no longer required. It is no longer a valid option.
133+#endif
134+
135+#ifdef __OBJC_GC__
136+#error JSONKit does not support Objective-C Garbage Collection
137+#endif
138+
139+#if __has_feature(objc_arc)
140+#error JSONKit does not support Objective-C Automatic Reference Counting (ARC)
141+#endif
142+
143+// The following checks are really nothing more than sanity checks.
144+// JSONKit technically has a few problems from a "strictly C99 conforming" standpoint, though they are of the pedantic nitpicking variety.
145+// In practice, though, for the compilers and architectures we can reasonably expect this code to be compiled for, these pedantic nitpicks aren't really a problem.
146+// Since we're limited as to what we can do with pre-processor #if checks, these checks are not nearly as through as they should be.
147+
148+#if (UINT_MAX != 0xffffffffU) || (INT_MIN != (-0x7fffffff-1)) || (ULLONG_MAX != 0xffffffffffffffffULL) || (LLONG_MIN != (-0x7fffffffffffffffLL-1LL))
149+#error JSONKit requires the C 'int' and 'long long' types to be 32 and 64 bits respectively.
150+#endif
151+
152+#if !defined(__LP64__) && ((UINT_MAX != ULONG_MAX) || (INT_MAX != LONG_MAX) || (INT_MIN != LONG_MIN) || (WORD_BIT != LONG_BIT))
153+#error JSONKit requires the C 'int' and 'long' types to be the same on 32-bit architectures.
154+#endif
155+
156+// Cocoa / Foundation uses NS*Integer as the type for a lot of arguments. We make sure that NS*Integer is something we are expecting and is reasonably compatible with size_t / ssize_t
157+
158+#if (NSUIntegerMax != ULONG_MAX) || (NSIntegerMax != LONG_MAX) || (NSIntegerMin != LONG_MIN)
159+#error JSONKit requires NSInteger and NSUInteger to be the same size as the C 'long' type.
160+#endif
161+
162+#if (NSUIntegerMax != SIZE_MAX) || (NSIntegerMax != SSIZE_MAX)
163+#error JSONKit requires NSInteger and NSUInteger to be the same size as the C 'size_t' type.
164+#endif
165+
166+
167+// For DJB hash.
168+#define JK_HASH_INIT (1402737925UL)
169+
170+// Use __builtin_clz() instead of trailingBytesForUTF8[] table lookup.
171+#define JK_FAST_TRAILING_BYTES
172+
173+// JK_CACHE_SLOTS must be a power of 2. Default size is 1024 slots.
174+#define JK_CACHE_SLOTS_BITS (10)
175+#define JK_CACHE_SLOTS (1UL << JK_CACHE_SLOTS_BITS)
176+// JK_CACHE_PROBES is the number of probe attempts.
177+#define JK_CACHE_PROBES (4UL)
178+// JK_INIT_CACHE_AGE must be < (1 << AGE) - 1, where AGE is sizeof(typeof(AGE)) * 8.
179+#define JK_INIT_CACHE_AGE (0)
180+
181+// JK_TOKENBUFFER_SIZE is the default stack size for the temporary buffer used to hold "non-simple" strings (i.e., contains \ escapes)
182+#define JK_TOKENBUFFER_SIZE (1024UL * 2UL)
183+
184+// 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.
185+#define JK_STACK_OBJS (1024UL * 1UL)
186+
187+#define JK_JSONBUFFER_SIZE (1024UL * 4UL)
188+#define JK_UTF8BUFFER_SIZE (1024UL * 16UL)
189+
190+#define JK_ENCODE_CACHE_SLOTS (1024UL)
191+
192+
193+#if defined (__GNUC__) && (__GNUC__ >= 4)
194+#define JK_ATTRIBUTES(attr, ...) __attribute__((attr, ##__VA_ARGS__))
195+#define JK_EXPECTED(cond, expect) __builtin_expect((long)(cond), (expect))
196+#define JK_EXPECT_T(cond) JK_EXPECTED(cond, 1U)
197+#define JK_EXPECT_F(cond) JK_EXPECTED(cond, 0U)
198+#define JK_PREFETCH(ptr) __builtin_prefetch(ptr)
199+#else // defined (__GNUC__) && (__GNUC__ >= 4)
200+#define JK_ATTRIBUTES(attr, ...)
201+#define JK_EXPECTED(cond, expect) (cond)
202+#define JK_EXPECT_T(cond) (cond)
203+#define JK_EXPECT_F(cond) (cond)
204+#define JK_PREFETCH(ptr)
205+#endif // defined (__GNUC__) && (__GNUC__ >= 4)
206+
207+#define JK_STATIC_INLINE static __inline__ JK_ATTRIBUTES(always_inline)
208+#define JK_ALIGNED(arg) JK_ATTRIBUTES(aligned(arg))
209+#define JK_UNUSED_ARG JK_ATTRIBUTES(unused)
210+#define JK_WARN_UNUSED JK_ATTRIBUTES(warn_unused_result)
211+#define JK_WARN_UNUSED_CONST JK_ATTRIBUTES(warn_unused_result, const)
212+#define JK_WARN_UNUSED_PURE JK_ATTRIBUTES(warn_unused_result, pure)
213+#define JK_WARN_UNUSED_SENTINEL JK_ATTRIBUTES(warn_unused_result, sentinel)
214+#define JK_NONNULL_ARGS(arg, ...) JK_ATTRIBUTES(nonnull(arg, ##__VA_ARGS__))
215+#define JK_WARN_UNUSED_NONNULL_ARGS(arg, ...) JK_ATTRIBUTES(warn_unused_result, nonnull(arg, ##__VA_ARGS__))
216+#define JK_WARN_UNUSED_CONST_NONNULL_ARGS(arg, ...) JK_ATTRIBUTES(warn_unused_result, const, nonnull(arg, ##__VA_ARGS__))
217+#define JK_WARN_UNUSED_PURE_NONNULL_ARGS(arg, ...) JK_ATTRIBUTES(warn_unused_result, pure, nonnull(arg, ##__VA_ARGS__))
218+
219+#if defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
220+#define JK_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(as, nn, ...) JK_ATTRIBUTES(warn_unused_result, nonnull(nn, ##__VA_ARGS__), alloc_size(as))
221+#else // defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
222+#define JK_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(as, nn, ...) JK_ATTRIBUTES(warn_unused_result, nonnull(nn, ##__VA_ARGS__))
223+#endif // defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
224+
225+
226+@class JKArray, JKDictionaryEnumerator, JKDictionary;
227+
228+enum {
229+ JSONNumberStateStart = 0,
230+ JSONNumberStateFinished = 1,
231+ JSONNumberStateError = 2,
232+ JSONNumberStateWholeNumberStart = 3,
233+ JSONNumberStateWholeNumberMinus = 4,
234+ JSONNumberStateWholeNumberZero = 5,
235+ JSONNumberStateWholeNumber = 6,
236+ JSONNumberStatePeriod = 7,
237+ JSONNumberStateFractionalNumberStart = 8,
238+ JSONNumberStateFractionalNumber = 9,
239+ JSONNumberStateExponentStart = 10,
240+ JSONNumberStateExponentPlusMinus = 11,
241+ JSONNumberStateExponent = 12,
242+};
243+
244+enum {
245+ JSONStringStateStart = 0,
246+ JSONStringStateParsing = 1,
247+ JSONStringStateFinished = 2,
248+ JSONStringStateError = 3,
249+ JSONStringStateEscape = 4,
250+ JSONStringStateEscapedUnicode1 = 5,
251+ JSONStringStateEscapedUnicode2 = 6,
252+ JSONStringStateEscapedUnicode3 = 7,
253+ JSONStringStateEscapedUnicode4 = 8,
254+ JSONStringStateEscapedUnicodeSurrogate1 = 9,
255+ JSONStringStateEscapedUnicodeSurrogate2 = 10,
256+ JSONStringStateEscapedUnicodeSurrogate3 = 11,
257+ JSONStringStateEscapedUnicodeSurrogate4 = 12,
258+ JSONStringStateEscapedNeedEscapeForSurrogate = 13,
259+ JSONStringStateEscapedNeedEscapedUForSurrogate = 14,
260+};
261+
262+enum {
263+ JKParseAcceptValue = (1 << 0),
264+ JKParseAcceptComma = (1 << 1),
265+ JKParseAcceptEnd = (1 << 2),
266+ JKParseAcceptValueOrEnd = (JKParseAcceptValue | JKParseAcceptEnd),
267+ JKParseAcceptCommaOrEnd = (JKParseAcceptComma | JKParseAcceptEnd),
268+};
269+
270+enum {
271+ JKClassUnknown = 0,
272+ JKClassString = 1,
273+ JKClassNumber = 2,
274+ JKClassArray = 3,
275+ JKClassDictionary = 4,
276+ JKClassNull = 5,
277+};
278+
279+enum {
280+ JKManagedBufferOnStack = 1,
281+ JKManagedBufferOnHeap = 2,
282+ JKManagedBufferLocationMask = (0x3),
283+ JKManagedBufferLocationShift = (0),
284+
285+ JKManagedBufferMustFree = (1 << 2),
286+};
287+typedef JKFlags JKManagedBufferFlags;
288+
289+enum {
290+ JKObjectStackOnStack = 1,
291+ JKObjectStackOnHeap = 2,
292+ JKObjectStackLocationMask = (0x3),
293+ JKObjectStackLocationShift = (0),
294+
295+ JKObjectStackMustFree = (1 << 2),
296+};
297+typedef JKFlags JKObjectStackFlags;
298+
299+enum {
300+ JKTokenTypeInvalid = 0,
301+ JKTokenTypeNumber = 1,
302+ JKTokenTypeString = 2,
303+ JKTokenTypeObjectBegin = 3,
304+ JKTokenTypeObjectEnd = 4,
305+ JKTokenTypeArrayBegin = 5,
306+ JKTokenTypeArrayEnd = 6,
307+ JKTokenTypeSeparator = 7,
308+ JKTokenTypeComma = 8,
309+ JKTokenTypeTrue = 9,
310+ JKTokenTypeFalse = 10,
311+ JKTokenTypeNull = 11,
312+ JKTokenTypeWhiteSpace = 12,
313+};
314+typedef NSUInteger JKTokenType;
315+
316+// These are prime numbers to assist with hash slot probing.
317+enum {
318+ JKValueTypeNone = 0,
319+ JKValueTypeString = 5,
320+ JKValueTypeLongLong = 7,
321+ JKValueTypeUnsignedLongLong = 11,
322+ JKValueTypeDouble = 13,
323+};
324+typedef NSUInteger JKValueType;
325+
326+enum {
327+ JKEncodeOptionAsData = 1,
328+ JKEncodeOptionAsString = 2,
329+ JKEncodeOptionAsTypeMask = 0x7,
330+ JKEncodeOptionCollectionObj = (1 << 3),
331+ JKEncodeOptionStringObj = (1 << 4),
332+ JKEncodeOptionStringObjTrimQuotes = (1 << 5),
333+
334+};
335+typedef NSUInteger JKEncodeOptionType;
336+
337+typedef NSUInteger JKHash;
338+
339+typedef struct JKTokenCacheItem JKTokenCacheItem;
340+typedef struct JKTokenCache JKTokenCache;
341+typedef struct JKTokenValue JKTokenValue;
342+typedef struct JKParseToken JKParseToken;
343+typedef struct JKPtrRange JKPtrRange;
344+typedef struct JKObjectStack JKObjectStack;
345+typedef struct JKBuffer JKBuffer;
346+typedef struct JKConstBuffer JKConstBuffer;
347+typedef struct JKConstPtrRange JKConstPtrRange;
348+typedef struct JKRange JKRange;
349+typedef struct JKManagedBuffer JKManagedBuffer;
350+typedef struct JKFastClassLookup JKFastClassLookup;
351+typedef struct JKEncodeCache JKEncodeCache;
352+typedef struct JKEncodeState JKEncodeState;
353+typedef struct JKObjCImpCache JKObjCImpCache;
354+typedef struct JKHashTableEntry JKHashTableEntry;
355+
356+typedef id (*NSNumberAllocImp)(id receiver, SEL selector);
357+typedef id (*NSNumberInitWithUnsignedLongLongImp)(id receiver, SEL selector, unsigned long long value);
358+typedef id (*JKClassFormatterIMP)(id receiver, SEL selector, id object);
359+#ifdef __BLOCKS__
360+typedef id (^JKClassFormatterBlock)(id formatObject);
361+#endif
362+
363+
364+struct JKPtrRange {
365+ unsigned char *ptr;
366+ size_t length;
367+};
368+
369+struct JKConstPtrRange {
370+ const unsigned char *ptr;
371+ size_t length;
372+};
373+
374+struct JKRange {
375+ size_t location, length;
376+};
377+
378+struct JKManagedBuffer {
379+ JKPtrRange bytes;
380+ JKManagedBufferFlags flags;
381+ size_t roundSizeUpToMultipleOf;
382+};
383+
384+struct JKObjectStack {
385+ void **objects, **keys;
386+ CFHashCode *cfHashes;
387+ size_t count, index, roundSizeUpToMultipleOf;
388+ JKObjectStackFlags flags;
389+};
390+
391+struct JKBuffer {
392+ JKPtrRange bytes;
393+};
394+
395+struct JKConstBuffer {
396+ JKConstPtrRange bytes;
397+};
398+
399+struct JKTokenValue {
400+ JKConstPtrRange ptrRange;
401+ JKValueType type;
402+ JKHash hash;
403+ union {
404+ long long longLongValue;
405+ unsigned long long unsignedLongLongValue;
406+ double doubleValue;
407+ } number;
408+ JKTokenCacheItem *cacheItem;
409+};
410+
411+struct JKParseToken {
412+ JKConstPtrRange tokenPtrRange;
413+ JKTokenType type;
414+ JKTokenValue value;
415+ JKManagedBuffer tokenBuffer;
416+};
417+
418+struct JKTokenCacheItem {
419+ void *object;
420+ JKHash hash;
421+ CFHashCode cfHash;
422+ size_t size;
423+ unsigned char *bytes;
424+ JKValueType type;
425+};
426+
427+struct JKTokenCache {
428+ JKTokenCacheItem *items;
429+ size_t count;
430+ unsigned int prng_lfsr;
431+ unsigned char age[JK_CACHE_SLOTS];
432+};
433+
434+struct JKObjCImpCache {
435+ Class NSNumberClass;
436+ NSNumberAllocImp NSNumberAlloc;
437+ NSNumberInitWithUnsignedLongLongImp NSNumberInitWithUnsignedLongLong;
438+};
439+
440+struct JKParseState {
441+ JKParseOptionFlags parseOptionFlags;
442+ JKConstBuffer stringBuffer;
443+ size_t atIndex, lineNumber, lineStartIndex;
444+ size_t prev_atIndex, prev_lineNumber, prev_lineStartIndex;
445+ JKParseToken token;
446+ JKObjectStack objectStack;
447+ JKTokenCache cache;
448+ JKObjCImpCache objCImpCache;
449+ NSError *error;
450+ int errorIsPrev;
451+ BOOL mutableCollections;
452+};
453+
454+struct JKFastClassLookup {
455+ void *stringClass;
456+ void *numberClass;
457+ void *arrayClass;
458+ void *dictionaryClass;
459+ void *nullClass;
460+};
461+
462+struct JKEncodeCache {
463+ id object;
464+ size_t offset;
465+ size_t length;
466+};
467+
468+struct JKEncodeState {
469+ JKManagedBuffer utf8ConversionBuffer;
470+ JKManagedBuffer stringBuffer;
471+ size_t atIndex;
472+ JKFastClassLookup fastClassLookup;
473+ JKEncodeCache cache[JK_ENCODE_CACHE_SLOTS];
474+ JKSerializeOptionFlags serializeOptionFlags;
475+ JKEncodeOptionType encodeOption;
476+ size_t depth;
477+ NSError *error;
478+ id classFormatterDelegate;
479+ SEL classFormatterSelector;
480+ JKClassFormatterIMP classFormatterIMP;
481+#ifdef __BLOCKS__
482+ JKClassFormatterBlock classFormatterBlock;
483+#endif
484+};
485+
486+// This is a JSONKit private class.
487+@interface JKSerializer : NSObject {
488+ JKEncodeState *encodeState;
489+}
490+
491+#ifdef __BLOCKS__
492+#define JKSERIALIZER_BLOCKS_PROTO id(^)(id object)
493+#else
494+#define JKSERIALIZER_BLOCKS_PROTO id
495+#endif
496+
497++ (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
498+- (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
499+- (void)releaseState;
500+
501+@end
502+
503+struct JKHashTableEntry {
504+ NSUInteger keyHash;
505+ id key, object;
506+};
507+
508+
509+typedef uint32_t UTF32; /* at least 32 bits */
510+typedef uint16_t UTF16; /* at least 16 bits */
511+typedef uint8_t UTF8; /* typically 8 bits */
512+
513+typedef enum {
514+ conversionOK, /* conversion successful */
515+ sourceExhausted, /* partial character in source, but hit end */
516+ targetExhausted, /* insuff. room in target for conversion */
517+ sourceIllegal /* source sequence is illegal/malformed */
518+} ConversionResult;
519+
520+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
521+#define UNI_MAX_BMP (UTF32)0x0000FFFF
522+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
523+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
524+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
525+#define UNI_SUR_HIGH_START (UTF32)0xD800
526+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
527+#define UNI_SUR_LOW_START (UTF32)0xDC00
528+#define UNI_SUR_LOW_END (UTF32)0xDFFF
529+
530+
531+#if !defined(JK_FAST_TRAILING_BYTES)
532+static const char trailingBytesForUTF8[256] = {
533+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
534+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
535+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
536+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
537+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
538+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
539+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
540+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
541+};
542+#endif
543+
544+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
545+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
546+
547+#define JK_AT_STRING_PTR(x) (&((x)->stringBuffer.bytes.ptr[(x)->atIndex]))
548+#define JK_END_STRING_PTR(x) (&((x)->stringBuffer.bytes.ptr[(x)->stringBuffer.bytes.length]))
549+
550+
551+static JKArray *_JKArrayCreate(id *objects, NSUInteger count, BOOL mutableCollection);
552+static void _JKArrayInsertObjectAtIndex(JKArray *array, id newObject, NSUInteger objectIndex);
553+static void _JKArrayReplaceObjectAtIndexWithObject(JKArray *array, NSUInteger objectIndex, id newObject);
554+static void _JKArrayRemoveObjectAtIndex(JKArray *array, NSUInteger objectIndex);
555+
556+
557+static NSUInteger _JKDictionaryCapacityForCount(NSUInteger count);
558+static JKDictionary *_JKDictionaryCreate(id *keys, NSUInteger *keyHashes, id *objects, NSUInteger count, BOOL mutableCollection);
559+static JKHashTableEntry *_JKDictionaryHashEntry(JKDictionary *dictionary);
560+static NSUInteger _JKDictionaryCapacity(JKDictionary *dictionary);
561+static void _JKDictionaryResizeIfNeccessary(JKDictionary *dictionary);
562+static void _JKDictionaryRemoveObjectWithEntry(JKDictionary *dictionary, JKHashTableEntry *entry);
563+static void _JKDictionaryAddObject(JKDictionary *dictionary, NSUInteger keyHash, id key, id object);
564+static JKHashTableEntry *_JKDictionaryHashTableEntryForKey(JKDictionary *dictionary, id aKey);
565+
566+
567+static void _JSONDecoderCleanup(JSONDecoder *decoder);
568+
569+static id _NSStringObjectFromJSONString(NSString *jsonString, JKParseOptionFlags parseOptionFlags, NSError **error, BOOL mutableCollection);
570+
571+
572+static void jk_managedBuffer_release(JKManagedBuffer *managedBuffer);
573+static void jk_managedBuffer_setToStackBuffer(JKManagedBuffer *managedBuffer, unsigned char *ptr, size_t length);
574+static unsigned char *jk_managedBuffer_resize(JKManagedBuffer *managedBuffer, size_t newSize);
575+static void jk_objectStack_release(JKObjectStack *objectStack);
576+static void jk_objectStack_setToStackBuffer(JKObjectStack *objectStack, void **objects, void **keys, CFHashCode *cfHashes, size_t count);
577+static int jk_objectStack_resize(JKObjectStack *objectStack, size_t newCount);
578+
579+static void jk_error(JKParseState *parseState, NSString *format, ...);
580+static int jk_parse_string(JKParseState *parseState);
581+static int jk_parse_number(JKParseState *parseState);
582+static size_t jk_parse_is_newline(JKParseState *parseState, const unsigned char *atCharacterPtr);
583+JK_STATIC_INLINE int jk_parse_skip_newline(JKParseState *parseState);
584+JK_STATIC_INLINE void jk_parse_skip_whitespace(JKParseState *parseState);
585+static int jk_parse_next_token(JKParseState *parseState);
586+static void jk_error_parse_accept_or3(JKParseState *parseState, int state, NSString *or1String, NSString *or2String, NSString *or3String);
587+static void *jk_create_dictionary(JKParseState *parseState, size_t startingObjectIndex);
588+static void *jk_parse_dictionary(JKParseState *parseState);
589+static void *jk_parse_array(JKParseState *parseState);
590+static void *jk_object_for_token(JKParseState *parseState);
591+static void *jk_cachedObjects(JKParseState *parseState);
592+JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState);
593+JK_STATIC_INLINE void jk_set_parsed_token(JKParseState *parseState, const unsigned char *ptr, size_t length, JKTokenType type, size_t advanceBy);
594+
595+
596+static void jk_encode_error(JKEncodeState *encodeState, NSString *format, ...);
597+static int jk_encode_printf(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, ...);
598+static int jk_encode_write(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format);
599+static int jk_encode_writePrettyPrintWhiteSpace(JKEncodeState *encodeState);
600+static int jk_encode_write1slow(JKEncodeState *encodeState, ssize_t depthChange, const char *format);
601+static int jk_encode_write1fast(JKEncodeState *encodeState, ssize_t depthChange JK_UNUSED_ARG, const char *format);
602+static int jk_encode_writen(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, size_t length);
603+JK_STATIC_INLINE JKHash jk_encode_object_hash(void *objectPtr);
604+JK_STATIC_INLINE void jk_encode_updateCache(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object);
605+static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *objectPtr);
606+
607+#define jk_encode_write1(es, dc, f) (JK_EXPECT_F(_jk_encode_prettyPrint) ? jk_encode_write1slow(es, dc, f) : jk_encode_write1fast(es, dc, f))
608+
609+
610+JK_STATIC_INLINE size_t jk_min(size_t a, size_t b);
611+JK_STATIC_INLINE size_t jk_max(size_t a, size_t b);
612+JK_STATIC_INLINE JKHash jk_calculateHash(JKHash currentHash, unsigned char c);
613+
614+// JSONKit v1.4 used both a JKArray : NSArray and JKMutableArray : NSMutableArray, and the same for the dictionary collection type.
615+// However, Louis Gerbarg (via cocoa-dev) pointed out that Cocoa / Core Foundation actually implements only a single class that inherits from the
616+// mutable version, and keeps an ivar bit for whether or not that instance is mutable. This means that the immutable versions of the collection
617+// classes receive the mutating methods, but this is handled by having those methods throw an exception when the ivar bit is set to immutable.
618+// We adopt the same strategy here. It's both cleaner and gets rid of the method swizzling hackery used in JSONKit v1.4.
619+
620+
621+// This is a workaround for issue #23 https://github.com/johnezang/JSONKit/pull/23
622+// Basically, there seem to be a problem with using +load in static libraries on iOS. However, __attribute__ ((constructor)) does work correctly.
623+// Since we do not require anything "special" that +load provides, and we can accomplish the same thing using __attribute__ ((constructor)), the +load logic was moved here.
624+
625+static Class _JKArrayClass = NULL;
626+static size_t _JKArrayInstanceSize = 0UL;
627+static Class _JKDictionaryClass = NULL;
628+static size_t _JKDictionaryInstanceSize = 0UL;
629+
630+// For JSONDecoder...
631+static Class _jk_NSNumberClass = NULL;
632+static NSNumberAllocImp _jk_NSNumberAllocImp = NULL;
633+static NSNumberInitWithUnsignedLongLongImp _jk_NSNumberInitWithUnsignedLongLongImp = NULL;
634+
635+extern void jk_collectionClassLoadTimeInitialization(void) __attribute__ ((constructor));
636+
637+void jk_collectionClassLoadTimeInitialization(void) {
638+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Though technically not required, the run time environment at load time initialization may be less than ideal.
639+
640+ _JKArrayClass = objc_getClass("JKArray");
641+ _JKArrayInstanceSize = jk_max(16UL, class_getInstanceSize(_JKArrayClass));
642+
643+ _JKDictionaryClass = objc_getClass("JKDictionary");
644+ _JKDictionaryInstanceSize = jk_max(16UL, class_getInstanceSize(_JKDictionaryClass));
645+
646+ // For JSONDecoder...
647+ _jk_NSNumberClass = [NSNumber class];
648+ _jk_NSNumberAllocImp = (NSNumberAllocImp)[NSNumber methodForSelector:@selector(alloc)];
649+
650+ // Hacktacular. Need to do it this way due to the nature of class clusters.
651+ id temp_NSNumber = [NSNumber alloc];
652+ _jk_NSNumberInitWithUnsignedLongLongImp = (NSNumberInitWithUnsignedLongLongImp)[temp_NSNumber methodForSelector:@selector(initWithUnsignedLongLong:)];
653+ [[temp_NSNumber init] release];
654+ temp_NSNumber = NULL;
655+
656+ [pool release]; pool = NULL;
657+}
658+
659+
660+#pragma mark -
661+@interface JKArray : NSMutableArray <NSCopying, NSMutableCopying, NSFastEnumeration> {
662+ id *objects;
663+ NSUInteger count, capacity, mutations;
664+}
665+@end
666+
667+@implementation JKArray
668+
669++ (id)allocWithZone:(NSZone *)zone
670+{
671+#pragma unused(zone)
672+ [NSException raise:NSInvalidArgumentException format:@"*** - [%@ %@]: The %@ class is private to JSONKit and should not be used in this fashion.", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromClass([self class])];
673+ return(NULL);
674+}
675+
676+static JKArray *_JKArrayCreate(id *objects, NSUInteger count, BOOL mutableCollection) {
677+ NSCParameterAssert((objects != NULL) && (_JKArrayClass != NULL) && (_JKArrayInstanceSize > 0UL));
678+ JKArray *array = NULL;
679+ if(JK_EXPECT_T((array = (JKArray *)calloc(1UL, _JKArrayInstanceSize)) != NULL)) { // Directly allocate the JKArray instance via calloc.
680+ object_setClass(array, _JKArrayClass);
681+ if((array = [array init]) == NULL) { return(NULL); }
682+ array->capacity = count;
683+ array->count = count;
684+ if(JK_EXPECT_F((array->objects = (id *)malloc(sizeof(id) * array->capacity)) == NULL)) { [array autorelease]; return(NULL); }
685+ memcpy(array->objects, objects, array->capacity * sizeof(id));
686+ array->mutations = (mutableCollection == NO) ? 0UL : 1UL;
687+ }
688+ return(array);
689+}
690+
691+// Note: The caller is responsible for -retaining the object that is to be added.
692+static void _JKArrayInsertObjectAtIndex(JKArray *array, id newObject, NSUInteger objectIndex) {
693+ NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity) && (objectIndex <= array->count) && (newObject != NULL));
694+ if(!((array != NULL) && (array->objects != NULL) && (objectIndex <= array->count) && (newObject != NULL))) { [newObject autorelease]; return; }
695+ if((array->count + 1UL) >= array->capacity) {
696+ id *newObjects = NULL;
697+ if((newObjects = (id *)realloc(array->objects, sizeof(id) * (array->capacity + 16UL))) == NULL) { [NSException raise:NSMallocException format:@"Unable to resize objects array."]; }
698+ array->objects = newObjects;
699+ array->capacity += 16UL;
700+ memset(&array->objects[array->count], 0, sizeof(id) * (array->capacity - array->count));
701+ }
702+ array->count++;
703+ if((objectIndex + 1UL) < array->count) { memmove(&array->objects[objectIndex + 1UL], &array->objects[objectIndex], sizeof(id) * ((array->count - 1UL) - objectIndex)); array->objects[objectIndex] = NULL; }
704+ array->objects[objectIndex] = newObject;
705+}
706+
707+// Note: The caller is responsible for -retaining the object that is to be added.
708+static void _JKArrayReplaceObjectAtIndexWithObject(JKArray *array, NSUInteger objectIndex, id newObject) {
709+ NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL) && (newObject != NULL));
710+ if(!((array != NULL) && (array->objects != NULL) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL) && (newObject != NULL))) { [newObject autorelease]; return; }
711+ CFRelease(array->objects[objectIndex]);
712+ array->objects[objectIndex] = NULL;
713+ array->objects[objectIndex] = newObject;
714+}
715+
716+static void _JKArrayRemoveObjectAtIndex(JKArray *array, NSUInteger objectIndex) {
717+ NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count > 0UL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL));
718+ if(!((array != NULL) && (array->objects != NULL) && (array->count > 0UL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL))) { return; }
719+ CFRelease(array->objects[objectIndex]);
720+ array->objects[objectIndex] = NULL;
721+ if((objectIndex + 1UL) < array->count) { memmove(&array->objects[objectIndex], &array->objects[objectIndex + 1UL], sizeof(id) * ((array->count - 1UL) - objectIndex)); array->objects[array->count - 1UL] = NULL; }
722+ array->count--;
723+}
724+
725+- (void)dealloc
726+{
727+ if(JK_EXPECT_T(objects != NULL)) {
728+ NSUInteger atObject = 0UL;
729+ for(atObject = 0UL; atObject < count; atObject++) { if(JK_EXPECT_T(objects[atObject] != NULL)) { CFRelease(objects[atObject]); objects[atObject] = NULL; } }
730+ free(objects); objects = NULL;
731+ }
732+
733+ [super dealloc];
734+}
735+
736+- (NSUInteger)count
737+{
738+ NSParameterAssert((objects != NULL) && (count <= capacity));
739+ return(count);
740+}
741+
742+- (void)getObjects:(id *)objectsPtr range:(NSRange)range
743+{
744+ NSParameterAssert((objects != NULL) && (count <= capacity));
745+ if((objectsPtr == NULL) && (NSMaxRange(range) > 0UL)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: pointer to objects array is NULL but range length is %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)NSMaxRange(range)]; }
746+ if((range.location > count) || (NSMaxRange(range) > count)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)NSMaxRange(range), (unsigned long)count]; }
747+#ifndef __clang_analyzer__
748+ memcpy(objectsPtr, objects + range.location, range.length * sizeof(id));
749+#endif
750+}
751+
752+- (id)objectAtIndex:(NSUInteger)objectIndex
753+{
754+ if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)count]; }
755+ NSParameterAssert((objects != NULL) && (count <= capacity) && (objects[objectIndex] != NULL));
756+ return(objects[objectIndex]);
757+}
758+
759+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
760+{
761+ NSParameterAssert((state != NULL) && (stackbuf != NULL) && (len > 0UL) && (objects != NULL) && (count <= capacity));
762+ if(JK_EXPECT_F(state->state == 0UL)) { state->mutationsPtr = (unsigned long *)&mutations; state->itemsPtr = stackbuf; }
763+ if(JK_EXPECT_F(state->state >= count)) { return(0UL); }
764+
765+ NSUInteger enumeratedCount = 0UL;
766+ while(JK_EXPECT_T(enumeratedCount < len) && JK_EXPECT_T(state->state < count)) { NSParameterAssert(objects[state->state] != NULL); stackbuf[enumeratedCount++] = objects[state->state++]; }
767+
768+ return(enumeratedCount);
769+}
770+
771+- (void)insertObject:(id)anObject atIndex:(NSUInteger)objectIndex
772+{
773+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
774+ if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
775+ if(objectIndex > count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)(count + 1UL)]; }
776+#ifdef __clang_analyzer__
777+ [anObject retain]; // Stupid clang analyzer... Issue #19.
778+#else
779+ anObject = [anObject retain];
780+#endif
781+ _JKArrayInsertObjectAtIndex(self, anObject, objectIndex);
782+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
783+}
784+
785+- (void)removeObjectAtIndex:(NSUInteger)objectIndex
786+{
787+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
788+ if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)count]; }
789+ _JKArrayRemoveObjectAtIndex(self, objectIndex);
790+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
791+}
792+
793+- (void)replaceObjectAtIndex:(NSUInteger)objectIndex withObject:(id)anObject
794+{
795+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
796+ if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
797+ if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)count]; }
798+#ifdef __clang_analyzer__
799+ [anObject retain]; // Stupid clang analyzer... Issue #19.
800+#else
801+ anObject = [anObject retain];
802+#endif
803+ _JKArrayReplaceObjectAtIndexWithObject(self, objectIndex, anObject);
804+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
805+}
806+
807+- (id)copyWithZone:(NSZone *)zone
808+{
809+ NSParameterAssert((objects != NULL) && (count <= capacity));
810+ return((mutations == 0UL) ? [self retain] : [(NSArray *)[NSArray allocWithZone:zone] initWithObjects:objects count:count]);
811+}
812+
813+- (id)mutableCopyWithZone:(NSZone *)zone
814+{
815+ NSParameterAssert((objects != NULL) && (count <= capacity));
816+ return([(NSMutableArray *)[NSMutableArray allocWithZone:zone] initWithObjects:objects count:count]);
817+}
818+
819+@end
820+
821+
822+#pragma mark -
823+@interface JKDictionaryEnumerator : NSEnumerator {
824+ id collection;
825+ NSUInteger nextObject;
826+}
827+
828+- (id)initWithJKDictionary:(JKDictionary *)initDictionary;
829+- (NSArray *)allObjects;
830+- (id)nextObject;
831+
832+@end
833+
834+@implementation JKDictionaryEnumerator
835+
836+- (id)initWithJKDictionary:(JKDictionary *)initDictionary
837+{
838+ NSParameterAssert(initDictionary != NULL);
839+ if((self = [super init]) == NULL) { return(NULL); }
840+ if((collection = (id)CFRetain(initDictionary)) == NULL) { [self autorelease]; return(NULL); }
841+ return(self);
842+}
843+
844+- (void)dealloc
845+{
846+ if(collection != NULL) { CFRelease(collection); collection = NULL; }
847+ [super dealloc];
848+}
849+
850+- (NSArray *)allObjects
851+{
852+ NSParameterAssert(collection != NULL);
853+ NSUInteger count = [(NSDictionary *)collection count], atObject = 0UL;
854+ id objects[count];
855+
856+ while((objects[atObject] = [self nextObject]) != NULL) { NSParameterAssert(atObject < count); atObject++; }
857+
858+ return([NSArray arrayWithObjects:objects count:atObject]);
859+}
860+
861+- (id)nextObject
862+{
863+ NSParameterAssert((collection != NULL) && (_JKDictionaryHashEntry(collection) != NULL));
864+ JKHashTableEntry *entry = _JKDictionaryHashEntry(collection);
865+ NSUInteger capacity = _JKDictionaryCapacity(collection);
866+ id returnObject = NULL;
867+
868+ if(entry != NULL) { while((nextObject < capacity) && ((returnObject = entry[nextObject++].key) == NULL)) { /* ... */ } }
869+
870+ return(returnObject);
871+}
872+
873+@end
874+
875+#pragma mark -
876+@interface JKDictionary : NSMutableDictionary <NSCopying, NSMutableCopying, NSFastEnumeration> {
877+ NSUInteger count, capacity, mutations;
878+ JKHashTableEntry *entry;
879+}
880+@end
881+
882+@implementation JKDictionary
883+
884++ (id)allocWithZone:(NSZone *)zone
885+{
886+#pragma unused(zone)
887+ [NSException raise:NSInvalidArgumentException format:@"*** - [%@ %@]: The %@ class is private to JSONKit and should not be used in this fashion.", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromClass([self class])];
888+ return(NULL);
889+}
890+
891+// These values are taken from Core Foundation CF-550 CFBasicHash.m. As a bonus, they align very well with our JKHashTableEntry struct too.
892+static const NSUInteger jk_dictionaryCapacities[] = {
893+ 0UL, 3UL, 7UL, 13UL, 23UL, 41UL, 71UL, 127UL, 191UL, 251UL, 383UL, 631UL, 1087UL, 1723UL,
894+ 2803UL, 4523UL, 7351UL, 11959UL, 19447UL, 31231UL, 50683UL, 81919UL, 132607UL,
895+ 214519UL, 346607UL, 561109UL, 907759UL, 1468927UL, 2376191UL, 3845119UL,
896+ 6221311UL, 10066421UL, 16287743UL, 26354171UL, 42641881UL, 68996069UL,
897+ 111638519UL, 180634607UL, 292272623UL, 472907251UL
898+};
899+
900+static NSUInteger _JKDictionaryCapacityForCount(NSUInteger count) {
901+ NSUInteger bottom = 0UL, top = sizeof(jk_dictionaryCapacities) / sizeof(NSUInteger), mid = 0UL, tableSize = (NSUInteger)lround(floor(((double)count) * 1.33));
902+ while(top > bottom) { mid = (top + bottom) / 2UL; if(jk_dictionaryCapacities[mid] < tableSize) { bottom = mid + 1UL; } else { top = mid; } }
903+ return(jk_dictionaryCapacities[bottom]);
904+}
905+
906+static void _JKDictionaryResizeIfNeccessary(JKDictionary *dictionary) {
907+ NSCParameterAssert((dictionary != NULL) && (dictionary->entry != NULL) && (dictionary->count <= dictionary->capacity));
908+
909+ NSUInteger capacityForCount = 0UL;
910+ if(dictionary->capacity < (capacityForCount = _JKDictionaryCapacityForCount(dictionary->count + 1UL))) { // resize
911+ NSUInteger oldCapacity = dictionary->capacity;
912+#ifndef NS_BLOCK_ASSERTIONS
913+ NSUInteger oldCount = dictionary->count;
914+#endif
915+ JKHashTableEntry *oldEntry = dictionary->entry;
916+ if(JK_EXPECT_F((dictionary->entry = (JKHashTableEntry *)calloc(1UL, sizeof(JKHashTableEntry) * capacityForCount)) == NULL)) { [NSException raise:NSMallocException format:@"Unable to allocate memory for hash table."]; }
917+ dictionary->capacity = capacityForCount;
918+ dictionary->count = 0UL;
919+
920+ NSUInteger idx = 0UL;
921+ 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; } }
922+ NSCParameterAssert((oldCount == dictionary->count));
923+ free(oldEntry); oldEntry = NULL;
924+ }
925+}
926+
927+static JKDictionary *_JKDictionaryCreate(id *keys, NSUInteger *keyHashes, id *objects, NSUInteger count, BOOL mutableCollection) {
928+ NSCParameterAssert((keys != NULL) && (keyHashes != NULL) && (objects != NULL) && (_JKDictionaryClass != NULL) && (_JKDictionaryInstanceSize > 0UL));
929+ JKDictionary *dictionary = NULL;
930+ if(JK_EXPECT_T((dictionary = (JKDictionary *)calloc(1UL, _JKDictionaryInstanceSize)) != NULL)) { // Directly allocate the JKDictionary instance via calloc.
931+ object_setClass(dictionary, _JKDictionaryClass);
932+ if((dictionary = [dictionary init]) == NULL) { return(NULL); }
933+ dictionary->capacity = _JKDictionaryCapacityForCount(count);
934+ dictionary->count = 0UL;
935+
936+ if(JK_EXPECT_F((dictionary->entry = (JKHashTableEntry *)calloc(1UL, sizeof(JKHashTableEntry) * dictionary->capacity)) == NULL)) { [dictionary autorelease]; return(NULL); }
937+
938+ NSUInteger idx = 0UL;
939+ for(idx = 0UL; idx < count; idx++) { _JKDictionaryAddObject(dictionary, keyHashes[idx], keys[idx], objects[idx]); }
940+
941+ dictionary->mutations = (mutableCollection == NO) ? 0UL : 1UL;
942+ }
943+ return(dictionary);
944+}
945+
946+- (void)dealloc
947+{
948+ if(JK_EXPECT_T(entry != NULL)) {
949+ NSUInteger atEntry = 0UL;
950+ for(atEntry = 0UL; atEntry < capacity; atEntry++) {
951+ if(JK_EXPECT_T(entry[atEntry].key != NULL)) { CFRelease(entry[atEntry].key); entry[atEntry].key = NULL; }
952+ if(JK_EXPECT_T(entry[atEntry].object != NULL)) { CFRelease(entry[atEntry].object); entry[atEntry].object = NULL; }
953+ }
954+
955+ free(entry); entry = NULL;
956+ }
957+
958+ [super dealloc];
959+}
960+
961+static JKHashTableEntry *_JKDictionaryHashEntry(JKDictionary *dictionary) {
962+ NSCParameterAssert(dictionary != NULL);
963+ return(dictionary->entry);
964+}
965+
966+static NSUInteger _JKDictionaryCapacity(JKDictionary *dictionary) {
967+ NSCParameterAssert(dictionary != NULL);
968+ return(dictionary->capacity);
969+}
970+
971+static void _JKDictionaryRemoveObjectWithEntry(JKDictionary *dictionary, JKHashTableEntry *entry) {
972+ NSCParameterAssert((dictionary != NULL) && (entry != NULL) && (entry->key != NULL) && (entry->object != NULL) && (dictionary->count > 0UL) && (dictionary->count <= dictionary->capacity));
973+ CFRelease(entry->key); entry->key = NULL;
974+ CFRelease(entry->object); entry->object = NULL;
975+ entry->keyHash = 0UL;
976+ dictionary->count--;
977+ // 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.
978+ NSUInteger removeIdx = entry - dictionary->entry, idx = 0UL;
979+ NSCParameterAssert((removeIdx < dictionary->capacity));
980+ for(idx = 0UL; idx < dictionary->capacity; idx++) {
981+ NSUInteger entryIdx = (removeIdx + idx + 1UL) % dictionary->capacity;
982+ JKHashTableEntry *atEntry = &dictionary->entry[entryIdx];
983+ if(atEntry->key == NULL) { break; }
984+ NSUInteger keyHash = atEntry->keyHash;
985+ id key = atEntry->key, object = atEntry->object;
986+ NSCParameterAssert(object != NULL);
987+ atEntry->keyHash = 0UL;
988+ atEntry->key = NULL;
989+ atEntry->object = NULL;
990+ NSUInteger addKeyEntry = keyHash % dictionary->capacity, addIdx = 0UL;
991+ for(addIdx = 0UL; addIdx < dictionary->capacity; addIdx++) {
992+ JKHashTableEntry *atAddEntry = &dictionary->entry[((addKeyEntry + addIdx) % dictionary->capacity)];
993+ if(JK_EXPECT_T(atAddEntry->key == NULL)) { NSCParameterAssert((atAddEntry->keyHash == 0UL) && (atAddEntry->object == NULL)); atAddEntry->key = key; atAddEntry->object = object; atAddEntry->keyHash = keyHash; break; }
994+ }
995+ }
996+}
997+
998+static void _JKDictionaryAddObject(JKDictionary *dictionary, NSUInteger keyHash, id key, id object) {
999+ NSCParameterAssert((dictionary != NULL) && (key != NULL) && (object != NULL) && (dictionary->count < dictionary->capacity) && (dictionary->entry != NULL));
1000+ NSUInteger keyEntry = keyHash % dictionary->capacity, idx = 0UL;
1001+ for(idx = 0UL; idx < dictionary->capacity; idx++) {
1002+ NSUInteger entryIdx = (keyEntry + idx) % dictionary->capacity;
1003+ JKHashTableEntry *atEntry = &dictionary->entry[entryIdx];
1004+ 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); }
1005+ 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; }
1006+ }
1007+
1008+ // We should never get here. If we do, we -release the key / object because it's our responsibility.
1009+ CFRelease(key);
1010+ CFRelease(object);
1011+}
1012+
1013+- (NSUInteger)count
1014+{
1015+ return(count);
1016+}
1017+
1018+static JKHashTableEntry *_JKDictionaryHashTableEntryForKey(JKDictionary *dictionary, id aKey) {
1019+ NSCParameterAssert((dictionary != NULL) && (dictionary->entry != NULL) && (dictionary->count <= dictionary->capacity));
1020+ if((aKey == NULL) || (dictionary->capacity == 0UL)) { return(NULL); }
1021+ NSUInteger keyHash = CFHash(aKey), keyEntry = (keyHash % dictionary->capacity), idx = 0UL;
1022+ JKHashTableEntry *atEntry = NULL;
1023+ for(idx = 0UL; idx < dictionary->capacity; idx++) {
1024+ atEntry = &dictionary->entry[(keyEntry + idx) % dictionary->capacity];
1025+ if(JK_EXPECT_T(atEntry->keyHash == keyHash) && JK_EXPECT_T(atEntry->key != NULL) && ((atEntry->key == aKey) || CFEqual(atEntry->key, aKey))) { NSCParameterAssert(atEntry->object != NULL); return(atEntry); break; }
1026+ if(JK_EXPECT_F(atEntry->key == NULL)) { NSCParameterAssert(atEntry->object == NULL); return(NULL); break; } // If the key was in the table, we would have found it by now.
1027+ }
1028+ return(NULL);
1029+}
1030+
1031+- (id)objectForKey:(id)aKey
1032+{
1033+ NSParameterAssert((entry != NULL) && (count <= capacity));
1034+ JKHashTableEntry *entryForKey = _JKDictionaryHashTableEntryForKey(self, aKey);
1035+ return((entryForKey != NULL) ? entryForKey->object : NULL);
1036+}
1037+
1038+- (void)getObjects:(id *)objects andKeys:(id *)keys
1039+{
1040+ NSParameterAssert((entry != NULL) && (count <= capacity));
1041+ NSUInteger atEntry = 0UL; NSUInteger arrayIdx = 0UL;
1042+ for(atEntry = 0UL; atEntry < capacity; atEntry++) {
1043+ if(JK_EXPECT_T(entry[atEntry].key != NULL)) {
1044+ NSCParameterAssert((entry[atEntry].object != NULL) && (arrayIdx < count));
1045+ if(JK_EXPECT_T(keys != NULL)) { keys[arrayIdx] = entry[atEntry].key; }
1046+ if(JK_EXPECT_T(objects != NULL)) { objects[arrayIdx] = entry[atEntry].object; }
1047+ arrayIdx++;
1048+ }
1049+ }
1050+}
1051+
1052+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
1053+{
1054+ NSParameterAssert((state != NULL) && (stackbuf != NULL) && (len > 0UL) && (entry != NULL) && (count <= capacity));
1055+ if(JK_EXPECT_F(state->state == 0UL)) { state->mutationsPtr = (unsigned long *)&mutations; state->itemsPtr = stackbuf; }
1056+ if(JK_EXPECT_F(state->state >= capacity)) { return(0UL); }
1057+
1058+ NSUInteger enumeratedCount = 0UL;
1059+ while(JK_EXPECT_T(enumeratedCount < len) && JK_EXPECT_T(state->state < capacity)) { if(JK_EXPECT_T(entry[state->state].key != NULL)) { stackbuf[enumeratedCount++] = entry[state->state].key; } state->state++; }
1060+
1061+ return(enumeratedCount);
1062+}
1063+
1064+- (NSEnumerator *)keyEnumerator
1065+{
1066+ return([[[JKDictionaryEnumerator alloc] initWithJKDictionary:self] autorelease]);
1067+}
1068+
1069+- (void)setObject:(id)anObject forKey:(id)aKey
1070+{
1071+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
1072+ if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
1073+ if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil value (key: %@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey]; }
1074+
1075+ _JKDictionaryResizeIfNeccessary(self);
1076+#ifndef __clang_analyzer__
1077+ aKey = [aKey copy]; // Why on earth would clang complain that this -copy "might leak",
1078+ anObject = [anObject retain]; // but this -retain doesn't!?
1079+#endif // __clang_analyzer__
1080+ _JKDictionaryAddObject(self, CFHash(aKey), aKey, anObject);
1081+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
1082+}
1083+
1084+- (void)removeObjectForKey:(id)aKey
1085+{
1086+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
1087+ if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to remove nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
1088+ JKHashTableEntry *entryForKey = _JKDictionaryHashTableEntryForKey(self, aKey);
1089+ if(entryForKey != NULL) {
1090+ _JKDictionaryRemoveObjectWithEntry(self, entryForKey);
1091+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
1092+ }
1093+}
1094+
1095+- (id)copyWithZone:(NSZone *)zone
1096+{
1097+ NSParameterAssert((entry != NULL) && (count <= capacity));
1098+ return((mutations == 0UL) ? [self retain] : [[NSDictionary allocWithZone:zone] initWithDictionary:self]);
1099+}
1100+
1101+- (id)mutableCopyWithZone:(NSZone *)zone
1102+{
1103+ NSParameterAssert((entry != NULL) && (count <= capacity));
1104+ return([[NSMutableDictionary allocWithZone:zone] initWithDictionary:self]);
1105+}
1106+
1107+@end
1108+
1109+
1110+
1111+#pragma mark -
1112+
1113+JK_STATIC_INLINE size_t jk_min(size_t a, size_t b) { return((a < b) ? a : b); }
1114+JK_STATIC_INLINE size_t jk_max(size_t a, size_t b) { return((a > b) ? a : b); }
1115+
1116+JK_STATIC_INLINE JKHash jk_calculateHash(JKHash currentHash, unsigned char c) { return((((currentHash << 5) + currentHash) + (c - 29)) ^ (currentHash >> 19)); }
1117+
1118+
1119+static void jk_error(JKParseState *parseState, NSString *format, ...) {
1120+ NSCParameterAssert((parseState != NULL) && (format != NULL));
1121+
1122+ va_list varArgsList;
1123+ va_start(varArgsList, format);
1124+ NSString *formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease];
1125+ va_end(varArgsList);
1126+
1127+#if 0
1128+ const unsigned char *lineStart = parseState->stringBuffer.bytes.ptr + parseState->lineStartIndex;
1129+ const unsigned char *lineEnd = lineStart;
1130+ const unsigned char *atCharacterPtr = NULL;
1131+
1132+ for(atCharacterPtr = lineStart; atCharacterPtr < JK_END_STRING_PTR(parseState); atCharacterPtr++) { lineEnd = atCharacterPtr; if(jk_parse_is_newline(parseState, atCharacterPtr)) { break; } }
1133+
1134+ NSString *lineString = @"", *carretString = @"";
1135+ if(lineStart < JK_END_STRING_PTR(parseState)) {
1136+ lineString = [[[NSString alloc] initWithBytes:lineStart length:(lineEnd - lineStart) encoding:NSUTF8StringEncoding] autorelease];
1137+ carretString = [NSString stringWithFormat:@"%*.*s^", (int)(parseState->atIndex - parseState->lineStartIndex), (int)(parseState->atIndex - parseState->lineStartIndex), " "];
1138+ }
1139+#endif
1140+
1141+ if(parseState->error == NULL) {
1142+ parseState->error = [NSError errorWithDomain:@"JKErrorDomain" code:-1L userInfo:
1143+ [NSDictionary dictionaryWithObjectsAndKeys:
1144+ formatString, NSLocalizedDescriptionKey,
1145+ [NSNumber numberWithUnsignedLong:parseState->atIndex], @"JKAtIndexKey",
1146+ [NSNumber numberWithUnsignedLong:parseState->lineNumber], @"JKLineNumberKey",
1147+ //lineString, @"JKErrorLine0Key",
1148+ //carretString, @"JKErrorLine1Key",
1149+ NULL]];
1150+ }
1151+}
1152+
1153+#pragma mark -
1154+#pragma mark Buffer and Object Stack management functions
1155+
1156+static void jk_managedBuffer_release(JKManagedBuffer *managedBuffer) {
1157+ if((managedBuffer->flags & JKManagedBufferMustFree)) {
1158+ if(managedBuffer->bytes.ptr != NULL) { free(managedBuffer->bytes.ptr); managedBuffer->bytes.ptr = NULL; }
1159+ managedBuffer->flags &= ~JKManagedBufferMustFree;
1160+ }
1161+
1162+ managedBuffer->bytes.ptr = NULL;
1163+ managedBuffer->bytes.length = 0UL;
1164+ managedBuffer->flags &= ~JKManagedBufferLocationMask;
1165+}
1166+
1167+static void jk_managedBuffer_setToStackBuffer(JKManagedBuffer *managedBuffer, unsigned char *ptr, size_t length) {
1168+ jk_managedBuffer_release(managedBuffer);
1169+ managedBuffer->bytes.ptr = ptr;
1170+ managedBuffer->bytes.length = length;
1171+ managedBuffer->flags = (managedBuffer->flags & ~JKManagedBufferLocationMask) | JKManagedBufferOnStack;
1172+}
1173+
1174+static unsigned char *jk_managedBuffer_resize(JKManagedBuffer *managedBuffer, size_t newSize) {
1175+ size_t roundedUpNewSize = newSize;
1176+
1177+ if(managedBuffer->roundSizeUpToMultipleOf > 0UL) { roundedUpNewSize = newSize + ((managedBuffer->roundSizeUpToMultipleOf - (newSize % managedBuffer->roundSizeUpToMultipleOf)) % managedBuffer->roundSizeUpToMultipleOf); }
1178+
1179+ if((roundedUpNewSize != managedBuffer->bytes.length) && (roundedUpNewSize > managedBuffer->bytes.length)) {
1180+ if((managedBuffer->flags & JKManagedBufferLocationMask) == JKManagedBufferOnStack) {
1181+ NSCParameterAssert((managedBuffer->flags & JKManagedBufferMustFree) == 0);
1182+ unsigned char *newBuffer = NULL, *oldBuffer = managedBuffer->bytes.ptr;
1183+
1184+ if((newBuffer = (unsigned char *)malloc(roundedUpNewSize)) == NULL) { return(NULL); }
1185+ memcpy(newBuffer, oldBuffer, jk_min(managedBuffer->bytes.length, roundedUpNewSize));
1186+ managedBuffer->flags = (managedBuffer->flags & ~JKManagedBufferLocationMask) | (JKManagedBufferOnHeap | JKManagedBufferMustFree);
1187+ managedBuffer->bytes.ptr = newBuffer;
1188+ managedBuffer->bytes.length = roundedUpNewSize;
1189+ } else {
1190+ NSCParameterAssert(((managedBuffer->flags & JKManagedBufferMustFree) != 0) && ((managedBuffer->flags & JKManagedBufferLocationMask) == JKManagedBufferOnHeap));
1191+ if((managedBuffer->bytes.ptr = (unsigned char *)reallocf(managedBuffer->bytes.ptr, roundedUpNewSize)) == NULL) { return(NULL); }
1192+ managedBuffer->bytes.length = roundedUpNewSize;
1193+ }
1194+ }
1195+
1196+ return(managedBuffer->bytes.ptr);
1197+}
1198+
1199+
1200+
1201+static void jk_objectStack_release(JKObjectStack *objectStack) {
1202+ NSCParameterAssert(objectStack != NULL);
1203+
1204+ NSCParameterAssert(objectStack->index <= objectStack->count);
1205+ size_t atIndex = 0UL;
1206+ for(atIndex = 0UL; atIndex < objectStack->index; atIndex++) {
1207+ if(objectStack->objects[atIndex] != NULL) { CFRelease(objectStack->objects[atIndex]); objectStack->objects[atIndex] = NULL; }
1208+ if(objectStack->keys[atIndex] != NULL) { CFRelease(objectStack->keys[atIndex]); objectStack->keys[atIndex] = NULL; }
1209+ }
1210+ objectStack->index = 0UL;
1211+
1212+ if(objectStack->flags & JKObjectStackMustFree) {
1213+ NSCParameterAssert((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnHeap);
1214+ if(objectStack->objects != NULL) { free(objectStack->objects); objectStack->objects = NULL; }
1215+ if(objectStack->keys != NULL) { free(objectStack->keys); objectStack->keys = NULL; }
1216+ if(objectStack->cfHashes != NULL) { free(objectStack->cfHashes); objectStack->cfHashes = NULL; }
1217+ objectStack->flags &= ~JKObjectStackMustFree;
1218+ }
1219+
1220+ objectStack->objects = NULL;
1221+ objectStack->keys = NULL;
1222+ objectStack->cfHashes = NULL;
1223+
1224+ objectStack->count = 0UL;
1225+ objectStack->flags &= ~JKObjectStackLocationMask;
1226+}
1227+
1228+static void jk_objectStack_setToStackBuffer(JKObjectStack *objectStack, void **objects, void **keys, CFHashCode *cfHashes, size_t count) {
1229+ NSCParameterAssert((objectStack != NULL) && (objects != NULL) && (keys != NULL) && (cfHashes != NULL) && (count > 0UL));
1230+ jk_objectStack_release(objectStack);
1231+ objectStack->objects = objects;
1232+ objectStack->keys = keys;
1233+ objectStack->cfHashes = cfHashes;
1234+ objectStack->count = count;
1235+ objectStack->flags = (objectStack->flags & ~JKObjectStackLocationMask) | JKObjectStackOnStack;
1236+#ifndef NS_BLOCK_ASSERTIONS
1237+ size_t idx;
1238+ for(idx = 0UL; idx < objectStack->count; idx++) { objectStack->objects[idx] = NULL; objectStack->keys[idx] = NULL; objectStack->cfHashes[idx] = 0UL; }
1239+#endif
1240+}
1241+
1242+static int jk_objectStack_resize(JKObjectStack *objectStack, size_t newCount) {
1243+ size_t roundedUpNewCount = newCount;
1244+ int returnCode = 0;
1245+
1246+ void **newObjects = NULL, **newKeys = NULL;
1247+ CFHashCode *newCFHashes = NULL;
1248+
1249+ if(objectStack->roundSizeUpToMultipleOf > 0UL) { roundedUpNewCount = newCount + ((objectStack->roundSizeUpToMultipleOf - (newCount % objectStack->roundSizeUpToMultipleOf)) % objectStack->roundSizeUpToMultipleOf); }
1250+
1251+ if((roundedUpNewCount != objectStack->count) && (roundedUpNewCount > objectStack->count)) {
1252+ if((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnStack) {
1253+ NSCParameterAssert((objectStack->flags & JKObjectStackMustFree) == 0);
1254+
1255+ if((newObjects = (void ** )calloc(1UL, roundedUpNewCount * sizeof(void * ))) == NULL) { returnCode = 1; goto errorExit; }
1256+ memcpy(newObjects, objectStack->objects, jk_min(objectStack->count, roundedUpNewCount) * sizeof(void *));
1257+ if((newKeys = (void ** )calloc(1UL, roundedUpNewCount * sizeof(void * ))) == NULL) { returnCode = 1; goto errorExit; }
1258+ memcpy(newKeys, objectStack->keys, jk_min(objectStack->count, roundedUpNewCount) * sizeof(void *));
1259+
1260+ if((newCFHashes = (CFHashCode *)calloc(1UL, roundedUpNewCount * sizeof(CFHashCode))) == NULL) { returnCode = 1; goto errorExit; }
1261+ memcpy(newCFHashes, objectStack->cfHashes, jk_min(objectStack->count, roundedUpNewCount) * sizeof(CFHashCode));
1262+
1263+ objectStack->flags = (objectStack->flags & ~JKObjectStackLocationMask) | (JKObjectStackOnHeap | JKObjectStackMustFree);
1264+ objectStack->objects = newObjects; newObjects = NULL;
1265+ objectStack->keys = newKeys; newKeys = NULL;
1266+ objectStack->cfHashes = newCFHashes; newCFHashes = NULL;
1267+ objectStack->count = roundedUpNewCount;
1268+ } else {
1269+ NSCParameterAssert(((objectStack->flags & JKObjectStackMustFree) != 0) && ((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnHeap));
1270+ if((newObjects = (void ** )realloc(objectStack->objects, roundedUpNewCount * sizeof(void * ))) != NULL) { objectStack->objects = newObjects; newObjects = NULL; } else { returnCode = 1; goto errorExit; }
1271+ if((newKeys = (void ** )realloc(objectStack->keys, roundedUpNewCount * sizeof(void * ))) != NULL) { objectStack->keys = newKeys; newKeys = NULL; } else { returnCode = 1; goto errorExit; }
1272+ if((newCFHashes = (CFHashCode *)realloc(objectStack->cfHashes, roundedUpNewCount * sizeof(CFHashCode))) != NULL) { objectStack->cfHashes = newCFHashes; newCFHashes = NULL; } else { returnCode = 1; goto errorExit; }
1273+
1274+#ifndef NS_BLOCK_ASSERTIONS
1275+ size_t idx;
1276+ for(idx = objectStack->count; idx < roundedUpNewCount; idx++) { objectStack->objects[idx] = NULL; objectStack->keys[idx] = NULL; objectStack->cfHashes[idx] = 0UL; }
1277+#endif
1278+ objectStack->count = roundedUpNewCount;
1279+ }
1280+ }
1281+
1282+ errorExit:
1283+ if(newObjects != NULL) { free(newObjects); newObjects = NULL; }
1284+ if(newKeys != NULL) { free(newKeys); newKeys = NULL; }
1285+ if(newCFHashes != NULL) { free(newCFHashes); newCFHashes = NULL; }
1286+
1287+ return(returnCode);
1288+}
1289+
1290+////////////
1291+#pragma mark -
1292+#pragma mark Unicode related functions
1293+
1294+JK_STATIC_INLINE ConversionResult isValidCodePoint(UTF32 *u32CodePoint) {
1295+ ConversionResult result = conversionOK;
1296+ UTF32 ch = *u32CodePoint;
1297+
1298+ if(JK_EXPECT_F(ch >= UNI_SUR_HIGH_START) && (JK_EXPECT_T(ch <= UNI_SUR_LOW_END))) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
1299+ if(JK_EXPECT_F(ch >= 0xFDD0U) && (JK_EXPECT_F(ch <= 0xFDEFU) || JK_EXPECT_F((ch & 0xFFFEU) == 0xFFFEU)) && JK_EXPECT_T(ch <= 0x10FFFFU)) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
1300+ if(JK_EXPECT_F(ch == 0U)) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
1301+
1302+ finished:
1303+ *u32CodePoint = ch;
1304+ return(result);
1305+}
1306+
1307+
1308+static int isLegalUTF8(const UTF8 *source, size_t length) {
1309+ const UTF8 *srcptr = source + length;
1310+ UTF8 a;
1311+
1312+ switch(length) {
1313+ default: return(0); // Everything else falls through when "true"...
1314+ case 4: if(JK_EXPECT_F(((a = (*--srcptr)) < 0x80) || (a > 0xBF))) { return(0); }
1315+ case 3: if(JK_EXPECT_F(((a = (*--srcptr)) < 0x80) || (a > 0xBF))) { return(0); }
1316+ case 2: if(JK_EXPECT_F( (a = (*--srcptr)) > 0xBF )) { return(0); }
1317+
1318+ switch(*source) { // no fall-through in this inner switch
1319+ case 0xE0: if(JK_EXPECT_F(a < 0xA0)) { return(0); } break;
1320+ case 0xED: if(JK_EXPECT_F(a > 0x9F)) { return(0); } break;
1321+ case 0xF0: if(JK_EXPECT_F(a < 0x90)) { return(0); } break;
1322+ case 0xF4: if(JK_EXPECT_F(a > 0x8F)) { return(0); } break;
1323+ default: if(JK_EXPECT_F(a < 0x80)) { return(0); }
1324+ }
1325+
1326+ case 1: if(JK_EXPECT_F((JK_EXPECT_T(*source < 0xC2)) && JK_EXPECT_F(*source >= 0x80))) { return(0); }
1327+ }
1328+
1329+ if(JK_EXPECT_F(*source > 0xF4)) { return(0); }
1330+
1331+ return(1);
1332+}
1333+
1334+static ConversionResult ConvertSingleCodePointInUTF8(const UTF8 *sourceStart, const UTF8 *sourceEnd, UTF8 const **nextUTF8, UTF32 *convertedUTF32) {
1335+ ConversionResult result = conversionOK;
1336+ const UTF8 *source = sourceStart;
1337+ UTF32 ch = 0UL;
1338+
1339+#if !defined(JK_FAST_TRAILING_BYTES)
1340+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
1341+#else
1342+ unsigned short extraBytesToRead = __builtin_clz(((*source)^0xff) << 25);
1343+#endif
1344+
1345+ if(JK_EXPECT_F((source + extraBytesToRead + 1) > sourceEnd) || JK_EXPECT_F(!isLegalUTF8(source, extraBytesToRead + 1))) {
1346+ source++;
1347+ while((source < sourceEnd) && (((*source) & 0xc0) == 0x80) && ((source - sourceStart) < (extraBytesToRead + 1))) { source++; }
1348+ NSCParameterAssert(source <= sourceEnd);
1349+ result = ((source < sourceEnd) && (((*source) & 0xc0) != 0x80)) ? sourceIllegal : ((sourceStart + extraBytesToRead + 1) > sourceEnd) ? sourceExhausted : sourceIllegal;
1350+ ch = UNI_REPLACEMENT_CHAR;
1351+ goto finished;
1352+ }
1353+
1354+ switch(extraBytesToRead) { // The cases all fall through.
1355+ case 5: ch += *source++; ch <<= 6;
1356+ case 4: ch += *source++; ch <<= 6;
1357+ case 3: ch += *source++; ch <<= 6;
1358+ case 2: ch += *source++; ch <<= 6;
1359+ case 1: ch += *source++; ch <<= 6;
1360+ case 0: ch += *source++;
1361+ }
1362+ ch -= offsetsFromUTF8[extraBytesToRead];
1363+
1364+ result = isValidCodePoint(&ch);
1365+
1366+ finished:
1367+ *nextUTF8 = source;
1368+ *convertedUTF32 = ch;
1369+
1370+ return(result);
1371+}
1372+
1373+
1374+static ConversionResult ConvertUTF32toUTF8 (UTF32 u32CodePoint, UTF8 **targetStart, UTF8 *targetEnd) {
1375+ const UTF32 byteMask = 0xBF, byteMark = 0x80;
1376+ ConversionResult result = conversionOK;
1377+ UTF8 *target = *targetStart;
1378+ UTF32 ch = u32CodePoint;
1379+ unsigned short bytesToWrite = 0;
1380+
1381+ result = isValidCodePoint(&ch);
1382+
1383+ // Figure out how many bytes the result will require. Turn any illegally large UTF32 things (> Plane 17) into replacement chars.
1384+ if(ch < (UTF32)0x80) { bytesToWrite = 1; }
1385+ else if(ch < (UTF32)0x800) { bytesToWrite = 2; }
1386+ else if(ch < (UTF32)0x10000) { bytesToWrite = 3; }
1387+ else if(ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; }
1388+ else { bytesToWrite = 3; ch = UNI_REPLACEMENT_CHAR; result = sourceIllegal; }
1389+
1390+ target += bytesToWrite;
1391+ if (target > targetEnd) { target -= bytesToWrite; result = targetExhausted; goto finished; }
1392+
1393+ switch (bytesToWrite) { // note: everything falls through.
1394+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
1395+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
1396+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
1397+ case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
1398+ }
1399+
1400+ target += bytesToWrite;
1401+
1402+ finished:
1403+ *targetStart = target;
1404+ return(result);
1405+}
1406+
1407+JK_STATIC_INLINE int jk_string_add_unicodeCodePoint(JKParseState *parseState, uint32_t unicodeCodePoint, size_t *tokenBufferIdx, JKHash *stringHash) {
1408+ UTF8 *u8s = &parseState->token.tokenBuffer.bytes.ptr[*tokenBufferIdx];
1409+ ConversionResult result;
1410+
1411+ if((result = ConvertUTF32toUTF8(unicodeCodePoint, &u8s, (parseState->token.tokenBuffer.bytes.ptr + parseState->token.tokenBuffer.bytes.length))) != conversionOK) { if(result == targetExhausted) { return(1); } }
1412+ size_t utf8len = u8s - &parseState->token.tokenBuffer.bytes.ptr[*tokenBufferIdx], nextIdx = (*tokenBufferIdx) + utf8len;
1413+
1414+ while(*tokenBufferIdx < nextIdx) { *stringHash = jk_calculateHash(*stringHash, parseState->token.tokenBuffer.bytes.ptr[(*tokenBufferIdx)++]); }
1415+
1416+ return(0);
1417+}
1418+
1419+////////////
1420+#pragma mark -
1421+#pragma mark Decoding / parsing / deserializing functions
1422+
1423+static int jk_parse_string(JKParseState *parseState) {
1424+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
1425+ const unsigned char *stringStart = JK_AT_STRING_PTR(parseState) + 1;
1426+ const unsigned char *endOfBuffer = JK_END_STRING_PTR(parseState);
1427+ const unsigned char *atStringCharacter = stringStart;
1428+ unsigned char *tokenBuffer = parseState->token.tokenBuffer.bytes.ptr;
1429+ size_t tokenStartIndex = parseState->atIndex;
1430+ size_t tokenBufferIdx = 0UL;
1431+
1432+ int onlySimpleString = 1, stringState = JSONStringStateStart;
1433+ uint16_t escapedUnicode1 = 0U, escapedUnicode2 = 0U;
1434+ uint32_t escapedUnicodeCodePoint = 0U;
1435+ JKHash stringHash = JK_HASH_INIT;
1436+
1437+ while(1) {
1438+ unsigned long currentChar;
1439+
1440+ if(JK_EXPECT_F(atStringCharacter == endOfBuffer)) { /* XXX Add error message */ stringState = JSONStringStateError; goto finishedParsing; }
1441+
1442+ if(JK_EXPECT_F((currentChar = *atStringCharacter++) >= 0x80UL)) {
1443+ const unsigned char *nextValidCharacter = NULL;
1444+ UTF32 u32ch = 0U;
1445+ ConversionResult result;
1446+
1447+ if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(atStringCharacter - 1, endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { goto switchToSlowPath; }
1448+ stringHash = jk_calculateHash(stringHash, currentChar);
1449+ while(atStringCharacter < nextValidCharacter) { NSCParameterAssert(JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)); stringHash = jk_calculateHash(stringHash, *atStringCharacter++); }
1450+ continue;
1451+ } else {
1452+ if(JK_EXPECT_F(currentChar == (unsigned long)'"')) { stringState = JSONStringStateFinished; goto finishedParsing; }
1453+
1454+ if(JK_EXPECT_F(currentChar == (unsigned long)'\\')) {
1455+ switchToSlowPath:
1456+ onlySimpleString = 0;
1457+ stringState = JSONStringStateParsing;
1458+ tokenBufferIdx = (atStringCharacter - stringStart) - 1L;
1459+ if(JK_EXPECT_F((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; } }
1460+ memcpy(tokenBuffer, stringStart, tokenBufferIdx);
1461+ goto slowMatch;
1462+ }
1463+
1464+ if(JK_EXPECT_F(currentChar < 0x20UL)) { jk_error(parseState, @"Invalid character < 0x20 found in string: 0x%2.2x.", currentChar); stringState = JSONStringStateError; goto finishedParsing; }
1465+
1466+ stringHash = jk_calculateHash(stringHash, currentChar);
1467+ }
1468+ }
1469+
1470+ slowMatch:
1471+
1472+ for(atStringCharacter = (stringStart + ((atStringCharacter - stringStart) - 1L)); (atStringCharacter < endOfBuffer) && (tokenBufferIdx < parseState->token.tokenBuffer.bytes.length); atStringCharacter++) {
1473+ 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; } }
1474+
1475+ NSCParameterAssert(tokenBufferIdx < parseState->token.tokenBuffer.bytes.length);
1476+
1477+ unsigned long currentChar = (*atStringCharacter), escapedChar;
1478+
1479+ if(JK_EXPECT_T(stringState == JSONStringStateParsing)) {
1480+ if(JK_EXPECT_T(currentChar >= 0x20UL)) {
1481+ if(JK_EXPECT_T(currentChar < (unsigned long)0x80)) { // Not a UTF8 sequence
1482+ if(JK_EXPECT_F(currentChar == (unsigned long)'"')) { stringState = JSONStringStateFinished; atStringCharacter++; goto finishedParsing; }
1483+ if(JK_EXPECT_F(currentChar == (unsigned long)'\\')) { stringState = JSONStringStateEscape; continue; }
1484+ stringHash = jk_calculateHash(stringHash, currentChar);
1485+ tokenBuffer[tokenBufferIdx++] = currentChar;
1486+ continue;
1487+ } else { // UTF8 sequence
1488+ const unsigned char *nextValidCharacter = NULL;
1489+ UTF32 u32ch = 0U;
1490+ ConversionResult result;
1491+
1492+ if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(atStringCharacter, endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) {
1493+ if((result == sourceIllegal) && ((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0)) { jk_error(parseState, @"Illegal UTF8 sequence found in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; }
1494+ if(result == sourceExhausted) { jk_error(parseState, @"End of buffer reached while parsing UTF8 in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; }
1495+ 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; }
1496+ atStringCharacter = nextValidCharacter - 1;
1497+ continue;
1498+ } else {
1499+ while(atStringCharacter < nextValidCharacter) { tokenBuffer[tokenBufferIdx++] = *atStringCharacter; stringHash = jk_calculateHash(stringHash, *atStringCharacter++); }
1500+ atStringCharacter--;
1501+ continue;
1502+ }
1503+ }
1504+ } else { // currentChar < 0x20
1505+ jk_error(parseState, @"Invalid character < 0x20 found in string: 0x%2.2x.", currentChar); stringState = JSONStringStateError; goto finishedParsing;
1506+ }
1507+
1508+ } else { // stringState != JSONStringStateParsing
1509+ int isSurrogate = 1;
1510+
1511+ switch(stringState) {
1512+ case JSONStringStateEscape:
1513+ switch(currentChar) {
1514+ case 'u': escapedUnicode1 = 0U; escapedUnicode2 = 0U; escapedUnicodeCodePoint = 0U; stringState = JSONStringStateEscapedUnicode1; break;
1515+
1516+ case 'b': escapedChar = '\b'; goto parsedEscapedChar;
1517+ case 'f': escapedChar = '\f'; goto parsedEscapedChar;
1518+ case 'n': escapedChar = '\n'; goto parsedEscapedChar;
1519+ case 'r': escapedChar = '\r'; goto parsedEscapedChar;
1520+ case 't': escapedChar = '\t'; goto parsedEscapedChar;
1521+ case '\\': escapedChar = '\\'; goto parsedEscapedChar;
1522+ case '/': escapedChar = '/'; goto parsedEscapedChar;
1523+ case '"': escapedChar = '"'; goto parsedEscapedChar;
1524+
1525+ parsedEscapedChar:
1526+ stringState = JSONStringStateParsing;
1527+ stringHash = jk_calculateHash(stringHash, escapedChar);
1528+ tokenBuffer[tokenBufferIdx++] = escapedChar;
1529+ break;
1530+
1531+ default: jk_error(parseState, @"Invalid escape sequence found in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; break;
1532+ }
1533+ break;
1534+
1535+ case JSONStringStateEscapedUnicode1:
1536+ case JSONStringStateEscapedUnicode2:
1537+ case JSONStringStateEscapedUnicode3:
1538+ case JSONStringStateEscapedUnicode4: isSurrogate = 0;
1539+ case JSONStringStateEscapedUnicodeSurrogate1:
1540+ case JSONStringStateEscapedUnicodeSurrogate2:
1541+ case JSONStringStateEscapedUnicodeSurrogate3:
1542+ case JSONStringStateEscapedUnicodeSurrogate4:
1543+ {
1544+ uint16_t hexValue = 0U;
1545+
1546+ switch(currentChar) {
1547+ case '0' ... '9': hexValue = currentChar - '0'; goto parsedHex;
1548+ case 'a' ... 'f': hexValue = (currentChar - 'a') + 10U; goto parsedHex;
1549+ case 'A' ... 'F': hexValue = (currentChar - 'A') + 10U; goto parsedHex;
1550+
1551+ parsedHex:
1552+ if(!isSurrogate) { escapedUnicode1 = (escapedUnicode1 << 4) | hexValue; } else { escapedUnicode2 = (escapedUnicode2 << 4) | hexValue; }
1553+
1554+ if(stringState == JSONStringStateEscapedUnicode4) {
1555+ if(((escapedUnicode1 >= 0xD800U) && (escapedUnicode1 < 0xE000U))) {
1556+ if((escapedUnicode1 >= 0xD800U) && (escapedUnicode1 < 0xDC00U)) { stringState = JSONStringStateEscapedNeedEscapeForSurrogate; }
1557+ else if((escapedUnicode1 >= 0xDC00U) && (escapedUnicode1 < 0xE000U)) {
1558+ if((parseState->parseOptionFlags & JKParseOptionLooseUnicode)) { escapedUnicodeCodePoint = UNI_REPLACEMENT_CHAR; }
1559+ else { jk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
1560+ }
1561+ }
1562+ else { escapedUnicodeCodePoint = escapedUnicode1; }
1563+ }
1564+
1565+ if(stringState == JSONStringStateEscapedUnicodeSurrogate4) {
1566+ if((escapedUnicode2 < 0xdc00) || (escapedUnicode2 > 0xdfff)) {
1567+ if((parseState->parseOptionFlags & JKParseOptionLooseUnicode)) { escapedUnicodeCodePoint = UNI_REPLACEMENT_CHAR; }
1568+ else { jk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
1569+ }
1570+ else { escapedUnicodeCodePoint = ((escapedUnicode1 - 0xd800) * 0x400) + (escapedUnicode2 - 0xdc00) + 0x10000; }
1571+ }
1572+
1573+ if((stringState == JSONStringStateEscapedUnicode4) || (stringState == JSONStringStateEscapedUnicodeSurrogate4)) {
1574+ if((isValidCodePoint(&escapedUnicodeCodePoint) == sourceIllegal) && ((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0)) { jk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
1575+ stringState = JSONStringStateParsing;
1576+ 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; }
1577+ }
1578+ else if((stringState >= JSONStringStateEscapedUnicode1) && (stringState <= JSONStringStateEscapedUnicodeSurrogate4)) { stringState++; }
1579+ break;
1580+
1581+ default: jk_error(parseState, @"Unexpected character found in \\u Unicode escape sequence. Found '%c', expected [0-9a-fA-F].", currentChar); stringState = JSONStringStateError; goto finishedParsing; break;
1582+ }
1583+ }
1584+ break;
1585+
1586+ case JSONStringStateEscapedNeedEscapeForSurrogate:
1587+ if(currentChar == '\\') { stringState = JSONStringStateEscapedNeedEscapedUForSurrogate; }
1588+ else {
1589+ if((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0) { jk_error(parseState, @"Required a second \\u Unicode escape sequence following a surrogate \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
1590+ else { stringState = JSONStringStateParsing; atStringCharacter--; if(jk_string_add_unicodeCodePoint(parseState, UNI_REPLACEMENT_CHAR, &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; } }
1591+ }
1592+ break;
1593+
1594+ case JSONStringStateEscapedNeedEscapedUForSurrogate:
1595+ if(currentChar == 'u') { stringState = JSONStringStateEscapedUnicodeSurrogate1; }
1596+ else {
1597+ if((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0) { jk_error(parseState, @"Required a second \\u Unicode escape sequence following a surrogate \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
1598+ else { stringState = JSONStringStateParsing; atStringCharacter -= 2; if(jk_string_add_unicodeCodePoint(parseState, UNI_REPLACEMENT_CHAR, &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; } }
1599+ }
1600+ break;
1601+
1602+ default: jk_error(parseState, @"Internal error: Unknown stringState. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; break;
1603+ }
1604+ }
1605+ }
1606+
1607+finishedParsing:
1608+
1609+ if(JK_EXPECT_T(stringState == JSONStringStateFinished)) {
1610+ NSCParameterAssert((parseState->stringBuffer.bytes.ptr + tokenStartIndex) < atStringCharacter);
1611+
1612+ parseState->token.tokenPtrRange.ptr = parseState->stringBuffer.bytes.ptr + tokenStartIndex;
1613+ parseState->token.tokenPtrRange.length = (atStringCharacter - parseState->token.tokenPtrRange.ptr);
1614+
1615+ if(JK_EXPECT_T(onlySimpleString)) {
1616+ NSCParameterAssert(((parseState->token.tokenPtrRange.ptr + 1) < endOfBuffer) && (parseState->token.tokenPtrRange.length >= 2UL) && (((parseState->token.tokenPtrRange.ptr + 1) + (parseState->token.tokenPtrRange.length - 2)) < endOfBuffer));
1617+ parseState->token.value.ptrRange.ptr = parseState->token.tokenPtrRange.ptr + 1;
1618+ parseState->token.value.ptrRange.length = parseState->token.tokenPtrRange.length - 2UL;
1619+ } else {
1620+ parseState->token.value.ptrRange.ptr = parseState->token.tokenBuffer.bytes.ptr;
1621+ parseState->token.value.ptrRange.length = tokenBufferIdx;
1622+ }
1623+
1624+ parseState->token.value.hash = stringHash;
1625+ parseState->token.value.type = JKValueTypeString;
1626+ parseState->atIndex = (atStringCharacter - parseState->stringBuffer.bytes.ptr);
1627+ }
1628+
1629+ if(JK_EXPECT_F(stringState != JSONStringStateFinished)) { jk_error(parseState, @"Invalid string."); }
1630+ return(JK_EXPECT_T(stringState == JSONStringStateFinished) ? 0 : 1);
1631+}
1632+
1633+static int jk_parse_number(JKParseState *parseState) {
1634+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
1635+ const unsigned char *numberStart = JK_AT_STRING_PTR(parseState);
1636+ const unsigned char *endOfBuffer = JK_END_STRING_PTR(parseState);
1637+ const unsigned char *atNumberCharacter = NULL;
1638+ int numberState = JSONNumberStateWholeNumberStart, isFloatingPoint = 0, isNegative = 0, backup = 0;
1639+ size_t startingIndex = parseState->atIndex;
1640+
1641+ for(atNumberCharacter = numberStart; (JK_EXPECT_T(atNumberCharacter < endOfBuffer)) && (JK_EXPECT_T(!(JK_EXPECT_F(numberState == JSONNumberStateFinished) || JK_EXPECT_F(numberState == JSONNumberStateError)))); atNumberCharacter++) {
1642+ unsigned long currentChar = (unsigned long)(*atNumberCharacter), lowerCaseCC = currentChar | 0x20UL;
1643+
1644+ switch(numberState) {
1645+ case JSONNumberStateWholeNumberStart: if (currentChar == '-') { numberState = JSONNumberStateWholeNumberMinus; isNegative = 1; break; }
1646+ case JSONNumberStateWholeNumberMinus: if (currentChar == '0') { numberState = JSONNumberStateWholeNumberZero; break; }
1647+ else if( (currentChar >= '1') && (currentChar <= '9')) { numberState = JSONNumberStateWholeNumber; break; }
1648+ else { /* XXX Add error message */ numberState = JSONNumberStateError; break; }
1649+ case JSONNumberStateExponentStart: if( (currentChar == '+') || (currentChar == '-')) { numberState = JSONNumberStateExponentPlusMinus; break; }
1650+ case JSONNumberStateFractionalNumberStart:
1651+ case JSONNumberStateExponentPlusMinus:if(!((currentChar >= '0') && (currentChar <= '9'))) { /* XXX Add error message */ numberState = JSONNumberStateError; break; }
1652+ else { if(numberState == JSONNumberStateFractionalNumberStart) { numberState = JSONNumberStateFractionalNumber; }
1653+ else { numberState = JSONNumberStateExponent; } break; }
1654+ case JSONNumberStateWholeNumberZero:
1655+ case JSONNumberStateWholeNumber: if (currentChar == '.') { numberState = JSONNumberStateFractionalNumberStart; isFloatingPoint = 1; break; }
1656+ case JSONNumberStateFractionalNumber: if (lowerCaseCC == 'e') { numberState = JSONNumberStateExponentStart; isFloatingPoint = 1; break; }
1657+ case JSONNumberStateExponent: if(!((currentChar >= '0') && (currentChar <= '9')) || (numberState == JSONNumberStateWholeNumberZero)) { numberState = JSONNumberStateFinished; backup = 1; break; }
1658+ break;
1659+ default: /* XXX Add error message */ numberState = JSONNumberStateError; break;
1660+ }
1661+ }
1662+
1663+ parseState->token.tokenPtrRange.ptr = parseState->stringBuffer.bytes.ptr + startingIndex;
1664+ parseState->token.tokenPtrRange.length = (atNumberCharacter - parseState->token.tokenPtrRange.ptr) - backup;
1665+ parseState->atIndex = (parseState->token.tokenPtrRange.ptr + parseState->token.tokenPtrRange.length) - parseState->stringBuffer.bytes.ptr;
1666+
1667+ if(JK_EXPECT_T(numberState == JSONNumberStateFinished)) {
1668+ unsigned char numberTempBuf[parseState->token.tokenPtrRange.length + 4UL];
1669+ unsigned char *endOfNumber = NULL;
1670+
1671+ memcpy(numberTempBuf, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length);
1672+ numberTempBuf[parseState->token.tokenPtrRange.length] = 0;
1673+
1674+ errno = 0;
1675+
1676+ // Treat "-0" as a floating point number, which is capable of representing negative zeros.
1677+ if(JK_EXPECT_F(parseState->token.tokenPtrRange.length == 2UL) && JK_EXPECT_F(numberTempBuf[1] == '0') && JK_EXPECT_F(isNegative)) { isFloatingPoint = 1; }
1678+
1679+ if(isFloatingPoint) {
1680+ parseState->token.value.number.doubleValue = strtod((const char *)numberTempBuf, (char **)&endOfNumber); // strtod is documented to return U+2261 (identical to) 0.0 on an underflow error (along with setting errno to ERANGE).
1681+ parseState->token.value.type = JKValueTypeDouble;
1682+ parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.doubleValue;
1683+ parseState->token.value.ptrRange.length = sizeof(double);
1684+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type);
1685+ } else {
1686+ if(isNegative) {
1687+ parseState->token.value.number.longLongValue = strtoll((const char *)numberTempBuf, (char **)&endOfNumber, 10);
1688+ parseState->token.value.type = JKValueTypeLongLong;
1689+ parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.longLongValue;
1690+ parseState->token.value.ptrRange.length = sizeof(long long);
1691+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type) + (JKHash)parseState->token.value.number.longLongValue;
1692+ } else {
1693+ parseState->token.value.number.unsignedLongLongValue = strtoull((const char *)numberTempBuf, (char **)&endOfNumber, 10);
1694+ parseState->token.value.type = JKValueTypeUnsignedLongLong;
1695+ parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.unsignedLongLongValue;
1696+ parseState->token.value.ptrRange.length = sizeof(unsigned long long);
1697+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type) + (JKHash)parseState->token.value.number.unsignedLongLongValue;
1698+ }
1699+ }
1700+
1701+ if(JK_EXPECT_F(errno != 0)) {
1702+ numberState = JSONNumberStateError;
1703+ if(errno == ERANGE) {
1704+ switch(parseState->token.value.type) {
1705+ 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; // see above for == 0.0.
1706+ case JKValueTypeLongLong: jk_error(parseState, @"The value '%s' exceeded the minimum value that could be represented: %lld.", numberTempBuf, parseState->token.value.number.longLongValue); break;
1707+ case JKValueTypeUnsignedLongLong: jk_error(parseState, @"The value '%s' exceeded the maximum value that could be represented: %llu.", numberTempBuf, parseState->token.value.number.unsignedLongLongValue); break;
1708+ default: jk_error(parseState, @"Internal error: Unknown token value type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
1709+ }
1710+ }
1711+ }
1712+ if(JK_EXPECT_F(endOfNumber != &numberTempBuf[parseState->token.tokenPtrRange.length]) && JK_EXPECT_F(numberState != JSONNumberStateError)) { numberState = JSONNumberStateError; jk_error(parseState, @"The conversion function did not consume all of the number tokens characters."); }
1713+
1714+ size_t hashIndex = 0UL;
1715+ for(hashIndex = 0UL; hashIndex < parseState->token.value.ptrRange.length; hashIndex++) { parseState->token.value.hash = jk_calculateHash(parseState->token.value.hash, parseState->token.value.ptrRange.ptr[hashIndex]); }
1716+ }
1717+
1718+ if(JK_EXPECT_F(numberState != JSONNumberStateFinished)) { jk_error(parseState, @"Invalid number."); }
1719+ return(JK_EXPECT_T((numberState == JSONNumberStateFinished)) ? 0 : 1);
1720+}
1721+
1722+JK_STATIC_INLINE void jk_set_parsed_token(JKParseState *parseState, const unsigned char *ptr, size_t length, JKTokenType type, size_t advanceBy) {
1723+ parseState->token.tokenPtrRange.ptr = ptr;
1724+ parseState->token.tokenPtrRange.length = length;
1725+ parseState->token.type = type;
1726+ parseState->atIndex += advanceBy;
1727+}
1728+
1729+static size_t jk_parse_is_newline(JKParseState *parseState, const unsigned char *atCharacterPtr) {
1730+ NSCParameterAssert((parseState != NULL) && (atCharacterPtr != NULL) && (atCharacterPtr >= parseState->stringBuffer.bytes.ptr) && (atCharacterPtr < JK_END_STRING_PTR(parseState)));
1731+ const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
1732+
1733+ if(JK_EXPECT_F(atCharacterPtr >= endOfStringPtr)) { return(0UL); }
1734+
1735+ if(JK_EXPECT_F((*(atCharacterPtr + 0)) == '\n')) { return(1UL); }
1736+ if(JK_EXPECT_F((*(atCharacterPtr + 0)) == '\r')) { if((JK_EXPECT_T((atCharacterPtr + 1) < endOfStringPtr)) && ((*(atCharacterPtr + 1)) == '\n')) { return(2UL); } return(1UL); }
1737+ if(parseState->parseOptionFlags & JKParseOptionUnicodeNewlines) {
1738+ if((JK_EXPECT_F((*(atCharacterPtr + 0)) == 0xc2)) && (((atCharacterPtr + 1) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x85))) { return(2UL); }
1739+ if((JK_EXPECT_F((*(atCharacterPtr + 0)) == 0xe2)) && (((atCharacterPtr + 2) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x80) && (((*(atCharacterPtr + 2)) == 0xa8) || ((*(atCharacterPtr + 2)) == 0xa9)))) { return(3UL); }
1740+ }
1741+
1742+ return(0UL);
1743+}
1744+
1745+JK_STATIC_INLINE int jk_parse_skip_newline(JKParseState *parseState) {
1746+ size_t newlineAdvanceAtIndex = 0UL;
1747+ if(JK_EXPECT_F((newlineAdvanceAtIndex = jk_parse_is_newline(parseState, JK_AT_STRING_PTR(parseState))) > 0UL)) { parseState->lineNumber++; parseState->atIndex += (newlineAdvanceAtIndex - 1UL); parseState->lineStartIndex = parseState->atIndex + 1UL; return(1); }
1748+ return(0);
1749+}
1750+
1751+JK_STATIC_INLINE void jk_parse_skip_whitespace(JKParseState *parseState) {
1752+#ifndef __clang_analyzer__
1753+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
1754+ const unsigned char *atCharacterPtr = NULL;
1755+ const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
1756+
1757+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) {
1758+ if(((*(atCharacterPtr + 0)) == ' ') || ((*(atCharacterPtr + 0)) == '\t')) { continue; }
1759+ if(jk_parse_skip_newline(parseState)) { continue; }
1760+ if(parseState->parseOptionFlags & JKParseOptionComments) {
1761+ if((JK_EXPECT_F((*(atCharacterPtr + 0)) == '/')) && (JK_EXPECT_T((atCharacterPtr + 1) < endOfStringPtr))) {
1762+ if((*(atCharacterPtr + 1)) == '/') {
1763+ parseState->atIndex++;
1764+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) { if(jk_parse_skip_newline(parseState)) { break; } }
1765+ continue;
1766+ }
1767+ if((*(atCharacterPtr + 1)) == '*') {
1768+ parseState->atIndex++;
1769+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) {
1770+ if(jk_parse_skip_newline(parseState)) { continue; }
1771+ if(((*(atCharacterPtr + 0)) == '*') && (((atCharacterPtr + 1) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == '/'))) { parseState->atIndex++; break; }
1772+ }
1773+ continue;
1774+ }
1775+ }
1776+ }
1777+ break;
1778+ }
1779+#endif
1780+}
1781+
1782+static int jk_parse_next_token(JKParseState *parseState) {
1783+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
1784+ const unsigned char *atCharacterPtr = NULL;
1785+ const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
1786+ unsigned char currentCharacter = 0U;
1787+ int stopParsing = 0;
1788+
1789+ parseState->prev_atIndex = parseState->atIndex;
1790+ parseState->prev_lineNumber = parseState->lineNumber;
1791+ parseState->prev_lineStartIndex = parseState->lineStartIndex;
1792+
1793+ jk_parse_skip_whitespace(parseState);
1794+
1795+ if((JK_AT_STRING_PTR(parseState) == endOfStringPtr)) { stopParsing = 1; }
1796+
1797+ if((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr))) {
1798+ currentCharacter = *atCharacterPtr;
1799+
1800+ if(JK_EXPECT_T(currentCharacter == '"')) { if(JK_EXPECT_T((stopParsing = jk_parse_string(parseState)) == 0)) { jk_set_parsed_token(parseState, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length, JKTokenTypeString, 0UL); } }
1801+ else if(JK_EXPECT_T(currentCharacter == ':')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeSeparator, 1UL); }
1802+ else if(JK_EXPECT_T(currentCharacter == ',')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeComma, 1UL); }
1803+ else if((JK_EXPECT_T(currentCharacter >= '0') && JK_EXPECT_T(currentCharacter <= '9')) || JK_EXPECT_T(currentCharacter == '-')) { if(JK_EXPECT_T((stopParsing = jk_parse_number(parseState)) == 0)) { jk_set_parsed_token(parseState, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length, JKTokenTypeNumber, 0UL); } }
1804+ else if(JK_EXPECT_T(currentCharacter == '{')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeObjectBegin, 1UL); }
1805+ else if(JK_EXPECT_T(currentCharacter == '}')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeObjectEnd, 1UL); }
1806+ else if(JK_EXPECT_T(currentCharacter == '[')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeArrayBegin, 1UL); }
1807+ else if(JK_EXPECT_T(currentCharacter == ']')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeArrayEnd, 1UL); }
1808+
1809+ else if(JK_EXPECT_T(currentCharacter == 't')) { if(!((JK_EXPECT_T((atCharacterPtr + 4UL) < endOfStringPtr)) && (JK_EXPECT_T(atCharacterPtr[1] == 'r')) && (JK_EXPECT_T(atCharacterPtr[2] == 'u')) && (JK_EXPECT_T(atCharacterPtr[3] == 'e')))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 4UL, JKTokenTypeTrue, 4UL); } }
1810+ else if(JK_EXPECT_T(currentCharacter == 'f')) { if(!((JK_EXPECT_T((atCharacterPtr + 5UL) < endOfStringPtr)) && (JK_EXPECT_T(atCharacterPtr[1] == 'a')) && (JK_EXPECT_T(atCharacterPtr[2] == 'l')) && (JK_EXPECT_T(atCharacterPtr[3] == 's')) && (JK_EXPECT_T(atCharacterPtr[4] == 'e')))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 5UL, JKTokenTypeFalse, 5UL); } }
1811+ else if(JK_EXPECT_T(currentCharacter == 'n')) { if(!((JK_EXPECT_T((atCharacterPtr + 4UL) < endOfStringPtr)) && (JK_EXPECT_T(atCharacterPtr[1] == 'u')) && (JK_EXPECT_T(atCharacterPtr[2] == 'l')) && (JK_EXPECT_T(atCharacterPtr[3] == 'l')))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 4UL, JKTokenTypeNull, 4UL); } }
1812+ else { stopParsing = 1; /* XXX Add error message */ }
1813+ }
1814+
1815+ if(JK_EXPECT_F(stopParsing)) { jk_error(parseState, @"Unexpected token, wanted '{', '}', '[', ']', ',', ':', 'true', 'false', 'null', '\"STRING\"', 'NUMBER'."); }
1816+ return(stopParsing);
1817+}
1818+
1819+static void jk_error_parse_accept_or3(JKParseState *parseState, int state, NSString *or1String, NSString *or2String, NSString *or3String) {
1820+ NSString *acceptStrings[16];
1821+ int acceptIdx = 0;
1822+ if(state & JKParseAcceptValue) { acceptStrings[acceptIdx++] = or1String; }
1823+ if(state & JKParseAcceptComma) { acceptStrings[acceptIdx++] = or2String; }
1824+ if(state & JKParseAcceptEnd) { acceptStrings[acceptIdx++] = or3String; }
1825+ if(acceptIdx == 1) { jk_error(parseState, @"Expected %@, not '%*.*s'", acceptStrings[0], (int)parseState->token.tokenPtrRange.length, (int)parseState->token.tokenPtrRange.length, parseState->token.tokenPtrRange.ptr); }
1826+ else if(acceptIdx == 2) { jk_error(parseState, @"Expected %@ or %@, not '%*.*s'", acceptStrings[0], acceptStrings[1], (int)parseState->token.tokenPtrRange.length, (int)parseState->token.tokenPtrRange.length, parseState->token.tokenPtrRange.ptr); }
1827+ else if(acceptIdx == 3) { jk_error(parseState, @"Expected %@, %@, or %@, not '%*.*s", acceptStrings[0], acceptStrings[1], acceptStrings[2], (int)parseState->token.tokenPtrRange.length, (int)parseState->token.tokenPtrRange.length, parseState->token.tokenPtrRange.ptr); }
1828+}
1829+
1830+static void *jk_parse_array(JKParseState *parseState) {
1831+ size_t startingObjectIndex = parseState->objectStack.index;
1832+ int arrayState = JKParseAcceptValueOrEnd, stopParsing = 0;
1833+ void *parsedArray = NULL;
1834+
1835+ while(JK_EXPECT_T((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length)))) {
1836+ if(JK_EXPECT_F(parseState->objectStack.index > (parseState->objectStack.count - 4UL))) { 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; } }
1837+
1838+ if(JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0)) {
1839+ void *object = NULL;
1840+#ifndef NS_BLOCK_ASSERTIONS
1841+ parseState->objectStack.objects[parseState->objectStack.index] = NULL;
1842+ parseState->objectStack.keys [parseState->objectStack.index] = NULL;
1843+#endif
1844+ switch(parseState->token.type) {
1845+ case JKTokenTypeNumber:
1846+ case JKTokenTypeString:
1847+ case JKTokenTypeTrue:
1848+ case JKTokenTypeFalse:
1849+ case JKTokenTypeNull:
1850+ case JKTokenTypeArrayBegin:
1851+ case JKTokenTypeObjectBegin:
1852+ if(JK_EXPECT_F((arrayState & JKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected value."); stopParsing = 1; break; }
1853+ if(JK_EXPECT_F((object = jk_object_for_token(parseState)) == NULL)) { jk_error(parseState, @"Internal error: Object == NULL"); stopParsing = 1; break; } else { parseState->objectStack.objects[parseState->objectStack.index++] = object; arrayState = JKParseAcceptCommaOrEnd; }
1854+ break;
1855+ case JKTokenTypeArrayEnd: if(JK_EXPECT_T(arrayState & JKParseAcceptEnd)) { NSCParameterAssert(parseState->objectStack.index >= startingObjectIndex); parsedArray = (void *)_JKArrayCreate((id *)&parseState->objectStack.objects[startingObjectIndex], (parseState->objectStack.index - startingObjectIndex), parseState->mutableCollections); } else { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected ']'."); } stopParsing = 1; break;
1856+ case JKTokenTypeComma: if(JK_EXPECT_T(arrayState & JKParseAcceptComma)) { arrayState = JKParseAcceptValue; } else { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected ','."); stopParsing = 1; } break;
1857+ default: parseState->errorIsPrev = 1; jk_error_parse_accept_or3(parseState, arrayState, @"a value", @"a comma", @"a ']'"); stopParsing = 1; break;
1858+ }
1859+ }
1860+ }
1861+
1862+ if(JK_EXPECT_F(parsedArray == NULL)) { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { if(parseState->objectStack.objects[idx] != NULL) { CFRelease(parseState->objectStack.objects[idx]); parseState->objectStack.objects[idx] = NULL; } } }
1863+#if !defined(NS_BLOCK_ASSERTIONS)
1864+ else { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { parseState->objectStack.objects[idx] = NULL; parseState->objectStack.keys[idx] = NULL; } }
1865+#endif
1866+
1867+ parseState->objectStack.index = startingObjectIndex;
1868+ return(parsedArray);
1869+}
1870+
1871+static void *jk_create_dictionary(JKParseState *parseState, size_t startingObjectIndex) {
1872+ void *parsedDictionary = NULL;
1873+
1874+ parseState->objectStack.index--;
1875+
1876+ parsedDictionary = _JKDictionaryCreate((id *)&parseState->objectStack.keys[startingObjectIndex], (NSUInteger *)&parseState->objectStack.cfHashes[startingObjectIndex], (id *)&parseState->objectStack.objects[startingObjectIndex], (parseState->objectStack.index - startingObjectIndex), parseState->mutableCollections);
1877+
1878+ return(parsedDictionary);
1879+}
1880+
1881+static void *jk_parse_dictionary(JKParseState *parseState) {
1882+ size_t startingObjectIndex = parseState->objectStack.index;
1883+ int dictState = JKParseAcceptValueOrEnd, stopParsing = 0;
1884+ void *parsedDictionary = NULL;
1885+
1886+ while(JK_EXPECT_T((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length)))) {
1887+ if(JK_EXPECT_F(parseState->objectStack.index > (parseState->objectStack.count - 4UL))) { 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; } }
1888+
1889+ size_t objectStackIndex = parseState->objectStack.index++;
1890+ parseState->objectStack.keys[objectStackIndex] = NULL;
1891+ parseState->objectStack.objects[objectStackIndex] = NULL;
1892+ void *key = NULL, *object = NULL;
1893+
1894+ if(JK_EXPECT_T((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0)))) {
1895+ switch(parseState->token.type) {
1896+ case JKTokenTypeString:
1897+ if(JK_EXPECT_F((dictState & JKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected string."); stopParsing = 1; break; }
1898+ if(JK_EXPECT_F((key = jk_object_for_token(parseState)) == NULL)) { jk_error(parseState, @"Internal error: Key == NULL."); stopParsing = 1; break; }
1899+ else {
1900+ parseState->objectStack.keys[objectStackIndex] = key;
1901+ if(JK_EXPECT_T(parseState->token.value.cacheItem != NULL)) { if(JK_EXPECT_F(parseState->token.value.cacheItem->cfHash == 0UL)) { parseState->token.value.cacheItem->cfHash = CFHash(key); } parseState->objectStack.cfHashes[objectStackIndex] = parseState->token.value.cacheItem->cfHash; }
1902+ else { parseState->objectStack.cfHashes[objectStackIndex] = CFHash(key); }
1903+ }
1904+ break;
1905+
1906+ case JKTokenTypeObjectEnd: if((JK_EXPECT_T(dictState & JKParseAcceptEnd))) { NSCParameterAssert(parseState->objectStack.index >= startingObjectIndex); parsedDictionary = jk_create_dictionary(parseState, startingObjectIndex); } else { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected '}'."); } stopParsing = 1; break;
1907+ case JKTokenTypeComma: if((JK_EXPECT_T(dictState & JKParseAcceptComma))) { dictState = JKParseAcceptValue; parseState->objectStack.index--; continue; } else { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected ','."); stopParsing = 1; } break;
1908+
1909+ default: parseState->errorIsPrev = 1; jk_error_parse_accept_or3(parseState, dictState, @"a \"STRING\"", @"a comma", @"a '}'"); stopParsing = 1; break;
1910+ }
1911+ }
1912+
1913+ if(JK_EXPECT_T(stopParsing == 0)) {
1914+ if(JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0)) { if(JK_EXPECT_F(parseState->token.type != JKTokenTypeSeparator)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Expected ':'."); stopParsing = 1; } }
1915+ }
1916+
1917+ if((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0))) {
1918+ switch(parseState->token.type) {
1919+ case JKTokenTypeNumber:
1920+ case JKTokenTypeString:
1921+ case JKTokenTypeTrue:
1922+ case JKTokenTypeFalse:
1923+ case JKTokenTypeNull:
1924+ case JKTokenTypeArrayBegin:
1925+ case JKTokenTypeObjectBegin:
1926+ if(JK_EXPECT_F((dictState & JKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected value."); stopParsing = 1; break; }
1927+ if(JK_EXPECT_F((object = jk_object_for_token(parseState)) == NULL)) { jk_error(parseState, @"Internal error: Object == NULL."); stopParsing = 1; break; } else { parseState->objectStack.objects[objectStackIndex] = object; dictState = JKParseAcceptCommaOrEnd; }
1928+ break;
1929+ default: parseState->errorIsPrev = 1; jk_error_parse_accept_or3(parseState, dictState, @"a value", @"a comma", @"a '}'"); stopParsing = 1; break;
1930+ }
1931+ }
1932+ }
1933+
1934+ if(JK_EXPECT_F(parsedDictionary == NULL)) { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { if(parseState->objectStack.keys[idx] != NULL) { CFRelease(parseState->objectStack.keys[idx]); parseState->objectStack.keys[idx] = NULL; } if(parseState->objectStack.objects[idx] != NULL) { CFRelease(parseState->objectStack.objects[idx]); parseState->objectStack.objects[idx] = NULL; } } }
1935+#if !defined(NS_BLOCK_ASSERTIONS)
1936+ else { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { parseState->objectStack.objects[idx] = NULL; parseState->objectStack.keys[idx] = NULL; } }
1937+#endif
1938+
1939+ parseState->objectStack.index = startingObjectIndex;
1940+ return(parsedDictionary);
1941+}
1942+
1943+static id json_parse_it(JKParseState *parseState) {
1944+ id parsedObject = NULL;
1945+ int stopParsing = 0;
1946+
1947+ while((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length))) {
1948+ if((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0))) {
1949+ switch(parseState->token.type) {
1950+ case JKTokenTypeArrayBegin:
1951+ case JKTokenTypeObjectBegin: parsedObject = [(id)jk_object_for_token(parseState) autorelease]; stopParsing = 1; break;
1952+ default: jk_error(parseState, @"Expected either '[' or '{'."); stopParsing = 1; break;
1953+ }
1954+ }
1955+ }
1956+
1957+ NSCParameterAssert((parseState->objectStack.index == 0) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
1958+
1959+ if((parsedObject == NULL) && (JK_AT_STRING_PTR(parseState) == JK_END_STRING_PTR(parseState))) { jk_error(parseState, @"Reached the end of the buffer."); }
1960+ if(parsedObject == NULL) { jk_error(parseState, @"Unable to parse JSON."); }
1961+
1962+ if((parsedObject != NULL) && (JK_AT_STRING_PTR(parseState) < JK_END_STRING_PTR(parseState))) {
1963+ jk_parse_skip_whitespace(parseState);
1964+ if((parsedObject != NULL) && ((parseState->parseOptionFlags & JKParseOptionPermitTextAfterValidJSON) == 0) && (JK_AT_STRING_PTR(parseState) < JK_END_STRING_PTR(parseState))) {
1965+ jk_error(parseState, @"A valid JSON object was parsed but there were additional non-white-space characters remaining.");
1966+ parsedObject = NULL;
1967+ }
1968+ }
1969+
1970+ return(parsedObject);
1971+}
1972+
1973+////////////
1974+#pragma mark -
1975+#pragma mark Object cache
1976+
1977+// This uses a Galois Linear Feedback Shift Register (LFSR) PRNG to pick which item in the cache to age. It has a period of (2^32)-1.
1978+// NOTE: A LFSR *MUST* be initialized to a non-zero value and must always have a non-zero value. The LFSR is initalized to 1 in -initWithParseOptions:
1979+JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) {
1980+ NSCParameterAssert((parseState != NULL) && (parseState->cache.prng_lfsr != 0U));
1981+ parseState->cache.prng_lfsr = (parseState->cache.prng_lfsr >> 1) ^ ((0U - (parseState->cache.prng_lfsr & 1U)) & 0x80200003U);
1982+ parseState->cache.age[parseState->cache.prng_lfsr & (parseState->cache.count - 1UL)] >>= 1;
1983+}
1984+
1985+// The object cache is nothing more than a hash table with open addressing collision resolution that is bounded by JK_CACHE_PROBES attempts.
1986+//
1987+// The hash table is a linear C array of JKTokenCacheItem. The terms "item" and "bucket" are synonymous with the index in to the cache array, i.e. cache.items[bucket].
1988+//
1989+// Items in the cache have an age associated with them. An items age is incremented using saturating unsigned arithmetic and decremeted using unsigned right shifts.
1990+// Thus, an items age is managed using an AIMD policy- additive increase, multiplicative decrease. All age calculations and manipulations are branchless.
1991+// The primitive C type MUST be unsigned. It is currently a "char", which allows (at a minimum and in practice) 8 bits.
1992+//
1993+// A "useable bucket" is a bucket that is not in use (never populated), or has an age == 0.
1994+//
1995+// When an item is found in the cache, it's age is incremented.
1996+// If a useable bucket hasn't been found, the current item (bucket) is aged along with two random items.
1997+//
1998+// If a value is not found in the cache, and no useable bucket has been found, that value is not added to the cache.
1999+
2000+static void *jk_cachedObjects(JKParseState *parseState) {
2001+ unsigned long bucket = parseState->token.value.hash & (parseState->cache.count - 1UL), setBucket = 0UL, useableBucket = 0UL, x = 0UL;
2002+ void *parsedAtom = NULL;
2003+
2004+ if(JK_EXPECT_F(parseState->token.value.ptrRange.length == 0UL) && JK_EXPECT_T(parseState->token.value.type == JKValueTypeString)) { return(@""); }
2005+
2006+ for(x = 0UL; x < JK_CACHE_PROBES; x++) {
2007+ if(JK_EXPECT_F(parseState->cache.items[bucket].object == NULL)) { setBucket = 1UL; useableBucket = bucket; break; }
2008+
2009+ if((JK_EXPECT_T(parseState->cache.items[bucket].hash == parseState->token.value.hash)) && (JK_EXPECT_T(parseState->cache.items[bucket].size == parseState->token.value.ptrRange.length)) && (JK_EXPECT_T(parseState->cache.items[bucket].type == parseState->token.value.type)) && (JK_EXPECT_T(parseState->cache.items[bucket].bytes != NULL)) && (JK_EXPECT_T(memcmp(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length) == 0U))) {
2010+ parseState->cache.age[bucket] = (((uint32_t)parseState->cache.age[bucket]) + 1U) - (((((uint32_t)parseState->cache.age[bucket]) + 1U) >> 31) ^ 1U);
2011+ parseState->token.value.cacheItem = &parseState->cache.items[bucket];
2012+ NSCParameterAssert(parseState->cache.items[bucket].object != NULL);
2013+ return((void *)CFRetain(parseState->cache.items[bucket].object));
2014+ } else {
2015+ if(JK_EXPECT_F(setBucket == 0UL) && JK_EXPECT_F(parseState->cache.age[bucket] == 0U)) { setBucket = 1UL; useableBucket = bucket; }
2016+ if(JK_EXPECT_F(setBucket == 0UL)) { parseState->cache.age[bucket] >>= 1; jk_cache_age(parseState); jk_cache_age(parseState); }
2017+ // This is the open addressing function. The values length and type are used as a form of "double hashing" to distribute values with the same effective value hash across different object cache buckets.
2018+ // The values type is a prime number that is relatively coprime to the other primes in the set of value types and the number of hash table buckets.
2019+ bucket = (parseState->token.value.hash + (parseState->token.value.ptrRange.length * (x + 1UL)) + (parseState->token.value.type * (x + 1UL)) + (3UL * (x + 1UL))) & (parseState->cache.count - 1UL);
2020+ }
2021+ }
2022+
2023+ switch(parseState->token.value.type) {
2024+ case JKValueTypeString: parsedAtom = (void *)CFStringCreateWithBytes(NULL, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length, kCFStringEncodingUTF8, 0); break;
2025+ case JKValueTypeLongLong: parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberLongLongType, &parseState->token.value.number.longLongValue); break;
2026+ case JKValueTypeUnsignedLongLong:
2027+ if(parseState->token.value.number.unsignedLongLongValue <= LLONG_MAX) { parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberLongLongType, &parseState->token.value.number.unsignedLongLongValue); }
2028+ else { parsedAtom = (void *)parseState->objCImpCache.NSNumberInitWithUnsignedLongLong(parseState->objCImpCache.NSNumberAlloc(parseState->objCImpCache.NSNumberClass, @selector(alloc)), @selector(initWithUnsignedLongLong:), parseState->token.value.number.unsignedLongLongValue); }
2029+ break;
2030+ case JKValueTypeDouble: parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberDoubleType, &parseState->token.value.number.doubleValue); break;
2031+ default: jk_error(parseState, @"Internal error: Unknown token value type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
2032+ }
2033+
2034+ if(JK_EXPECT_T(setBucket) && (JK_EXPECT_T(parsedAtom != NULL))) {
2035+ bucket = useableBucket;
2036+ if(JK_EXPECT_T((parseState->cache.items[bucket].object != NULL))) { CFRelease(parseState->cache.items[bucket].object); parseState->cache.items[bucket].object = NULL; }
2037+
2038+ if(JK_EXPECT_T((parseState->cache.items[bucket].bytes = (unsigned char *)reallocf(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.length)) != NULL)) {
2039+ memcpy(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length);
2040+ parseState->cache.items[bucket].object = (void *)CFRetain(parsedAtom);
2041+ parseState->cache.items[bucket].hash = parseState->token.value.hash;
2042+ parseState->cache.items[bucket].cfHash = 0UL;
2043+ parseState->cache.items[bucket].size = parseState->token.value.ptrRange.length;
2044+ parseState->cache.items[bucket].type = parseState->token.value.type;
2045+ parseState->token.value.cacheItem = &parseState->cache.items[bucket];
2046+ parseState->cache.age[bucket] = JK_INIT_CACHE_AGE;
2047+ } else { // The realloc failed, so clear the appropriate fields.
2048+ parseState->cache.items[bucket].hash = 0UL;
2049+ parseState->cache.items[bucket].cfHash = 0UL;
2050+ parseState->cache.items[bucket].size = 0UL;
2051+ parseState->cache.items[bucket].type = 0UL;
2052+ }
2053+ }
2054+
2055+ return(parsedAtom);
2056+}
2057+
2058+
2059+static void *jk_object_for_token(JKParseState *parseState) {
2060+ void *parsedAtom = NULL;
2061+
2062+ parseState->token.value.cacheItem = NULL;
2063+ switch(parseState->token.type) {
2064+ case JKTokenTypeString: parsedAtom = jk_cachedObjects(parseState); break;
2065+ case JKTokenTypeNumber: parsedAtom = jk_cachedObjects(parseState); break;
2066+ case JKTokenTypeObjectBegin: parsedAtom = jk_parse_dictionary(parseState); break;
2067+ case JKTokenTypeArrayBegin: parsedAtom = jk_parse_array(parseState); break;
2068+ case JKTokenTypeTrue: parsedAtom = (void *)kCFBooleanTrue; break;
2069+ case JKTokenTypeFalse: parsedAtom = (void *)kCFBooleanFalse; break;
2070+ case JKTokenTypeNull: parsedAtom = (void *)kCFNull; break;
2071+ default: jk_error(parseState, @"Internal error: Unknown token type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
2072+ }
2073+
2074+ return(parsedAtom);
2075+}
2076+
2077+#pragma mark -
2078+@implementation JSONDecoder
2079+
2080++ (id)decoder
2081+{
2082+ return([self decoderWithParseOptions:JKParseOptionStrict]);
2083+}
2084+
2085++ (id)decoderWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2086+{
2087+ return([[[self alloc] initWithParseOptions:parseOptionFlags] autorelease]);
2088+}
2089+
2090+- (id)init
2091+{
2092+ return([self initWithParseOptions:JKParseOptionStrict]);
2093+}
2094+
2095+- (id)initWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2096+{
2097+ if((self = [super init]) == NULL) { return(NULL); }
2098+
2099+ if(parseOptionFlags & ~JKParseOptionValidFlags) { [self autorelease]; [NSException raise:NSInvalidArgumentException format:@"Invalid parse options."]; }
2100+
2101+ if((parseState = (JKParseState *)calloc(1UL, sizeof(JKParseState))) == NULL) { goto errorExit; }
2102+
2103+ parseState->parseOptionFlags = parseOptionFlags;
2104+
2105+ parseState->token.tokenBuffer.roundSizeUpToMultipleOf = 4096UL;
2106+ parseState->objectStack.roundSizeUpToMultipleOf = 2048UL;
2107+
2108+ parseState->objCImpCache.NSNumberClass = _jk_NSNumberClass;
2109+ parseState->objCImpCache.NSNumberAlloc = _jk_NSNumberAllocImp;
2110+ parseState->objCImpCache.NSNumberInitWithUnsignedLongLong = _jk_NSNumberInitWithUnsignedLongLongImp;
2111+
2112+ parseState->cache.prng_lfsr = 1U;
2113+ parseState->cache.count = JK_CACHE_SLOTS;
2114+ if((parseState->cache.items = (JKTokenCacheItem *)calloc(1UL, sizeof(JKTokenCacheItem) * parseState->cache.count)) == NULL) { goto errorExit; }
2115+
2116+ return(self);
2117+
2118+ errorExit:
2119+ if(self) { [self autorelease]; self = NULL; }
2120+ return(NULL);
2121+}
2122+
2123+// This is here primarily to support the NSString and NSData convenience functions so the autoreleased JSONDecoder can release most of its resources before the pool pops.
2124+static void _JSONDecoderCleanup(JSONDecoder *decoder) {
2125+ if((decoder != NULL) && (decoder->parseState != NULL)) {
2126+ jk_managedBuffer_release(&decoder->parseState->token.tokenBuffer);
2127+ jk_objectStack_release(&decoder->parseState->objectStack);
2128+
2129+ [decoder clearCache];
2130+ if(decoder->parseState->cache.items != NULL) { free(decoder->parseState->cache.items); decoder->parseState->cache.items = NULL; }
2131+
2132+ free(decoder->parseState); decoder->parseState = NULL;
2133+ }
2134+}
2135+
2136+- (void)dealloc
2137+{
2138+ _JSONDecoderCleanup(self);
2139+ [super dealloc];
2140+}
2141+
2142+- (void)clearCache
2143+{
2144+ if(JK_EXPECT_T(parseState != NULL)) {
2145+ if(JK_EXPECT_T(parseState->cache.items != NULL)) {
2146+ size_t idx = 0UL;
2147+ for(idx = 0UL; idx < parseState->cache.count; idx++) {
2148+ if(JK_EXPECT_T(parseState->cache.items[idx].object != NULL)) { CFRelease(parseState->cache.items[idx].object); parseState->cache.items[idx].object = NULL; }
2149+ if(JK_EXPECT_T(parseState->cache.items[idx].bytes != NULL)) { free(parseState->cache.items[idx].bytes); parseState->cache.items[idx].bytes = NULL; }
2150+ memset(&parseState->cache.items[idx], 0, sizeof(JKTokenCacheItem));
2151+ parseState->cache.age[idx] = 0U;
2152+ }
2153+ }
2154+ }
2155+}
2156+
2157+// This needs to be completely rewritten.
2158+static id _JKParseUTF8String(JKParseState *parseState, BOOL mutableCollections, const unsigned char *string, size_t length, NSError **error) {
2159+ NSCParameterAssert((parseState != NULL) && (string != NULL) && (parseState->cache.prng_lfsr != 0U));
2160+ parseState->stringBuffer.bytes.ptr = string;
2161+ parseState->stringBuffer.bytes.length = length;
2162+ parseState->atIndex = 0UL;
2163+ parseState->lineNumber = 1UL;
2164+ parseState->lineStartIndex = 0UL;
2165+ parseState->prev_atIndex = 0UL;
2166+ parseState->prev_lineNumber = 1UL;
2167+ parseState->prev_lineStartIndex = 0UL;
2168+ parseState->error = NULL;
2169+ parseState->errorIsPrev = 0;
2170+ parseState->mutableCollections = (mutableCollections == NO) ? NO : YES;
2171+
2172+ unsigned char stackTokenBuffer[JK_TOKENBUFFER_SIZE] JK_ALIGNED(64);
2173+ jk_managedBuffer_setToStackBuffer(&parseState->token.tokenBuffer, stackTokenBuffer, sizeof(stackTokenBuffer));
2174+
2175+ void *stackObjects [JK_STACK_OBJS] JK_ALIGNED(64);
2176+ void *stackKeys [JK_STACK_OBJS] JK_ALIGNED(64);
2177+ CFHashCode stackCFHashes[JK_STACK_OBJS] JK_ALIGNED(64);
2178+ jk_objectStack_setToStackBuffer(&parseState->objectStack, stackObjects, stackKeys, stackCFHashes, JK_STACK_OBJS);
2179+
2180+ id parsedJSON = json_parse_it(parseState);
2181+
2182+ if((error != NULL) && (parseState->error != NULL)) { *error = parseState->error; }
2183+
2184+ jk_managedBuffer_release(&parseState->token.tokenBuffer);
2185+ jk_objectStack_release(&parseState->objectStack);
2186+
2187+ parseState->stringBuffer.bytes.ptr = NULL;
2188+ parseState->stringBuffer.bytes.length = 0UL;
2189+ parseState->atIndex = 0UL;
2190+ parseState->lineNumber = 1UL;
2191+ parseState->lineStartIndex = 0UL;
2192+ parseState->prev_atIndex = 0UL;
2193+ parseState->prev_lineNumber = 1UL;
2194+ parseState->prev_lineStartIndex = 0UL;
2195+ parseState->error = NULL;
2196+ parseState->errorIsPrev = 0;
2197+ parseState->mutableCollections = NO;
2198+
2199+ return(parsedJSON);
2200+}
2201+
2202+////////////
2203+#pragma mark Deprecated as of v1.4
2204+////////////
2205+
2206+// Deprecated in JSONKit v1.4. Use objectWithUTF8String:length: instead.
2207+- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length
2208+{
2209+ return([self objectWithUTF8String:string length:length error:NULL]);
2210+}
2211+
2212+// Deprecated in JSONKit v1.4. Use objectWithUTF8String:length:error: instead.
2213+- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length error:(NSError **)error
2214+{
2215+ return([self objectWithUTF8String:string length:length error:error]);
2216+}
2217+
2218+// Deprecated in JSONKit v1.4. Use objectWithData: instead.
2219+- (id)parseJSONData:(NSData *)jsonData
2220+{
2221+ return([self objectWithData:jsonData error:NULL]);
2222+}
2223+
2224+// Deprecated in JSONKit v1.4. Use objectWithData:error: instead.
2225+- (id)parseJSONData:(NSData *)jsonData error:(NSError **)error
2226+{
2227+ return([self objectWithData:jsonData error:error]);
2228+}
2229+
2230+////////////
2231+#pragma mark Methods that return immutable collection objects
2232+////////////
2233+
2234+- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length
2235+{
2236+ return([self objectWithUTF8String:string length:length error:NULL]);
2237+}
2238+
2239+- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error
2240+{
2241+ if(parseState == NULL) { [NSException raise:NSInternalInconsistencyException format:@"parseState is NULL."]; }
2242+ if(string == NULL) { [NSException raise:NSInvalidArgumentException format:@"The string argument is NULL."]; }
2243+
2244+ return(_JKParseUTF8String(parseState, NO, string, (size_t)length, error));
2245+}
2246+
2247+- (id)objectWithData:(NSData *)jsonData
2248+{
2249+ return([self objectWithData:jsonData error:NULL]);
2250+}
2251+
2252+- (id)objectWithData:(NSData *)jsonData error:(NSError **)error
2253+{
2254+ if(jsonData == NULL) { [NSException raise:NSInvalidArgumentException format:@"The jsonData argument is NULL."]; }
2255+ return([self objectWithUTF8String:(const unsigned char *)[jsonData bytes] length:[jsonData length] error:error]);
2256+}
2257+
2258+////////////
2259+#pragma mark Methods that return mutable collection objects
2260+////////////
2261+
2262+- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length
2263+{
2264+ return([self mutableObjectWithUTF8String:string length:length error:NULL]);
2265+}
2266+
2267+- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error
2268+{
2269+ if(parseState == NULL) { [NSException raise:NSInternalInconsistencyException format:@"parseState is NULL."]; }
2270+ if(string == NULL) { [NSException raise:NSInvalidArgumentException format:@"The string argument is NULL."]; }
2271+
2272+ return(_JKParseUTF8String(parseState, YES, string, (size_t)length, error));
2273+}
2274+
2275+- (id)mutableObjectWithData:(NSData *)jsonData
2276+{
2277+ return([self mutableObjectWithData:jsonData error:NULL]);
2278+}
2279+
2280+- (id)mutableObjectWithData:(NSData *)jsonData error:(NSError **)error
2281+{
2282+ if(jsonData == NULL) { [NSException raise:NSInvalidArgumentException format:@"The jsonData argument is NULL."]; }
2283+ return([self mutableObjectWithUTF8String:(const unsigned char *)[jsonData bytes] length:[jsonData length] error:error]);
2284+}
2285+
2286+@end
2287+
2288+/*
2289+ The NSString and NSData convenience methods need a little bit of explanation.
2290+
2291+ Prior to JSONKit v1.4, the NSString -objectFromJSONStringWithParseOptions:error: method looked like
2292+
2293+ const unsigned char *utf8String = (const unsigned char *)[self UTF8String];
2294+ if(utf8String == NULL) { return(NULL); }
2295+ size_t utf8Length = strlen((const char *)utf8String);
2296+ return([[JSONDecoder decoderWithParseOptions:parseOptionFlags] parseUTF8String:utf8String length:utf8Length error:error]);
2297+
2298+ This changed with v1.4 to a more complicated method. The reason for this is to keep the amount of memory that is
2299+ allocated, but not yet freed because it is dependent on the autorelease pool to pop before it can be reclaimed.
2300+
2301+ In the simpler v1.3 code, this included all the bytes used to store the -UTF8String along with the JSONDecoder and all its overhead.
2302+
2303+ Now we use an autoreleased CFMutableData that is sized to the UTF8 length of the NSString in question and is used to hold the UTF8
2304+ conversion of said string.
2305+
2306+ Once parsed, the CFMutableData has its length set to 0. This should, hopefully, allow the CFMutableData to realloc and/or free
2307+ the buffer.
2308+
2309+ Another change made was a slight modification to JSONDecoder so that most of the cleanup work that was done in -dealloc was moved
2310+ to a private, internal function. These convenience routines keep the pointer to the autoreleased JSONDecoder and calls
2311+ _JSONDecoderCleanup() to early release the decoders resources since we already know that particular decoder is not going to be used
2312+ again.
2313+
2314+ If everything goes smoothly, this will most likely result in perhaps a few hundred bytes that are allocated but waiting for the
2315+ autorelease pool to pop. This is compared to the thousands and easily hundreds of thousands of bytes that would have been in
2316+ autorelease limbo. It's more complicated for us, but a win for the user.
2317+
2318+ Autorelease objects are used in case things don't go smoothly. By having them autoreleased, we effectively guarantee that our
2319+ requirement to -release the object is always met, not matter what goes wrong. The downside is having a an object or two in
2320+ autorelease limbo, but we've done our best to minimize that impact, so it all balances out.
2321+ */
2322+
2323+@implementation NSString (JSONKitDeserializing)
2324+
2325+static id _NSStringObjectFromJSONString(NSString *jsonString, JKParseOptionFlags parseOptionFlags, NSError **error, BOOL mutableCollection) {
2326+ id returnObject = NULL;
2327+ CFMutableDataRef mutableData = NULL;
2328+ JSONDecoder *decoder = NULL;
2329+
2330+ CFIndex stringLength = CFStringGetLength((CFStringRef)jsonString);
2331+ NSUInteger stringUTF8Length = [jsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
2332+
2333+ if((mutableData = (CFMutableDataRef)[(id)CFDataCreateMutable(NULL, (NSUInteger)stringUTF8Length) autorelease]) != NULL) {
2334+ UInt8 *utf8String = CFDataGetMutableBytePtr(mutableData);
2335+ CFIndex usedBytes = 0L, convertedCount = 0L;
2336+
2337+ convertedCount = CFStringGetBytes((CFStringRef)jsonString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', NO, utf8String, (NSUInteger)stringUTF8Length, &usedBytes);
2338+ if(JK_EXPECT_F(convertedCount != stringLength) || JK_EXPECT_F(usedBytes < 0L)) { if(error != NULL) { *error = [NSError errorWithDomain:@"JKErrorDomain" code:-1L userInfo:[NSDictionary dictionaryWithObject:@"An error occurred converting the contents of a NSString to UTF8." forKey:NSLocalizedDescriptionKey]]; } goto exitNow; }
2339+
2340+ if(mutableCollection == NO) { returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) objectWithUTF8String:(const unsigned char *)utf8String length:(size_t)usedBytes error:error]; }
2341+ else { returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) mutableObjectWithUTF8String:(const unsigned char *)utf8String length:(size_t)usedBytes error:error]; }
2342+ }
2343+
2344+exitNow:
2345+ if(mutableData != NULL) { CFDataSetLength(mutableData, 0L); }
2346+ if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
2347+ return(returnObject);
2348+}
2349+
2350+- (id)objectFromJSONString
2351+{
2352+ return([self objectFromJSONStringWithParseOptions:JKParseOptionStrict error:NULL]);
2353+}
2354+
2355+- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2356+{
2357+ return([self objectFromJSONStringWithParseOptions:parseOptionFlags error:NULL]);
2358+}
2359+
2360+- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
2361+{
2362+ return(_NSStringObjectFromJSONString(self, parseOptionFlags, error, NO));
2363+}
2364+
2365+
2366+- (id)mutableObjectFromJSONString
2367+{
2368+ return([self mutableObjectFromJSONStringWithParseOptions:JKParseOptionStrict error:NULL]);
2369+}
2370+
2371+- (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2372+{
2373+ return([self mutableObjectFromJSONStringWithParseOptions:parseOptionFlags error:NULL]);
2374+}
2375+
2376+- (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
2377+{
2378+ return(_NSStringObjectFromJSONString(self, parseOptionFlags, error, YES));
2379+}
2380+
2381+@end
2382+
2383+@implementation NSData (JSONKitDeserializing)
2384+
2385+- (id)objectFromJSONData
2386+{
2387+ return([self objectFromJSONDataWithParseOptions:JKParseOptionStrict error:NULL]);
2388+}
2389+
2390+- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2391+{
2392+ return([self objectFromJSONDataWithParseOptions:parseOptionFlags error:NULL]);
2393+}
2394+
2395+- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
2396+{
2397+ JSONDecoder *decoder = NULL;
2398+ id returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) objectWithData:self error:error];
2399+ if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
2400+ return(returnObject);
2401+}
2402+
2403+- (id)mutableObjectFromJSONData
2404+{
2405+ return([self mutableObjectFromJSONDataWithParseOptions:JKParseOptionStrict error:NULL]);
2406+}
2407+
2408+- (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2409+{
2410+ return([self mutableObjectFromJSONDataWithParseOptions:parseOptionFlags error:NULL]);
2411+}
2412+
2413+- (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
2414+{
2415+ JSONDecoder *decoder = NULL;
2416+ id returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) mutableObjectWithData:self error:error];
2417+ if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
2418+ return(returnObject);
2419+}
2420+
2421+
2422+@end
2423+
2424+////////////
2425+#pragma mark -
2426+#pragma mark Encoding / deserializing functions
2427+
2428+static void jk_encode_error(JKEncodeState *encodeState, NSString *format, ...) {
2429+ NSCParameterAssert((encodeState != NULL) && (format != NULL));
2430+
2431+ va_list varArgsList;
2432+ va_start(varArgsList, format);
2433+ NSString *formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease];
2434+ va_end(varArgsList);
2435+
2436+ if(encodeState->error == NULL) {
2437+ encodeState->error = [NSError errorWithDomain:@"JKErrorDomain" code:-1L userInfo:
2438+ [NSDictionary dictionaryWithObjectsAndKeys:
2439+ formatString, NSLocalizedDescriptionKey,
2440+ NULL]];
2441+ }
2442+}
2443+
2444+JK_STATIC_INLINE void jk_encode_updateCache(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object) {
2445+ NSCParameterAssert(encodeState != NULL);
2446+ if(JK_EXPECT_T(cacheSlot != NULL)) {
2447+ NSCParameterAssert((object != NULL) && (startingAtIndex <= encodeState->atIndex));
2448+ cacheSlot->object = object;
2449+ cacheSlot->offset = startingAtIndex;
2450+ cacheSlot->length = (size_t)(encodeState->atIndex - startingAtIndex);
2451+ }
2452+}
2453+
2454+static int jk_encode_printf(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, ...) {
2455+ va_list varArgsList, varArgsListCopy;
2456+ va_start(varArgsList, format);
2457+ va_copy(varArgsListCopy, varArgsList);
2458+
2459+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex) && (format != NULL));
2460+
2461+ ssize_t formattedStringLength = 0L;
2462+ int returnValue = 0;
2463+
2464+ if(JK_EXPECT_T((formattedStringLength = vsnprintf((char *)&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex], (encodeState->stringBuffer.bytes.length - encodeState->atIndex), format, varArgsList)) >= (ssize_t)(encodeState->stringBuffer.bytes.length - encodeState->atIndex))) {
2465+ NSCParameterAssert(((encodeState->atIndex + (formattedStringLength * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length));
2466+ if(JK_EXPECT_F(((encodeState->atIndex + (formattedStringLength * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length)) && JK_EXPECT_F((jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (formattedStringLength * 2UL)+ 4096UL) == NULL))) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); returnValue = 1; goto exitNow; }
2467+ if(JK_EXPECT_F((formattedStringLength = vsnprintf((char *)&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex], (encodeState->stringBuffer.bytes.length - encodeState->atIndex), format, varArgsListCopy)) >= (ssize_t)(encodeState->stringBuffer.bytes.length - encodeState->atIndex))) { jk_encode_error(encodeState, @"vsnprintf failed unexpectedly."); returnValue = 1; goto exitNow; }
2468+ }
2469+
2470+exitNow:
2471+ va_end(varArgsList);
2472+ va_end(varArgsListCopy);
2473+ if(JK_EXPECT_T(returnValue == 0)) { encodeState->atIndex += formattedStringLength; jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object); }
2474+ return(returnValue);
2475+}
2476+
2477+static int jk_encode_write(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format) {
2478+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex) && (format != NULL));
2479+ if(JK_EXPECT_F(((encodeState->atIndex + strlen(format) + 256UL) > encodeState->stringBuffer.bytes.length)) && JK_EXPECT_F((jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + strlen(format) + 1024UL) == NULL))) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
2480+
2481+ size_t formatIdx = 0UL;
2482+ for(formatIdx = 0UL; format[formatIdx] != 0; formatIdx++) { NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length); encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[formatIdx]; }
2483+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object);
2484+ return(0);
2485+}
2486+
2487+static int jk_encode_writePrettyPrintWhiteSpace(JKEncodeState *encodeState) {
2488+ NSCParameterAssert((encodeState != NULL) && ((encodeState->serializeOptionFlags & JKSerializeOptionPretty) != 0UL));
2489+ if(JK_EXPECT_F((encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 16UL) > encodeState->stringBuffer.bytes.length) && JK_EXPECT_T(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 4096UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
2490+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\n';
2491+ size_t depthWhiteSpace = 0UL;
2492+ for(depthWhiteSpace = 0UL; depthWhiteSpace < (encodeState->depth * 2UL); depthWhiteSpace++) { NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length); encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = ' '; }
2493+ return(0);
2494+}
2495+
2496+static int jk_encode_write1slow(JKEncodeState *encodeState, ssize_t depthChange, const char *format) {
2497+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (format != NULL) && ((depthChange >= -1L) && (depthChange <= 1L)) && ((encodeState->depth == 0UL) ? (depthChange >= 0L) : 1) && ((encodeState->serializeOptionFlags & JKSerializeOptionPretty) != 0UL));
2498+ if(JK_EXPECT_F((encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 16UL) > encodeState->stringBuffer.bytes.length) && JK_EXPECT_F(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 4096UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
2499+ encodeState->depth += depthChange;
2500+ if(JK_EXPECT_T(format[0] == ':')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = ' '; }
2501+ else {
2502+ if(JK_EXPECT_F(depthChange == -1L)) { if(JK_EXPECT_F(jk_encode_writePrettyPrintWhiteSpace(encodeState))) { return(1); } }
2503+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0];
2504+ if(JK_EXPECT_T(depthChange != -1L)) { if(JK_EXPECT_F(jk_encode_writePrettyPrintWhiteSpace(encodeState))) { return(1); } }
2505+ }
2506+ NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
2507+ return(0);
2508+}
2509+
2510+static int jk_encode_write1fast(JKEncodeState *encodeState, ssize_t depthChange JK_UNUSED_ARG, const char *format) {
2511+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && ((encodeState->serializeOptionFlags & JKSerializeOptionPretty) == 0UL));
2512+ if(JK_EXPECT_T((encodeState->atIndex + 4UL) < encodeState->stringBuffer.bytes.length)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; }
2513+ else { return(jk_encode_write(encodeState, NULL, 0UL, NULL, format)); }
2514+ return(0);
2515+}
2516+
2517+static int jk_encode_writen(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, size_t length) {
2518+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex));
2519+ if(JK_EXPECT_F((encodeState->stringBuffer.bytes.length - encodeState->atIndex) < (length + 4UL))) { if(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + 4096UL + length) == NULL) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); } }
2520+ memcpy(encodeState->stringBuffer.bytes.ptr + encodeState->atIndex, format, length);
2521+ encodeState->atIndex += length;
2522+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object);
2523+ return(0);
2524+}
2525+
2526+JK_STATIC_INLINE JKHash jk_encode_object_hash(void *objectPtr) {
2527+ return( ( (((JKHash)objectPtr) >> 21) ^ (((JKHash)objectPtr) >> 9) ) + (((JKHash)objectPtr) >> 4) );
2528+}
2529+
2530+static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *objectPtr) {
2531+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (objectPtr != NULL));
2532+
2533+ id object = (id)objectPtr, encodeCacheObject = object;
2534+ int isClass = JKClassUnknown;
2535+ size_t startingAtIndex = encodeState->atIndex;
2536+
2537+ JKHash objectHash = jk_encode_object_hash(objectPtr);
2538+ JKEncodeCache *cacheSlot = &encodeState->cache[objectHash % JK_ENCODE_CACHE_SLOTS];
2539+
2540+ if(JK_EXPECT_T(cacheSlot->object == object)) {
2541+ NSCParameterAssert((cacheSlot->object != NULL) &&
2542+ (cacheSlot->offset < encodeState->atIndex) && ((cacheSlot->offset + cacheSlot->length) < encodeState->atIndex) &&
2543+ (cacheSlot->offset < encodeState->stringBuffer.bytes.length) && ((cacheSlot->offset + cacheSlot->length) < encodeState->stringBuffer.bytes.length) &&
2544+ ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2545+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2546+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)));
2547+ if(JK_EXPECT_F(((encodeState->atIndex + cacheSlot->length + 256UL) > encodeState->stringBuffer.bytes.length)) && JK_EXPECT_F((jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + cacheSlot->length + 1024UL) == NULL))) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
2548+ NSCParameterAssert(((encodeState->atIndex + cacheSlot->length) < encodeState->stringBuffer.bytes.length) &&
2549+ ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2550+ ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2551+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2552+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2553+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->atIndex)));
2554+ memcpy(encodeState->stringBuffer.bytes.ptr + encodeState->atIndex, encodeState->stringBuffer.bytes.ptr + cacheSlot->offset, cacheSlot->length);
2555+ encodeState->atIndex += cacheSlot->length;
2556+ return(0);
2557+ }
2558+
2559+ // When we encounter a class that we do not handle, and we have either a delegate or block that the user supplied to format unsupported classes,
2560+ // we "re-run" the object check. However, we re-run the object check exactly ONCE. If the user supplies an object that isn't one of the
2561+ // supported classes, we fail the second time (i.e., double fault error).
2562+ BOOL rerunningAfterClassFormatter = NO;
2563+ rerunAfterClassFormatter:;
2564+
2565+ // XXX XXX XXX XXX
2566+ //
2567+ // We need to work around a bug in 10.7, which breaks ABI compatibility with Objective-C going back not just to 10.0, but OpenStep and even NextStep.
2568+ //
2569+ // It has long been documented that "the very first thing that a pointer to an Objective-C object "points to" is a pointer to that objects class".
2570+ //
2571+ // This is euphemistically called "tagged pointers". There are a number of highly technical problems with this, most involving long passages from
2572+ // the C standard(s). In short, one can make a strong case, couched from the perspective of the C standard(s), that that 10.7 "tagged pointers" are
2573+ // fundamentally Wrong and Broken, and should have never been implemented. Assuming those points are glossed over, because the change is very clearly
2574+ // breaking ABI compatibility, this should have resulted in a minimum of a "minimum version required" bump in various shared libraries to prevent
2575+ // causes code that used to work just fine to suddenly break without warning.
2576+ //
2577+ // In fact, the C standard says that the hack below is "undefined behavior"- there is no requirement that the 10.7 tagged pointer hack of setting the
2578+ // "lower, unused bits" must be preserved when casting the result to an integer type, but this "works" because for most architectures
2579+ // `sizeof(long) == sizeof(void *)` and the compiler uses the same representation for both. (note: this is informal, not meant to be
2580+ // normative or pedantically correct).
2581+ //
2582+ // In other words, while this "works" for now, technically the compiler is not obligated to do "what we want", and a later version of the compiler
2583+ // is not required in any way to produce the same results or behavior that earlier versions of the compiler did for the statement below.
2584+ //
2585+ // Fan-fucking-tastic.
2586+ //
2587+ // Why not just use `object_getClass()`? Because `object->isa` reduces to (typically) a *single* instruction. Calling `object_getClass()` requires
2588+ // that the compiler potentially spill registers, establish a function call frame / environment, and finally execute a "jump subroutine" instruction.
2589+ // Then, the called subroutine must spend half a dozen instructions in its prolog, however many instructions doing whatever it does, then half a dozen
2590+ // instructions in its prolog. One instruction compared to dozens, maybe a hundred instructions.
2591+ //
2592+ // Yes, that's one to two orders of magnitude difference. Which is compelling in its own right. When going for performance, you're often happy with
2593+ // gains in the two to three percent range.
2594+ //
2595+ // XXX XXX XXX XXX
2596+
2597+
2598+ BOOL workAroundMacOSXABIBreakingBug = (JK_EXPECT_F(((NSUInteger)object) & 0x1)) ? YES : NO;
2599+ void *objectISA = (JK_EXPECT_F(workAroundMacOSXABIBreakingBug)) ? NULL : *((void **)objectPtr);
2600+ if(JK_EXPECT_F(workAroundMacOSXABIBreakingBug)) { goto slowClassLookup; }
2601+
2602+ if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.stringClass)) { isClass = JKClassString; }
2603+ else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.numberClass)) { isClass = JKClassNumber; }
2604+ else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.dictionaryClass)) { isClass = JKClassDictionary; }
2605+ else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.arrayClass)) { isClass = JKClassArray; }
2606+ else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.nullClass)) { isClass = JKClassNull; }
2607+ else {
2608+ slowClassLookup:
2609+ if(JK_EXPECT_T([object isKindOfClass:[NSString class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.stringClass = objectISA; } isClass = JKClassString; }
2610+ else if(JK_EXPECT_T([object isKindOfClass:[NSNumber class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.numberClass = objectISA; } isClass = JKClassNumber; }
2611+ else if(JK_EXPECT_T([object isKindOfClass:[NSDictionary class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.dictionaryClass = objectISA; } isClass = JKClassDictionary; }
2612+ else if(JK_EXPECT_T([object isKindOfClass:[NSArray class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.arrayClass = objectISA; } isClass = JKClassArray; }
2613+ else if(JK_EXPECT_T([object isKindOfClass:[NSNull class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.nullClass = objectISA; } isClass = JKClassNull; }
2614+ else {
2615+ if((rerunningAfterClassFormatter == NO) && (
2616+#ifdef __BLOCKS__
2617+ ((encodeState->classFormatterBlock) && ((object = encodeState->classFormatterBlock(object)) != NULL)) ||
2618+#endif
2619+ ((encodeState->classFormatterIMP) && ((object = encodeState->classFormatterIMP(encodeState->classFormatterDelegate, encodeState->classFormatterSelector, object)) != NULL)) )) { rerunningAfterClassFormatter = YES; goto rerunAfterClassFormatter; }
2620+
2621+ if(rerunningAfterClassFormatter == NO) { jk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([encodeCacheObject class])); return(1); }
2622+ else { jk_encode_error(encodeState, @"Unable to serialize object class %@ that was returned by the unsupported class formatter. Original object class was %@.", (object == NULL) ? @"NULL" : NSStringFromClass([object class]), NSStringFromClass([encodeCacheObject class])); return(1); }
2623+ }
2624+ }
2625+
2626+ // This is here for the benefit of the optimizer. It allows the optimizer to do loop invariant code motion for the JKClassArray
2627+ // and JKClassDictionary cases when printing simple, single characters via jk_encode_write(), which is actually a macro:
2628+ // #define jk_encode_write1(es, dc, f) (_jk_encode_prettyPrint ? jk_encode_write1slow(es, dc, f) : jk_encode_write1fast(es, dc, f))
2629+ int _jk_encode_prettyPrint = JK_EXPECT_T((encodeState->serializeOptionFlags & JKSerializeOptionPretty) == 0) ? 0 : 1;
2630+
2631+ switch(isClass) {
2632+ case JKClassString:
2633+ {
2634+ {
2635+ const unsigned char *cStringPtr = (const unsigned char *)CFStringGetCStringPtr((CFStringRef)object, kCFStringEncodingMacRoman);
2636+ if(cStringPtr != NULL) {
2637+ const unsigned char *utf8String = cStringPtr;
2638+ size_t utf8Idx = 0UL;
2639+
2640+ CFIndex stringLength = CFStringGetLength((CFStringRef)object);
2641+ if(JK_EXPECT_F(((encodeState->atIndex + (stringLength * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length)) && JK_EXPECT_F((jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (stringLength * 2UL) + 1024UL) == NULL))) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
2642+
2643+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
2644+ for(utf8Idx = 0UL; utf8String[utf8Idx] != 0U; utf8Idx++) {
2645+ NSCParameterAssert(((&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex]) - encodeState->stringBuffer.bytes.ptr) < (ssize_t)encodeState->stringBuffer.bytes.length);
2646+ NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
2647+ if(JK_EXPECT_F(utf8String[utf8Idx] >= 0x80U)) { encodeState->atIndex = startingAtIndex; goto slowUTF8Path; }
2648+ if(JK_EXPECT_F(utf8String[utf8Idx] < 0x20U)) {
2649+ switch(utf8String[utf8Idx]) {
2650+ case '\b': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'b'; break;
2651+ case '\f': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'f'; break;
2652+ case '\n': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'n'; break;
2653+ case '\r': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'r'; break;
2654+ case '\t': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 't'; break;
2655+ default: if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break;
2656+ }
2657+ } else {
2658+ 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++] = '\\'; }
2659+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx];
2660+ }
2661+ }
2662+ NSCParameterAssert((encodeState->atIndex + 1UL) < encodeState->stringBuffer.bytes.length);
2663+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
2664+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, encodeCacheObject);
2665+ return(0);
2666+ }
2667+ }
2668+
2669+ slowUTF8Path:
2670+ {
2671+ CFIndex stringLength = CFStringGetLength((CFStringRef)object);
2672+ CFIndex maxStringUTF8Length = CFStringGetMaximumSizeForEncoding(stringLength, kCFStringEncodingUTF8) + 32L;
2673+
2674+ if(JK_EXPECT_F((size_t)maxStringUTF8Length > encodeState->utf8ConversionBuffer.bytes.length) && JK_EXPECT_F(jk_managedBuffer_resize(&encodeState->utf8ConversionBuffer, maxStringUTF8Length + 1024UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
2675+
2676+ CFIndex usedBytes = 0L, convertedCount = 0L;
2677+ convertedCount = CFStringGetBytes((CFStringRef)object, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', NO, encodeState->utf8ConversionBuffer.bytes.ptr, encodeState->utf8ConversionBuffer.bytes.length - 16L, &usedBytes);
2678+ if(JK_EXPECT_F(convertedCount != stringLength) || JK_EXPECT_F(usedBytes < 0L)) { jk_encode_error(encodeState, @"An error occurred converting the contents of a NSString to UTF8."); return(1); }
2679+
2680+ if(JK_EXPECT_F((encodeState->atIndex + (maxStringUTF8Length * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length) && JK_EXPECT_F(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (maxStringUTF8Length * 2UL) + 1024UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
2681+
2682+ const unsigned char *utf8String = encodeState->utf8ConversionBuffer.bytes.ptr;
2683+
2684+ size_t utf8Idx = 0UL;
2685+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
2686+ for(utf8Idx = 0UL; utf8Idx < (size_t)usedBytes; utf8Idx++) {
2687+ NSCParameterAssert(((&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex]) - encodeState->stringBuffer.bytes.ptr) < (ssize_t)encodeState->stringBuffer.bytes.length);
2688+ NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
2689+ NSCParameterAssert((CFIndex)utf8Idx < usedBytes);
2690+ if(JK_EXPECT_F(utf8String[utf8Idx] < 0x20U)) {
2691+ switch(utf8String[utf8Idx]) {
2692+ case '\b': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'b'; break;
2693+ case '\f': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'f'; break;
2694+ case '\n': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'n'; break;
2695+ case '\r': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'r'; break;
2696+ case '\t': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 't'; break;
2697+ default: if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break;
2698+ }
2699+ } else {
2700+ if(JK_EXPECT_F(utf8String[utf8Idx] >= 0x80U) && (encodeState->serializeOptionFlags & JKSerializeOptionEscapeUnicode)) {
2701+ const unsigned char *nextValidCharacter = NULL;
2702+ UTF32 u32ch = 0U;
2703+ ConversionResult result;
2704+
2705+ if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(&utf8String[utf8Idx], &utf8String[usedBytes], (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { jk_encode_error(encodeState, @"Error converting UTF8."); return(1); }
2706+ else {
2707+ utf8Idx = (nextValidCharacter - utf8String) - 1UL;
2708+ if(JK_EXPECT_T(u32ch <= 0xffffU)) { if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", u32ch))) { return(1); } }
2709+ 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); } }
2710+ }
2711+ } else {
2712+ 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++] = '\\'; }
2713+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx];
2714+ }
2715+ }
2716+ }
2717+ NSCParameterAssert((encodeState->atIndex + 1UL) < encodeState->stringBuffer.bytes.length);
2718+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
2719+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, encodeCacheObject);
2720+ return(0);
2721+ }
2722+ }
2723+ break;
2724+
2725+ case JKClassNumber:
2726+ {
2727+ if(object == (id)kCFBooleanTrue) { return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "true", 4UL)); }
2728+ else if(object == (id)kCFBooleanFalse) { return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "false", 5UL)); }
2729+
2730+ const char *objCType = [object objCType];
2731+ char anum[256], *aptr = &anum[255];
2732+ int isNegative = 0;
2733+ unsigned long long ullv;
2734+ long long llv;
2735+
2736+ if(JK_EXPECT_F(objCType == NULL) || JK_EXPECT_F(objCType[0] == 0) || JK_EXPECT_F(objCType[1] != 0)) { jk_encode_error(encodeState, @"NSNumber conversion error, unknown type. Type: '%s'", (objCType == NULL) ? "<NULL>" : objCType); return(1); }
2737+
2738+ switch(objCType[0]) {
2739+ case 'c': case 'i': case 's': case 'l': case 'q':
2740+ if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberLongLongType, &llv))) {
2741+ if(llv < 0LL) { ullv = -llv; isNegative = 1; } else { ullv = llv; isNegative = 0; }
2742+ goto convertNumber;
2743+ } else { jk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); }
2744+ break;
2745+ case 'C': case 'I': case 'S': case 'L': case 'Q': case 'B':
2746+ if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberLongLongType, &ullv))) {
2747+ convertNumber:
2748+ 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); } }
2749+ if(isNegative) { *--aptr = '-'; }
2750+ NSCParameterAssert(aptr > anum);
2751+ return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, aptr, &anum[255] - aptr));
2752+ } else { jk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); }
2753+ break;
2754+ case 'f': case 'd':
2755+ {
2756+ double dv;
2757+ if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberDoubleType, &dv))) {
2758+ if(JK_EXPECT_F(!isfinite(dv))) { jk_encode_error(encodeState, @"Floating point values must be finite. JSON does not support NaN or Infinity."); return(1); }
2759+ return(jk_encode_printf(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "%.17g", dv));
2760+ } else { jk_encode_error(encodeState, @"Unable to get floating point value from number object."); return(1); }
2761+ }
2762+ break;
2763+ default: jk_encode_error(encodeState, @"NSNumber conversion error, unknown type. Type: '%c' / 0x%2.2x", objCType[0], objCType[0]); return(1); break;
2764+ }
2765+ }
2766+ break;
2767+
2768+ case JKClassArray:
2769+ {
2770+ int printComma = 0;
2771+ CFIndex arrayCount = CFArrayGetCount((CFArrayRef)object), idx = 0L;
2772+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 1L, "["))) { return(1); }
2773+ if(JK_EXPECT_F(arrayCount > 1020L)) {
2774+ for(id arrayObject in object) { if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } } printComma = 1; if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, arrayObject))) { return(1); } }
2775+ } else {
2776+ void *objects[1024];
2777+ CFArrayGetValues((CFArrayRef)object, CFRangeMake(0L, arrayCount), (const void **)objects);
2778+ for(idx = 0L; idx < arrayCount; idx++) { if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } } printComma = 1; if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, objects[idx]))) { return(1); } }
2779+ }
2780+ return(jk_encode_write1(encodeState, -1L, "]"));
2781+ }
2782+ break;
2783+
2784+ case JKClassDictionary:
2785+ {
2786+ int printComma = 0;
2787+ CFIndex dictionaryCount = CFDictionaryGetCount((CFDictionaryRef)object), idx = 0L;
2788+ id enumerateObject = JK_EXPECT_F(_jk_encode_prettyPrint) ? [[object allKeys] sortedArrayUsingSelector:@selector(compare:)] : object;
2789+
2790+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 1L, "{"))) { return(1); }
2791+ if(JK_EXPECT_F(_jk_encode_prettyPrint) || JK_EXPECT_F(dictionaryCount > 1020L)) {
2792+ for(id keyObject in enumerateObject) {
2793+ if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } }
2794+ printComma = 1;
2795+ void *keyObjectISA = *((void **)keyObject);
2796+ if(JK_EXPECT_F((keyObjectISA != encodeState->fastClassLookup.stringClass)) && JK_EXPECT_F(([keyObject isKindOfClass:[NSString class]] == NO))) { jk_encode_error(encodeState, @"Key must be a string object."); return(1); }
2797+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, keyObject))) { return(1); }
2798+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ":"))) { return(1); }
2799+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, (void *)CFDictionaryGetValue((CFDictionaryRef)object, keyObject)))) { return(1); }
2800+ }
2801+ } else {
2802+ void *keys[1024], *objects[1024];
2803+ CFDictionaryGetKeysAndValues((CFDictionaryRef)object, (const void **)keys, (const void **)objects);
2804+ for(idx = 0L; idx < dictionaryCount; idx++) {
2805+ if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } }
2806+ printComma = 1;
2807+ void *keyObjectISA = *((void **)keys[idx]);
2808+ if(JK_EXPECT_F(keyObjectISA != encodeState->fastClassLookup.stringClass) && JK_EXPECT_F([(id)keys[idx] isKindOfClass:[NSString class]] == NO)) { jk_encode_error(encodeState, @"Key must be a string object."); return(1); }
2809+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, keys[idx]))) { return(1); }
2810+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ":"))) { return(1); }
2811+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, objects[idx]))) { return(1); }
2812+ }
2813+ }
2814+ return(jk_encode_write1(encodeState, -1L, "}"));
2815+ }
2816+ break;
2817+
2818+ case JKClassNull: return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "null", 4UL)); break;
2819+
2820+ default: jk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([object class])); return(1); break;
2821+ }
2822+
2823+ return(0);
2824+}
2825+
2826+
2827+@implementation JKSerializer
2828+
2829++ (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error
2830+{
2831+ return([[[[self alloc] init] autorelease] serializeObject:object options:optionFlags encodeOption:encodeOption block:block delegate:delegate selector:selector error:error]);
2832+}
2833+
2834+- (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error
2835+{
2836+#ifndef __BLOCKS__
2837+#pragma unused(block)
2838+#endif
2839+ NSParameterAssert((object != NULL) && (encodeState == NULL) && ((delegate != NULL) ? (block == NULL) : 1) && ((block != NULL) ? (delegate == NULL) : 1) &&
2840+ (((encodeOption & JKEncodeOptionCollectionObj) != 0UL) ? (((encodeOption & JKEncodeOptionStringObj) == 0UL) && ((encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) : 1) &&
2841+ (((encodeOption & JKEncodeOptionStringObj) != 0UL) ? ((encodeOption & JKEncodeOptionCollectionObj) == 0UL) : 1));
2842+
2843+ id returnObject = NULL;
2844+
2845+ if(encodeState != NULL) { [self releaseState]; }
2846+ if((encodeState = (struct JKEncodeState *)calloc(1UL, sizeof(JKEncodeState))) == NULL) { [NSException raise:NSMallocException format:@"Unable to allocate state structure."]; return(NULL); }
2847+
2848+ if((error != NULL) && (*error != NULL)) { *error = NULL; }
2849+
2850+ if(delegate != NULL) {
2851+ if(selector == NULL) { [NSException raise:NSInvalidArgumentException format:@"The delegate argument is not NULL, but the selector argument is NULL."]; }
2852+ if([delegate respondsToSelector:selector] == NO) { [NSException raise:NSInvalidArgumentException format:@"The serializeUnsupportedClassesUsingDelegate: delegate does not respond to the selector argument."]; }
2853+ encodeState->classFormatterDelegate = delegate;
2854+ encodeState->classFormatterSelector = selector;
2855+ encodeState->classFormatterIMP = (JKClassFormatterIMP)[delegate methodForSelector:selector];
2856+ NSCParameterAssert(encodeState->classFormatterIMP != NULL);
2857+ }
2858+
2859+#ifdef __BLOCKS__
2860+ encodeState->classFormatterBlock = block;
2861+#endif
2862+ encodeState->serializeOptionFlags = optionFlags;
2863+ encodeState->encodeOption = encodeOption;
2864+ encodeState->stringBuffer.roundSizeUpToMultipleOf = (1024UL * 32UL);
2865+ encodeState->utf8ConversionBuffer.roundSizeUpToMultipleOf = 4096UL;
2866+
2867+ unsigned char stackJSONBuffer[JK_JSONBUFFER_SIZE] JK_ALIGNED(64);
2868+ jk_managedBuffer_setToStackBuffer(&encodeState->stringBuffer, stackJSONBuffer, sizeof(stackJSONBuffer));
2869+
2870+ unsigned char stackUTF8Buffer[JK_UTF8BUFFER_SIZE] JK_ALIGNED(64);
2871+ jk_managedBuffer_setToStackBuffer(&encodeState->utf8ConversionBuffer, stackUTF8Buffer, sizeof(stackUTF8Buffer));
2872+
2873+ if(((encodeOption & JKEncodeOptionCollectionObj) != 0UL) && (([object isKindOfClass:[NSArray class]] == NO) && ([object isKindOfClass:[NSDictionary class]] == NO))) { jk_encode_error(encodeState, @"Unable to serialize object class %@, expected a NSArray or NSDictionary.", NSStringFromClass([object class])); goto errorExit; }
2874+ if(((encodeOption & JKEncodeOptionStringObj) != 0UL) && ([object isKindOfClass:[NSString class]] == NO)) { jk_encode_error(encodeState, @"Unable to serialize object class %@, expected a NSString.", NSStringFromClass([object class])); goto errorExit; }
2875+
2876+ if(jk_encode_add_atom_to_buffer(encodeState, object) == 0) {
2877+ BOOL stackBuffer = ((encodeState->stringBuffer.flags & JKManagedBufferMustFree) == 0UL) ? YES : NO;
2878+
2879+ if((encodeState->atIndex < 2UL))
2880+ if((stackBuffer == NO) && ((encodeState->stringBuffer.bytes.ptr = (unsigned char *)reallocf(encodeState->stringBuffer.bytes.ptr, encodeState->atIndex + 16UL)) == NULL)) { jk_encode_error(encodeState, @"Unable to realloc buffer"); goto errorExit; }
2881+
2882+ switch((encodeOption & JKEncodeOptionAsTypeMask)) {
2883+ case JKEncodeOptionAsData:
2884+ if(stackBuffer == YES) { if((returnObject = [(id)CFDataCreate( NULL, encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex) autorelease]) == NULL) { jk_encode_error(encodeState, @"Unable to create NSData object"); } }
2885+ else { if((returnObject = [(id)CFDataCreateWithBytesNoCopy( NULL, encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex, NULL) autorelease]) == NULL) { jk_encode_error(encodeState, @"Unable to create NSData object"); } }
2886+ break;
2887+
2888+ case JKEncodeOptionAsString:
2889+ if(stackBuffer == YES) { if((returnObject = [(id)CFStringCreateWithBytes( NULL, (const UInt8 *)encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex, kCFStringEncodingUTF8, NO) autorelease]) == NULL) { jk_encode_error(encodeState, @"Unable to create NSString object"); } }
2890+ else { if((returnObject = [(id)CFStringCreateWithBytesNoCopy(NULL, (const UInt8 *)encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex, kCFStringEncodingUTF8, NO, NULL) autorelease]) == NULL) { jk_encode_error(encodeState, @"Unable to create NSString object"); } }
2891+ break;
2892+
2893+ default: jk_encode_error(encodeState, @"Unknown encode as type."); break;
2894+ }
2895+
2896+ if((returnObject != NULL) && (stackBuffer == NO)) { encodeState->stringBuffer.flags &= ~JKManagedBufferMustFree; encodeState->stringBuffer.bytes.ptr = NULL; encodeState->stringBuffer.bytes.length = 0UL; }
2897+ }
2898+
2899+errorExit:
2900+ if((encodeState != NULL) && (error != NULL) && (encodeState->error != NULL)) { *error = encodeState->error; encodeState->error = NULL; }
2901+ [self releaseState];
2902+
2903+ return(returnObject);
2904+}
2905+
2906+- (void)releaseState
2907+{
2908+ if(encodeState != NULL) {
2909+ jk_managedBuffer_release(&encodeState->stringBuffer);
2910+ jk_managedBuffer_release(&encodeState->utf8ConversionBuffer);
2911+ free(encodeState); encodeState = NULL;
2912+ }
2913+}
2914+
2915+- (void)dealloc
2916+{
2917+ [self releaseState];
2918+ [super dealloc];
2919+}
2920+
2921+@end
2922+
2923+@implementation NSString (JSONKitSerializing)
2924+
2925+////////////
2926+#pragma mark Methods for serializing a single NSString.
2927+////////////
2928+
2929+// Useful for those who need to serialize just a NSString. Otherwise you would have to do something like [NSArray arrayWithObject:stringToBeJSONSerialized], serializing the array, and then chopping of the extra ^\[.*\]$ square brackets.
2930+
2931+// NSData returning methods...
2932+
2933+- (NSData *)JSONData
2934+{
2935+ return([self JSONDataWithOptions:JKSerializeOptionNone includeQuotes:YES error:NULL]);
2936+}
2937+
2938+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error
2939+{
2940+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | ((includeQuotes == NO) ? JKEncodeOptionStringObjTrimQuotes : 0UL) | JKEncodeOptionStringObj) block:NULL delegate:NULL selector:NULL error:error]);
2941+}
2942+
2943+// NSString returning methods...
2944+
2945+- (NSString *)JSONString
2946+{
2947+ return([self JSONStringWithOptions:JKSerializeOptionNone includeQuotes:YES error:NULL]);
2948+}
2949+
2950+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error
2951+{
2952+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | ((includeQuotes == NO) ? JKEncodeOptionStringObjTrimQuotes : 0UL) | JKEncodeOptionStringObj) block:NULL delegate:NULL selector:NULL error:error]);
2953+}
2954+
2955+@end
2956+
2957+@implementation NSArray (JSONKitSerializing)
2958+
2959+// NSData returning methods...
2960+
2961+- (NSData *)JSONData
2962+{
2963+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
2964+}
2965+
2966+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
2967+{
2968+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
2969+}
2970+
2971+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
2972+{
2973+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
2974+}
2975+
2976+// NSString returning methods...
2977+
2978+- (NSString *)JSONString
2979+{
2980+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
2981+}
2982+
2983+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
2984+{
2985+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
2986+}
2987+
2988+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
2989+{
2990+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
2991+}
2992+
2993+@end
2994+
2995+@implementation NSDictionary (JSONKitSerializing)
2996+
2997+// NSData returning methods...
2998+
2999+- (NSData *)JSONData
3000+{
3001+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
3002+}
3003+
3004+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
3005+{
3006+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
3007+}
3008+
3009+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
3010+{
3011+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
3012+}
3013+
3014+// NSString returning methods...
3015+
3016+- (NSString *)JSONString
3017+{
3018+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
3019+}
3020+
3021+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
3022+{
3023+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
3024+}
3025+
3026+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
3027+{
3028+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
3029+}
3030+
3031+@end
3032+
3033+
3034+#ifdef __BLOCKS__
3035+
3036+@implementation NSArray (JSONKitSerializingBlockAdditions)
3037+
3038+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
3039+{
3040+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
3041+}
3042+
3043+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
3044+{
3045+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
3046+}
3047+
3048+@end
3049+
3050+@implementation NSDictionary (JSONKitSerializingBlockAdditions)
3051+
3052+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
3053+{
3054+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
3055+}
3056+
3057+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
3058+{
3059+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
3060+}
3061+
3062+@end
3063+
3064+#endif // __BLOCKS__
3065+