appjam 0.1.8.6 → 0.1.8.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/lib/appjam.rb +1 -1
  2. data/lib/appjam/generators/blank.rb +133 -0
  3. data/lib/appjam/generators/help.rb +5 -3
  4. data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/project.pbxproj +855 -0
  5. data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  6. data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/project.xcworkspace/xcuserdata/eiffel.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  7. data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/xcuserdata/eiffel.xcuserdatad/xcschemes/EiffelApplication.xcscheme +96 -0
  8. data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/xcuserdata/eiffel.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
  9. data/lib/appjam/generators/templates/blank/EiffelApplication/AppDelegate.h.tt +22 -0
  10. data/lib/appjam/generators/templates/blank/EiffelApplication/AppDelegate.m.tt +156 -0
  11. data/lib/appjam/generators/templates/blank/EiffelApplication/EiffelApplication-Info.plist +45 -0
  12. data/lib/appjam/generators/templates/blank/EiffelApplication/EiffelApplication-Prefix.pch.tt +30 -0
  13. data/lib/appjam/generators/templates/blank/EiffelApplication/en.lproj/InfoPlist.strings +2 -0
  14. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFHTTPClient.h +636 -0
  15. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFHTTPClient.m +1359 -0
  16. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFHTTPRequestOperation.h +133 -0
  17. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFHTTPRequestOperation.m +318 -0
  18. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFImageRequestOperation.h +108 -0
  19. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFImageRequestOperation.m +234 -0
  20. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFJSONRequestOperation.h +71 -0
  21. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFJSONRequestOperation.m +142 -0
  22. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  23. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFNetworkActivityIndicatorManager.m +145 -0
  24. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFNetworking.h +43 -0
  25. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  26. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFPropertyListRequestOperation.m +142 -0
  27. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFURLConnectionOperation.h +379 -0
  28. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFURLConnectionOperation.m +818 -0
  29. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFXMLRequestOperation.h +89 -0
  30. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFXMLRequestOperation.m +166 -0
  31. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/UIImageView+AFNetworking.h +78 -0
  32. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/UIImageView+AFNetworking.m +184 -0
  33. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/JSONKit/JSONKit.h +251 -0
  34. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/JSONKit/JSONKit.m +3067 -0
  35. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabase.h +155 -0
  36. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabase.m +1162 -0
  37. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabaseAdditions.h +37 -0
  38. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabaseAdditions.m +163 -0
  39. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabasePool.h +75 -0
  40. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabasePool.m +244 -0
  41. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabaseQueue.h +38 -0
  42. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabaseQueue.m +176 -0
  43. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMResultSet.h +104 -0
  44. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMResultSet.m +413 -0
  45. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/EGOCache/EGOCache.h +78 -0
  46. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/EGOCache/EGOCache.m +370 -0
  47. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDASLLogger.h +41 -0
  48. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDASLLogger.m +99 -0
  49. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDAbstractDatabaseLogger.h +102 -0
  50. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDAbstractDatabaseLogger.m +727 -0
  51. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDFileLogger.h +334 -0
  52. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDFileLogger.m +1353 -0
  53. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDLog.h +601 -0
  54. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDLog.m +1083 -0
  55. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDTTYLogger.h +167 -0
  56. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDTTYLogger.m +1479 -0
  57. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/Extensions/ContextFilterLogFormatter.h +65 -0
  58. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/Extensions/ContextFilterLogFormatter.m +191 -0
  59. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/Extensions/DispatchQueueLogFormatter.h +116 -0
  60. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/Extensions/DispatchQueueLogFormatter.m +251 -0
  61. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/ObjectiveMixin/Mixin.h +33 -0
  62. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/ObjectiveMixin/Mixin.m +122 -0
  63. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/USArrayWrapper.h +72 -0
  64. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/USArrayWrapper.m +305 -0
  65. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/USConstants.h +38 -0
  66. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/USDictionaryWrapper.h +57 -0
  67. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/USDictionaryWrapper.m +188 -0
  68. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/Underscore+Functional.h +89 -0
  69. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/Underscore+Functional.m +261 -0
  70. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/Underscore-Prefix.pch +7 -0
  71. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/Underscore.h +50 -0
  72. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/Underscore.m +100 -0
  73. data/lib/appjam/generators/templates/blank/EiffelApplication/main.m.tt +18 -0
  74. data/lib/appjam/generators/templates/blank/EiffelApplicationTests/EiffelApplicationTests-Info.plist +22 -0
  75. data/lib/appjam/generators/templates/blank/EiffelApplicationTests/EiffelApplicationTests.h.tt +13 -0
  76. data/lib/appjam/generators/templates/blank/EiffelApplicationTests/EiffelApplicationTests.m.tt +32 -0
  77. data/lib/appjam/generators/templates/blank/EiffelApplicationTests/en.lproj/InfoPlist.strings +2 -0
  78. data/lib/appjam/generators/templates/resources/Default-568h@2x.png +0 -0
  79. data/lib/appjam/generators/templates/resources/Default.png +0 -0
  80. data/lib/appjam/generators/templates/resources/Default@2x.png +0 -0
  81. data/lib/appjam/generators/templates/resources/contents.tt +4 -0
  82. data/lib/appjam/version.rb +1 -1
  83. metadata +462 -326
  84. data/test/helper.rb +0 -132
  85. data/test/test_model_generator.rb +0 -28
  86. data/test/test_project_generator.rb +0 -38
@@ -0,0 +1,251 @@
1
+ //
2
+ // JSONKit.h
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
+ #include <stddef.h>
57
+ #include <stdint.h>
58
+ #include <limits.h>
59
+ #include <TargetConditionals.h>
60
+ #include <AvailabilityMacros.h>
61
+
62
+ #ifdef __OBJC__
63
+ #import <Foundation/NSArray.h>
64
+ #import <Foundation/NSData.h>
65
+ #import <Foundation/NSDictionary.h>
66
+ #import <Foundation/NSError.h>
67
+ #import <Foundation/NSObjCRuntime.h>
68
+ #import <Foundation/NSString.h>
69
+ #endif // __OBJC__
70
+
71
+ #ifdef __cplusplus
72
+ extern "C" {
73
+ #endif
74
+
75
+
76
+ // For Mac OS X < 10.5.
77
+ #ifndef NSINTEGER_DEFINED
78
+ #define NSINTEGER_DEFINED
79
+ #if defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
80
+ typedef long NSInteger;
81
+ typedef unsigned long NSUInteger;
82
+ #define NSIntegerMin LONG_MIN
83
+ #define NSIntegerMax LONG_MAX
84
+ #define NSUIntegerMax ULONG_MAX
85
+ #else // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
86
+ typedef int NSInteger;
87
+ typedef unsigned int NSUInteger;
88
+ #define NSIntegerMin INT_MIN
89
+ #define NSIntegerMax INT_MAX
90
+ #define NSUIntegerMax UINT_MAX
91
+ #endif // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
92
+ #endif // NSINTEGER_DEFINED
93
+
94
+
95
+ #ifndef _JSONKIT_H_
96
+ #define _JSONKIT_H_
97
+
98
+ #if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__APPLE_CC__) && (__APPLE_CC__ >= 5465)
99
+ #define JK_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
100
+ #else
101
+ #define JK_DEPRECATED_ATTRIBUTE
102
+ #endif
103
+
104
+ #define JSONKIT_VERSION_MAJOR 1
105
+ #define JSONKIT_VERSION_MINOR 4
106
+
107
+ typedef NSUInteger JKFlags;
108
+
109
+ /*
110
+ JKParseOptionComments : Allow C style // and /_* ... *_/ (without a _, obviously) comments in JSON.
111
+ JKParseOptionUnicodeNewlines : Allow Unicode recommended (?:\r\n|[\n\v\f\r\x85\p{Zl}\p{Zp}]) newlines.
112
+ JKParseOptionLooseUnicode : Normally the decoder will stop with an error at any malformed Unicode.
113
+ This option allows JSON with malformed Unicode to be parsed without reporting an error.
114
+ Any malformed Unicode is replaced with \uFFFD, or "REPLACEMENT CHARACTER".
115
+ */
116
+
117
+ enum {
118
+ JKParseOptionNone = 0,
119
+ JKParseOptionStrict = 0,
120
+ JKParseOptionComments = (1 << 0),
121
+ JKParseOptionUnicodeNewlines = (1 << 1),
122
+ JKParseOptionLooseUnicode = (1 << 2),
123
+ JKParseOptionPermitTextAfterValidJSON = (1 << 3),
124
+ JKParseOptionValidFlags = (JKParseOptionComments | JKParseOptionUnicodeNewlines | JKParseOptionLooseUnicode | JKParseOptionPermitTextAfterValidJSON),
125
+ };
126
+ typedef JKFlags JKParseOptionFlags;
127
+
128
+ enum {
129
+ JKSerializeOptionNone = 0,
130
+ JKSerializeOptionPretty = (1 << 0),
131
+ JKSerializeOptionEscapeUnicode = (1 << 1),
132
+ JKSerializeOptionEscapeForwardSlashes = (1 << 4),
133
+ JKSerializeOptionValidFlags = (JKSerializeOptionPretty | JKSerializeOptionEscapeUnicode | JKSerializeOptionEscapeForwardSlashes),
134
+ };
135
+ typedef JKFlags JKSerializeOptionFlags;
136
+
137
+ #ifdef __OBJC__
138
+
139
+ typedef struct JKParseState JKParseState; // Opaque internal, private type.
140
+
141
+ // As a general rule of thumb, if you use a method that doesn't accept a JKParseOptionFlags argument, it defaults to JKParseOptionStrict
142
+
143
+ @interface JSONDecoder : NSObject {
144
+ JKParseState *parseState;
145
+ }
146
+ + (id)decoder;
147
+ + (id)decoderWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
148
+ - (id)initWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
149
+ - (void)clearCache;
150
+
151
+ // The parse... methods were deprecated in v1.4 in favor of the v1.4 objectWith... methods.
152
+ - (id)parseUTF8String:(const unsigned char *)string length:(size_t)length JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithUTF8String:length: instead.
153
+ - (id)parseUTF8String:(const unsigned char *)string length:(size_t)length error:(NSError **)error JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithUTF8String:length:error: instead.
154
+ // The NSData MUST be UTF8 encoded JSON.
155
+ - (id)parseJSONData:(NSData *)jsonData JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithData: instead.
156
+ - (id)parseJSONData:(NSData *)jsonData error:(NSError **)error JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithData:error: instead.
157
+
158
+ // Methods that return immutable collection objects.
159
+ - (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length;
160
+ - (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error;
161
+ // The NSData MUST be UTF8 encoded JSON.
162
+ - (id)objectWithData:(NSData *)jsonData;
163
+ - (id)objectWithData:(NSData *)jsonData error:(NSError **)error;
164
+
165
+ // Methods that return mutable collection objects.
166
+ - (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length;
167
+ - (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error;
168
+ // The NSData MUST be UTF8 encoded JSON.
169
+ - (id)mutableObjectWithData:(NSData *)jsonData;
170
+ - (id)mutableObjectWithData:(NSData *)jsonData error:(NSError **)error;
171
+
172
+ @end
173
+
174
+ ////////////
175
+ #pragma mark Deserializing methods
176
+ ////////////
177
+
178
+ @interface NSString (JSONKitDeserializing)
179
+ - (id)objectFromJSONString;
180
+ - (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
181
+ - (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
182
+ - (id)mutableObjectFromJSONString;
183
+ - (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
184
+ - (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
185
+ @end
186
+
187
+ @interface NSData (JSONKitDeserializing)
188
+ // The NSData MUST be UTF8 encoded JSON.
189
+ - (id)objectFromJSONData;
190
+ - (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
191
+ - (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
192
+ - (id)mutableObjectFromJSONData;
193
+ - (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
194
+ - (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
195
+ @end
196
+
197
+ ////////////
198
+ #pragma mark Serializing methods
199
+ ////////////
200
+
201
+ @interface NSString (JSONKitSerializing)
202
+ // Convenience methods for those that need to serialize the receiving NSString (i.e., instead of having to serialize a NSArray with a single NSString, you can "serialize to JSON" just the NSString).
203
+ // Normally, a string that is serialized to JSON has quotation marks surrounding it, which you may or may not want when serializing a single string, and can be controlled with includeQuotes:
204
+ // includeQuotes:YES `a "test"...` -> `"a \"test\"..."`
205
+ // includeQuotes:NO `a "test"...` -> `a \"test\"...`
206
+ - (NSData *)JSONData; // Invokes JSONDataWithOptions:JKSerializeOptionNone includeQuotes:YES
207
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error;
208
+ - (NSString *)JSONString; // Invokes JSONStringWithOptions:JKSerializeOptionNone includeQuotes:YES
209
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error;
210
+ @end
211
+
212
+ @interface NSArray (JSONKitSerializing)
213
+ - (NSData *)JSONData;
214
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
215
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
216
+ - (NSString *)JSONString;
217
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
218
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
219
+ @end
220
+
221
+ @interface NSDictionary (JSONKitSerializing)
222
+ - (NSData *)JSONData;
223
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
224
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
225
+ - (NSString *)JSONString;
226
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
227
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
228
+ @end
229
+
230
+ #ifdef __BLOCKS__
231
+
232
+ @interface NSArray (JSONKitSerializingBlockAdditions)
233
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
234
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
235
+ @end
236
+
237
+ @interface NSDictionary (JSONKitSerializingBlockAdditions)
238
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
239
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
240
+ @end
241
+
242
+ #endif
243
+
244
+
245
+ #endif // __OBJC__
246
+
247
+ #endif // _JSONKIT_H_
248
+
249
+ #ifdef __cplusplus
250
+ } // extern "C"
251
+ #endif
@@ -0,0 +1,3067 @@
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
+ // array->isa = _JKArrayClass;
681
+ object_setClass(array, _JKArrayClass); // Array case
682
+ if((array = [array init]) == NULL) { return(NULL); }
683
+ array->capacity = count;
684
+ array->count = count;
685
+ if(JK_EXPECT_F((array->objects = (id *)malloc(sizeof(id) * array->capacity)) == NULL)) { [array autorelease]; return(NULL); }
686
+ memcpy(array->objects, objects, array->capacity * sizeof(id));
687
+ array->mutations = (mutableCollection == NO) ? 0UL : 1UL;
688
+ }
689
+ return(array);
690
+ }
691
+
692
+ // Note: The caller is responsible for -retaining the object that is to be added.
693
+ static void _JKArrayInsertObjectAtIndex(JKArray *array, id newObject, NSUInteger objectIndex) {
694
+ NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity) && (objectIndex <= array->count) && (newObject != NULL));
695
+ if(!((array != NULL) && (array->objects != NULL) && (objectIndex <= array->count) && (newObject != NULL))) { [newObject autorelease]; return; }
696
+ if((array->count + 1UL) >= array->capacity) {
697
+ id *newObjects = NULL;
698
+ if((newObjects = (id *)realloc(array->objects, sizeof(id) * (array->capacity + 16UL))) == NULL) { [NSException raise:NSMallocException format:@"Unable to resize objects array."]; }
699
+ array->objects = newObjects;
700
+ array->capacity += 16UL;
701
+ memset(&array->objects[array->count], 0, sizeof(id) * (array->capacity - array->count));
702
+ }
703
+ array->count++;
704
+ if((objectIndex + 1UL) < array->count) { memmove(&array->objects[objectIndex + 1UL], &array->objects[objectIndex], sizeof(id) * ((array->count - 1UL) - objectIndex)); array->objects[objectIndex] = NULL; }
705
+ array->objects[objectIndex] = newObject;
706
+ }
707
+
708
+ // Note: The caller is responsible for -retaining the object that is to be added.
709
+ static void _JKArrayReplaceObjectAtIndexWithObject(JKArray *array, NSUInteger objectIndex, id newObject) {
710
+ NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL) && (newObject != NULL));
711
+ if(!((array != NULL) && (array->objects != NULL) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL) && (newObject != NULL))) { [newObject autorelease]; return; }
712
+ CFRelease(array->objects[objectIndex]);
713
+ array->objects[objectIndex] = NULL;
714
+ array->objects[objectIndex] = newObject;
715
+ }
716
+
717
+ static void _JKArrayRemoveObjectAtIndex(JKArray *array, NSUInteger objectIndex) {
718
+ NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count > 0UL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL));
719
+ if(!((array != NULL) && (array->objects != NULL) && (array->count > 0UL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL))) { return; }
720
+ CFRelease(array->objects[objectIndex]);
721
+ array->objects[objectIndex] = NULL;
722
+ 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; }
723
+ array->count--;
724
+ }
725
+
726
+ - (void)dealloc
727
+ {
728
+ if(JK_EXPECT_T(objects != NULL)) {
729
+ NSUInteger atObject = 0UL;
730
+ for(atObject = 0UL; atObject < count; atObject++) { if(JK_EXPECT_T(objects[atObject] != NULL)) { CFRelease(objects[atObject]); objects[atObject] = NULL; } }
731
+ free(objects); objects = NULL;
732
+ }
733
+
734
+ [super dealloc];
735
+ }
736
+
737
+ - (NSUInteger)count
738
+ {
739
+ NSParameterAssert((objects != NULL) && (count <= capacity));
740
+ return(count);
741
+ }
742
+
743
+ - (void)getObjects:(id *)objectsPtr range:(NSRange)range
744
+ {
745
+ NSParameterAssert((objects != NULL) && (count <= capacity));
746
+ 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)]; }
747
+ 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]; }
748
+ #ifndef __clang_analyzer__
749
+ memcpy(objectsPtr, objects + range.location, range.length * sizeof(id));
750
+ #endif
751
+ }
752
+
753
+ - (id)objectAtIndex:(NSUInteger)objectIndex
754
+ {
755
+ if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)count]; }
756
+ NSParameterAssert((objects != NULL) && (count <= capacity) && (objects[objectIndex] != NULL));
757
+ return(objects[objectIndex]);
758
+ }
759
+
760
+ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
761
+ {
762
+ NSParameterAssert((state != NULL) && (stackbuf != NULL) && (len > 0UL) && (objects != NULL) && (count <= capacity));
763
+ if(JK_EXPECT_F(state->state == 0UL)) { state->mutationsPtr = (unsigned long *)&mutations; state->itemsPtr = stackbuf; }
764
+ if(JK_EXPECT_F(state->state >= count)) { return(0UL); }
765
+
766
+ NSUInteger enumeratedCount = 0UL;
767
+ while(JK_EXPECT_T(enumeratedCount < len) && JK_EXPECT_T(state->state < count)) { NSParameterAssert(objects[state->state] != NULL); stackbuf[enumeratedCount++] = objects[state->state++]; }
768
+
769
+ return(enumeratedCount);
770
+ }
771
+
772
+ - (void)insertObject:(id)anObject atIndex:(NSUInteger)objectIndex
773
+ {
774
+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
775
+ if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
776
+ if(objectIndex > count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)(count + 1UL)]; }
777
+ #ifdef __clang_analyzer__
778
+ [anObject retain]; // Stupid clang analyzer... Issue #19.
779
+ #else
780
+ anObject = [anObject retain];
781
+ #endif
782
+ _JKArrayInsertObjectAtIndex(self, anObject, objectIndex);
783
+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
784
+ }
785
+
786
+ - (void)removeObjectAtIndex:(NSUInteger)objectIndex
787
+ {
788
+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
789
+ if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)count]; }
790
+ _JKArrayRemoveObjectAtIndex(self, objectIndex);
791
+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
792
+ }
793
+
794
+ - (void)replaceObjectAtIndex:(NSUInteger)objectIndex withObject:(id)anObject
795
+ {
796
+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
797
+ if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
798
+ if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)objectIndex, (unsigned long)count]; }
799
+ #ifdef __clang_analyzer__
800
+ [anObject retain]; // Stupid clang analyzer... Issue #19.
801
+ #else
802
+ anObject = [anObject retain];
803
+ #endif
804
+ _JKArrayReplaceObjectAtIndexWithObject(self, objectIndex, anObject);
805
+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
806
+ }
807
+
808
+ - (id)copyWithZone:(NSZone *)zone
809
+ {
810
+ NSParameterAssert((objects != NULL) && (count <= capacity));
811
+ return((mutations == 0UL) ? [self retain] : [(NSArray *)[NSArray allocWithZone:zone] initWithObjects:objects count:count]);
812
+ }
813
+
814
+ - (id)mutableCopyWithZone:(NSZone *)zone
815
+ {
816
+ NSParameterAssert((objects != NULL) && (count <= capacity));
817
+ return([(NSMutableArray *)[NSMutableArray allocWithZone:zone] initWithObjects:objects count:count]);
818
+ }
819
+
820
+ @end
821
+
822
+
823
+ #pragma mark -
824
+ @interface JKDictionaryEnumerator : NSEnumerator {
825
+ id collection;
826
+ NSUInteger nextObject;
827
+ }
828
+
829
+ - (id)initWithJKDictionary:(JKDictionary *)initDictionary;
830
+ - (NSArray *)allObjects;
831
+ - (id)nextObject;
832
+
833
+ @end
834
+
835
+ @implementation JKDictionaryEnumerator
836
+
837
+ - (id)initWithJKDictionary:(JKDictionary *)initDictionary
838
+ {
839
+ NSParameterAssert(initDictionary != NULL);
840
+ if((self = [super init]) == NULL) { return(NULL); }
841
+ if((collection = (id)CFRetain(initDictionary)) == NULL) { [self autorelease]; return(NULL); }
842
+ return(self);
843
+ }
844
+
845
+ - (void)dealloc
846
+ {
847
+ if(collection != NULL) { CFRelease(collection); collection = NULL; }
848
+ [super dealloc];
849
+ }
850
+
851
+ - (NSArray *)allObjects
852
+ {
853
+ NSParameterAssert(collection != NULL);
854
+ NSUInteger count = [(NSDictionary *)collection count], atObject = 0UL;
855
+ id objects[count];
856
+
857
+ while((objects[atObject] = [self nextObject]) != NULL) { NSParameterAssert(atObject < count); atObject++; }
858
+
859
+ return([NSArray arrayWithObjects:objects count:atObject]);
860
+ }
861
+
862
+ - (id)nextObject
863
+ {
864
+ NSParameterAssert((collection != NULL) && (_JKDictionaryHashEntry(collection) != NULL));
865
+ JKHashTableEntry *entry = _JKDictionaryHashEntry(collection);
866
+ NSUInteger capacity = _JKDictionaryCapacity(collection);
867
+ id returnObject = NULL;
868
+
869
+ if(entry != NULL) { while((nextObject < capacity) && ((returnObject = entry[nextObject++].key) == NULL)) { /* ... */ } }
870
+
871
+ return(returnObject);
872
+ }
873
+
874
+ @end
875
+
876
+ #pragma mark -
877
+ @interface JKDictionary : NSMutableDictionary <NSCopying, NSMutableCopying, NSFastEnumeration> {
878
+ NSUInteger count, capacity, mutations;
879
+ JKHashTableEntry *entry;
880
+ }
881
+ @end
882
+
883
+ @implementation JKDictionary
884
+
885
+ + (id)allocWithZone:(NSZone *)zone
886
+ {
887
+ #pragma unused(zone)
888
+ [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])];
889
+ return(NULL);
890
+ }
891
+
892
+ // These values are taken from Core Foundation CF-550 CFBasicHash.m. As a bonus, they align very well with our JKHashTableEntry struct too.
893
+ static const NSUInteger jk_dictionaryCapacities[] = {
894
+ 0UL, 3UL, 7UL, 13UL, 23UL, 41UL, 71UL, 127UL, 191UL, 251UL, 383UL, 631UL, 1087UL, 1723UL,
895
+ 2803UL, 4523UL, 7351UL, 11959UL, 19447UL, 31231UL, 50683UL, 81919UL, 132607UL,
896
+ 214519UL, 346607UL, 561109UL, 907759UL, 1468927UL, 2376191UL, 3845119UL,
897
+ 6221311UL, 10066421UL, 16287743UL, 26354171UL, 42641881UL, 68996069UL,
898
+ 111638519UL, 180634607UL, 292272623UL, 472907251UL
899
+ };
900
+
901
+ static NSUInteger _JKDictionaryCapacityForCount(NSUInteger count) {
902
+ NSUInteger bottom = 0UL, top = sizeof(jk_dictionaryCapacities) / sizeof(NSUInteger), mid = 0UL, tableSize = (NSUInteger)lround(floor(((double)count) * 1.33));
903
+ while(top > bottom) { mid = (top + bottom) / 2UL; if(jk_dictionaryCapacities[mid] < tableSize) { bottom = mid + 1UL; } else { top = mid; } }
904
+ return(jk_dictionaryCapacities[bottom]);
905
+ }
906
+
907
+ static void _JKDictionaryResizeIfNeccessary(JKDictionary *dictionary) {
908
+ NSCParameterAssert((dictionary != NULL) && (dictionary->entry != NULL) && (dictionary->count <= dictionary->capacity));
909
+
910
+ NSUInteger capacityForCount = 0UL;
911
+ if(dictionary->capacity < (capacityForCount = _JKDictionaryCapacityForCount(dictionary->count + 1UL))) { // resize
912
+ NSUInteger oldCapacity = dictionary->capacity;
913
+ #ifndef NS_BLOCK_ASSERTIONS
914
+ NSUInteger oldCount = dictionary->count;
915
+ #endif
916
+ JKHashTableEntry *oldEntry = dictionary->entry;
917
+ if(JK_EXPECT_F((dictionary->entry = (JKHashTableEntry *)calloc(1UL, sizeof(JKHashTableEntry) * capacityForCount)) == NULL)) { [NSException raise:NSMallocException format:@"Unable to allocate memory for hash table."]; }
918
+ dictionary->capacity = capacityForCount;
919
+ dictionary->count = 0UL;
920
+
921
+ NSUInteger idx = 0UL;
922
+ 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; } }
923
+ NSCParameterAssert((oldCount == dictionary->count));
924
+ free(oldEntry); oldEntry = NULL;
925
+ }
926
+ }
927
+
928
+ static JKDictionary *_JKDictionaryCreate(id *keys, NSUInteger *keyHashes, id *objects, NSUInteger count, BOOL mutableCollection) {
929
+ NSCParameterAssert((keys != NULL) && (keyHashes != NULL) && (objects != NULL) && (_JKDictionaryClass != NULL) && (_JKDictionaryInstanceSize > 0UL));
930
+ JKDictionary *dictionary = NULL;
931
+ if(JK_EXPECT_T((dictionary = (JKDictionary *)calloc(1UL, _JKDictionaryInstanceSize)) != NULL)) { // Directly allocate the JKDictionary instance via calloc.
932
+ // dictionary->isa = _JKDictionaryClass;
933
+ object_setClass(dictionary, _JKDictionaryClass);
934
+ if((dictionary = [dictionary init]) == NULL) { return(NULL); }
935
+ dictionary->capacity = _JKDictionaryCapacityForCount(count);
936
+ dictionary->count = 0UL;
937
+
938
+ if(JK_EXPECT_F((dictionary->entry = (JKHashTableEntry *)calloc(1UL, sizeof(JKHashTableEntry) * dictionary->capacity)) == NULL)) { [dictionary autorelease]; return(NULL); }
939
+
940
+ NSUInteger idx = 0UL;
941
+ for(idx = 0UL; idx < count; idx++) { _JKDictionaryAddObject(dictionary, keyHashes[idx], keys[idx], objects[idx]); }
942
+
943
+ dictionary->mutations = (mutableCollection == NO) ? 0UL : 1UL;
944
+ }
945
+ return(dictionary);
946
+ }
947
+
948
+ - (void)dealloc
949
+ {
950
+ if(JK_EXPECT_T(entry != NULL)) {
951
+ NSUInteger atEntry = 0UL;
952
+ for(atEntry = 0UL; atEntry < capacity; atEntry++) {
953
+ if(JK_EXPECT_T(entry[atEntry].key != NULL)) { CFRelease(entry[atEntry].key); entry[atEntry].key = NULL; }
954
+ if(JK_EXPECT_T(entry[atEntry].object != NULL)) { CFRelease(entry[atEntry].object); entry[atEntry].object = NULL; }
955
+ }
956
+
957
+ free(entry); entry = NULL;
958
+ }
959
+
960
+ [super dealloc];
961
+ }
962
+
963
+ static JKHashTableEntry *_JKDictionaryHashEntry(JKDictionary *dictionary) {
964
+ NSCParameterAssert(dictionary != NULL);
965
+ return(dictionary->entry);
966
+ }
967
+
968
+ static NSUInteger _JKDictionaryCapacity(JKDictionary *dictionary) {
969
+ NSCParameterAssert(dictionary != NULL);
970
+ return(dictionary->capacity);
971
+ }
972
+
973
+ static void _JKDictionaryRemoveObjectWithEntry(JKDictionary *dictionary, JKHashTableEntry *entry) {
974
+ NSCParameterAssert((dictionary != NULL) && (entry != NULL) && (entry->key != NULL) && (entry->object != NULL) && (dictionary->count > 0UL) && (dictionary->count <= dictionary->capacity));
975
+ CFRelease(entry->key); entry->key = NULL;
976
+ CFRelease(entry->object); entry->object = NULL;
977
+ entry->keyHash = 0UL;
978
+ dictionary->count--;
979
+ // 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.
980
+ NSUInteger removeIdx = entry - dictionary->entry, idx = 0UL;
981
+ NSCParameterAssert((removeIdx < dictionary->capacity));
982
+ for(idx = 0UL; idx < dictionary->capacity; idx++) {
983
+ NSUInteger entryIdx = (removeIdx + idx + 1UL) % dictionary->capacity;
984
+ JKHashTableEntry *atEntry = &dictionary->entry[entryIdx];
985
+ if(atEntry->key == NULL) { break; }
986
+ NSUInteger keyHash = atEntry->keyHash;
987
+ id key = atEntry->key, object = atEntry->object;
988
+ NSCParameterAssert(object != NULL);
989
+ atEntry->keyHash = 0UL;
990
+ atEntry->key = NULL;
991
+ atEntry->object = NULL;
992
+ NSUInteger addKeyEntry = keyHash % dictionary->capacity, addIdx = 0UL;
993
+ for(addIdx = 0UL; addIdx < dictionary->capacity; addIdx++) {
994
+ JKHashTableEntry *atAddEntry = &dictionary->entry[((addKeyEntry + addIdx) % dictionary->capacity)];
995
+ if(JK_EXPECT_T(atAddEntry->key == NULL)) { NSCParameterAssert((atAddEntry->keyHash == 0UL) && (atAddEntry->object == NULL)); atAddEntry->key = key; atAddEntry->object = object; atAddEntry->keyHash = keyHash; break; }
996
+ }
997
+ }
998
+ }
999
+
1000
+ static void _JKDictionaryAddObject(JKDictionary *dictionary, NSUInteger keyHash, id key, id object) {
1001
+ NSCParameterAssert((dictionary != NULL) && (key != NULL) && (object != NULL) && (dictionary->count < dictionary->capacity) && (dictionary->entry != NULL));
1002
+ NSUInteger keyEntry = keyHash % dictionary->capacity, idx = 0UL;
1003
+ for(idx = 0UL; idx < dictionary->capacity; idx++) {
1004
+ NSUInteger entryIdx = (keyEntry + idx) % dictionary->capacity;
1005
+ JKHashTableEntry *atEntry = &dictionary->entry[entryIdx];
1006
+ 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); }
1007
+ 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; }
1008
+ }
1009
+
1010
+ // We should never get here. If we do, we -release the key / object because it's our responsibility.
1011
+ CFRelease(key);
1012
+ CFRelease(object);
1013
+ }
1014
+
1015
+ - (NSUInteger)count
1016
+ {
1017
+ return(count);
1018
+ }
1019
+
1020
+ static JKHashTableEntry *_JKDictionaryHashTableEntryForKey(JKDictionary *dictionary, id aKey) {
1021
+ NSCParameterAssert((dictionary != NULL) && (dictionary->entry != NULL) && (dictionary->count <= dictionary->capacity));
1022
+ if((aKey == NULL) || (dictionary->capacity == 0UL)) { return(NULL); }
1023
+ NSUInteger keyHash = CFHash(aKey), keyEntry = (keyHash % dictionary->capacity), idx = 0UL;
1024
+ JKHashTableEntry *atEntry = NULL;
1025
+ for(idx = 0UL; idx < dictionary->capacity; idx++) {
1026
+ atEntry = &dictionary->entry[(keyEntry + idx) % dictionary->capacity];
1027
+ 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; }
1028
+ 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.
1029
+ }
1030
+ return(NULL);
1031
+ }
1032
+
1033
+ - (id)objectForKey:(id)aKey
1034
+ {
1035
+ NSParameterAssert((entry != NULL) && (count <= capacity));
1036
+ JKHashTableEntry *entryForKey = _JKDictionaryHashTableEntryForKey(self, aKey);
1037
+ return((entryForKey != NULL) ? entryForKey->object : NULL);
1038
+ }
1039
+
1040
+ - (void)getObjects:(id *)objects andKeys:(id *)keys
1041
+ {
1042
+ NSParameterAssert((entry != NULL) && (count <= capacity));
1043
+ NSUInteger atEntry = 0UL; NSUInteger arrayIdx = 0UL;
1044
+ for(atEntry = 0UL; atEntry < capacity; atEntry++) {
1045
+ if(JK_EXPECT_T(entry[atEntry].key != NULL)) {
1046
+ NSCParameterAssert((entry[atEntry].object != NULL) && (arrayIdx < count));
1047
+ if(JK_EXPECT_T(keys != NULL)) { keys[arrayIdx] = entry[atEntry].key; }
1048
+ if(JK_EXPECT_T(objects != NULL)) { objects[arrayIdx] = entry[atEntry].object; }
1049
+ arrayIdx++;
1050
+ }
1051
+ }
1052
+ }
1053
+
1054
+ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
1055
+ {
1056
+ NSParameterAssert((state != NULL) && (stackbuf != NULL) && (len > 0UL) && (entry != NULL) && (count <= capacity));
1057
+ if(JK_EXPECT_F(state->state == 0UL)) { state->mutationsPtr = (unsigned long *)&mutations; state->itemsPtr = stackbuf; }
1058
+ if(JK_EXPECT_F(state->state >= capacity)) { return(0UL); }
1059
+
1060
+ NSUInteger enumeratedCount = 0UL;
1061
+ 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++; }
1062
+
1063
+ return(enumeratedCount);
1064
+ }
1065
+
1066
+ - (NSEnumerator *)keyEnumerator
1067
+ {
1068
+ return([[[JKDictionaryEnumerator alloc] initWithJKDictionary:self] autorelease]);
1069
+ }
1070
+
1071
+ - (void)setObject:(id)anObject forKey:(id)aKey
1072
+ {
1073
+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
1074
+ if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
1075
+ if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil value (key: %@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey]; }
1076
+
1077
+ _JKDictionaryResizeIfNeccessary(self);
1078
+ #ifndef __clang_analyzer__
1079
+ aKey = [aKey copy]; // Why on earth would clang complain that this -copy "might leak",
1080
+ anObject = [anObject retain]; // but this -retain doesn't!?
1081
+ #endif // __clang_analyzer__
1082
+ _JKDictionaryAddObject(self, CFHash(aKey), aKey, anObject);
1083
+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
1084
+ }
1085
+
1086
+ - (void)removeObjectForKey:(id)aKey
1087
+ {
1088
+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
1089
+ if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to remove nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
1090
+ JKHashTableEntry *entryForKey = _JKDictionaryHashTableEntryForKey(self, aKey);
1091
+ if(entryForKey != NULL) {
1092
+ _JKDictionaryRemoveObjectWithEntry(self, entryForKey);
1093
+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
1094
+ }
1095
+ }
1096
+
1097
+ - (id)copyWithZone:(NSZone *)zone
1098
+ {
1099
+ NSParameterAssert((entry != NULL) && (count <= capacity));
1100
+ return((mutations == 0UL) ? [self retain] : [[NSDictionary allocWithZone:zone] initWithDictionary:self]);
1101
+ }
1102
+
1103
+ - (id)mutableCopyWithZone:(NSZone *)zone
1104
+ {
1105
+ NSParameterAssert((entry != NULL) && (count <= capacity));
1106
+ return([[NSMutableDictionary allocWithZone:zone] initWithDictionary:self]);
1107
+ }
1108
+
1109
+ @end
1110
+
1111
+
1112
+
1113
+ #pragma mark -
1114
+
1115
+ JK_STATIC_INLINE size_t jk_min(size_t a, size_t b) { return((a < b) ? a : b); }
1116
+ JK_STATIC_INLINE size_t jk_max(size_t a, size_t b) { return((a > b) ? a : b); }
1117
+
1118
+ JK_STATIC_INLINE JKHash jk_calculateHash(JKHash currentHash, unsigned char c) { return((((currentHash << 5) + currentHash) + (c - 29)) ^ (currentHash >> 19)); }
1119
+
1120
+
1121
+ static void jk_error(JKParseState *parseState, NSString *format, ...) {
1122
+ NSCParameterAssert((parseState != NULL) && (format != NULL));
1123
+
1124
+ va_list varArgsList;
1125
+ va_start(varArgsList, format);
1126
+ NSString *formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease];
1127
+ va_end(varArgsList);
1128
+
1129
+ #if 0
1130
+ const unsigned char *lineStart = parseState->stringBuffer.bytes.ptr + parseState->lineStartIndex;
1131
+ const unsigned char *lineEnd = lineStart;
1132
+ const unsigned char *atCharacterPtr = NULL;
1133
+
1134
+ for(atCharacterPtr = lineStart; atCharacterPtr < JK_END_STRING_PTR(parseState); atCharacterPtr++) { lineEnd = atCharacterPtr; if(jk_parse_is_newline(parseState, atCharacterPtr)) { break; } }
1135
+
1136
+ NSString *lineString = @"", *carretString = @"";
1137
+ if(lineStart < JK_END_STRING_PTR(parseState)) {
1138
+ lineString = [[[NSString alloc] initWithBytes:lineStart length:(lineEnd - lineStart) encoding:NSUTF8StringEncoding] autorelease];
1139
+ carretString = [NSString stringWithFormat:@"%*.*s^", (int)(parseState->atIndex - parseState->lineStartIndex), (int)(parseState->atIndex - parseState->lineStartIndex), " "];
1140
+ }
1141
+ #endif
1142
+
1143
+ if(parseState->error == NULL) {
1144
+ parseState->error = [NSError errorWithDomain:@"JKErrorDomain" code:-1L userInfo:
1145
+ [NSDictionary dictionaryWithObjectsAndKeys:
1146
+ formatString, NSLocalizedDescriptionKey,
1147
+ [NSNumber numberWithUnsignedLong:parseState->atIndex], @"JKAtIndexKey",
1148
+ [NSNumber numberWithUnsignedLong:parseState->lineNumber], @"JKLineNumberKey",
1149
+ //lineString, @"JKErrorLine0Key",
1150
+ //carretString, @"JKErrorLine1Key",
1151
+ NULL]];
1152
+ }
1153
+ }
1154
+
1155
+ #pragma mark -
1156
+ #pragma mark Buffer and Object Stack management functions
1157
+
1158
+ static void jk_managedBuffer_release(JKManagedBuffer *managedBuffer) {
1159
+ if((managedBuffer->flags & JKManagedBufferMustFree)) {
1160
+ if(managedBuffer->bytes.ptr != NULL) { free(managedBuffer->bytes.ptr); managedBuffer->bytes.ptr = NULL; }
1161
+ managedBuffer->flags &= ~JKManagedBufferMustFree;
1162
+ }
1163
+
1164
+ managedBuffer->bytes.ptr = NULL;
1165
+ managedBuffer->bytes.length = 0UL;
1166
+ managedBuffer->flags &= ~JKManagedBufferLocationMask;
1167
+ }
1168
+
1169
+ static void jk_managedBuffer_setToStackBuffer(JKManagedBuffer *managedBuffer, unsigned char *ptr, size_t length) {
1170
+ jk_managedBuffer_release(managedBuffer);
1171
+ managedBuffer->bytes.ptr = ptr;
1172
+ managedBuffer->bytes.length = length;
1173
+ managedBuffer->flags = (managedBuffer->flags & ~JKManagedBufferLocationMask) | JKManagedBufferOnStack;
1174
+ }
1175
+
1176
+ static unsigned char *jk_managedBuffer_resize(JKManagedBuffer *managedBuffer, size_t newSize) {
1177
+ size_t roundedUpNewSize = newSize;
1178
+
1179
+ if(managedBuffer->roundSizeUpToMultipleOf > 0UL) { roundedUpNewSize = newSize + ((managedBuffer->roundSizeUpToMultipleOf - (newSize % managedBuffer->roundSizeUpToMultipleOf)) % managedBuffer->roundSizeUpToMultipleOf); }
1180
+
1181
+ if((roundedUpNewSize != managedBuffer->bytes.length) && (roundedUpNewSize > managedBuffer->bytes.length)) {
1182
+ if((managedBuffer->flags & JKManagedBufferLocationMask) == JKManagedBufferOnStack) {
1183
+ NSCParameterAssert((managedBuffer->flags & JKManagedBufferMustFree) == 0);
1184
+ unsigned char *newBuffer = NULL, *oldBuffer = managedBuffer->bytes.ptr;
1185
+
1186
+ if((newBuffer = (unsigned char *)malloc(roundedUpNewSize)) == NULL) { return(NULL); }
1187
+ memcpy(newBuffer, oldBuffer, jk_min(managedBuffer->bytes.length, roundedUpNewSize));
1188
+ managedBuffer->flags = (managedBuffer->flags & ~JKManagedBufferLocationMask) | (JKManagedBufferOnHeap | JKManagedBufferMustFree);
1189
+ managedBuffer->bytes.ptr = newBuffer;
1190
+ managedBuffer->bytes.length = roundedUpNewSize;
1191
+ } else {
1192
+ NSCParameterAssert(((managedBuffer->flags & JKManagedBufferMustFree) != 0) && ((managedBuffer->flags & JKManagedBufferLocationMask) == JKManagedBufferOnHeap));
1193
+ if((managedBuffer->bytes.ptr = (unsigned char *)reallocf(managedBuffer->bytes.ptr, roundedUpNewSize)) == NULL) { return(NULL); }
1194
+ managedBuffer->bytes.length = roundedUpNewSize;
1195
+ }
1196
+ }
1197
+
1198
+ return(managedBuffer->bytes.ptr);
1199
+ }
1200
+
1201
+
1202
+
1203
+ static void jk_objectStack_release(JKObjectStack *objectStack) {
1204
+ NSCParameterAssert(objectStack != NULL);
1205
+
1206
+ NSCParameterAssert(objectStack->index <= objectStack->count);
1207
+ size_t atIndex = 0UL;
1208
+ for(atIndex = 0UL; atIndex < objectStack->index; atIndex++) {
1209
+ if(objectStack->objects[atIndex] != NULL) { CFRelease(objectStack->objects[atIndex]); objectStack->objects[atIndex] = NULL; }
1210
+ if(objectStack->keys[atIndex] != NULL) { CFRelease(objectStack->keys[atIndex]); objectStack->keys[atIndex] = NULL; }
1211
+ }
1212
+ objectStack->index = 0UL;
1213
+
1214
+ if(objectStack->flags & JKObjectStackMustFree) {
1215
+ NSCParameterAssert((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnHeap);
1216
+ if(objectStack->objects != NULL) { free(objectStack->objects); objectStack->objects = NULL; }
1217
+ if(objectStack->keys != NULL) { free(objectStack->keys); objectStack->keys = NULL; }
1218
+ if(objectStack->cfHashes != NULL) { free(objectStack->cfHashes); objectStack->cfHashes = NULL; }
1219
+ objectStack->flags &= ~JKObjectStackMustFree;
1220
+ }
1221
+
1222
+ objectStack->objects = NULL;
1223
+ objectStack->keys = NULL;
1224
+ objectStack->cfHashes = NULL;
1225
+
1226
+ objectStack->count = 0UL;
1227
+ objectStack->flags &= ~JKObjectStackLocationMask;
1228
+ }
1229
+
1230
+ static void jk_objectStack_setToStackBuffer(JKObjectStack *objectStack, void **objects, void **keys, CFHashCode *cfHashes, size_t count) {
1231
+ NSCParameterAssert((objectStack != NULL) && (objects != NULL) && (keys != NULL) && (cfHashes != NULL) && (count > 0UL));
1232
+ jk_objectStack_release(objectStack);
1233
+ objectStack->objects = objects;
1234
+ objectStack->keys = keys;
1235
+ objectStack->cfHashes = cfHashes;
1236
+ objectStack->count = count;
1237
+ objectStack->flags = (objectStack->flags & ~JKObjectStackLocationMask) | JKObjectStackOnStack;
1238
+ #ifndef NS_BLOCK_ASSERTIONS
1239
+ size_t idx;
1240
+ for(idx = 0UL; idx < objectStack->count; idx++) { objectStack->objects[idx] = NULL; objectStack->keys[idx] = NULL; objectStack->cfHashes[idx] = 0UL; }
1241
+ #endif
1242
+ }
1243
+
1244
+ static int jk_objectStack_resize(JKObjectStack *objectStack, size_t newCount) {
1245
+ size_t roundedUpNewCount = newCount;
1246
+ int returnCode = 0;
1247
+
1248
+ void **newObjects = NULL, **newKeys = NULL;
1249
+ CFHashCode *newCFHashes = NULL;
1250
+
1251
+ if(objectStack->roundSizeUpToMultipleOf > 0UL) { roundedUpNewCount = newCount + ((objectStack->roundSizeUpToMultipleOf - (newCount % objectStack->roundSizeUpToMultipleOf)) % objectStack->roundSizeUpToMultipleOf); }
1252
+
1253
+ if((roundedUpNewCount != objectStack->count) && (roundedUpNewCount > objectStack->count)) {
1254
+ if((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnStack) {
1255
+ NSCParameterAssert((objectStack->flags & JKObjectStackMustFree) == 0);
1256
+
1257
+ if((newObjects = (void ** )calloc(1UL, roundedUpNewCount * sizeof(void * ))) == NULL) { returnCode = 1; goto errorExit; }
1258
+ memcpy(newObjects, objectStack->objects, jk_min(objectStack->count, roundedUpNewCount) * sizeof(void *));
1259
+ if((newKeys = (void ** )calloc(1UL, roundedUpNewCount * sizeof(void * ))) == NULL) { returnCode = 1; goto errorExit; }
1260
+ memcpy(newKeys, objectStack->keys, jk_min(objectStack->count, roundedUpNewCount) * sizeof(void *));
1261
+
1262
+ if((newCFHashes = (CFHashCode *)calloc(1UL, roundedUpNewCount * sizeof(CFHashCode))) == NULL) { returnCode = 1; goto errorExit; }
1263
+ memcpy(newCFHashes, objectStack->cfHashes, jk_min(objectStack->count, roundedUpNewCount) * sizeof(CFHashCode));
1264
+
1265
+ objectStack->flags = (objectStack->flags & ~JKObjectStackLocationMask) | (JKObjectStackOnHeap | JKObjectStackMustFree);
1266
+ objectStack->objects = newObjects; newObjects = NULL;
1267
+ objectStack->keys = newKeys; newKeys = NULL;
1268
+ objectStack->cfHashes = newCFHashes; newCFHashes = NULL;
1269
+ objectStack->count = roundedUpNewCount;
1270
+ } else {
1271
+ NSCParameterAssert(((objectStack->flags & JKObjectStackMustFree) != 0) && ((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnHeap));
1272
+ if((newObjects = (void ** )realloc(objectStack->objects, roundedUpNewCount * sizeof(void * ))) != NULL) { objectStack->objects = newObjects; newObjects = NULL; } else { returnCode = 1; goto errorExit; }
1273
+ if((newKeys = (void ** )realloc(objectStack->keys, roundedUpNewCount * sizeof(void * ))) != NULL) { objectStack->keys = newKeys; newKeys = NULL; } else { returnCode = 1; goto errorExit; }
1274
+ if((newCFHashes = (CFHashCode *)realloc(objectStack->cfHashes, roundedUpNewCount * sizeof(CFHashCode))) != NULL) { objectStack->cfHashes = newCFHashes; newCFHashes = NULL; } else { returnCode = 1; goto errorExit; }
1275
+
1276
+ #ifndef NS_BLOCK_ASSERTIONS
1277
+ size_t idx;
1278
+ for(idx = objectStack->count; idx < roundedUpNewCount; idx++) { objectStack->objects[idx] = NULL; objectStack->keys[idx] = NULL; objectStack->cfHashes[idx] = 0UL; }
1279
+ #endif
1280
+ objectStack->count = roundedUpNewCount;
1281
+ }
1282
+ }
1283
+
1284
+ errorExit:
1285
+ if(newObjects != NULL) { free(newObjects); newObjects = NULL; }
1286
+ if(newKeys != NULL) { free(newKeys); newKeys = NULL; }
1287
+ if(newCFHashes != NULL) { free(newCFHashes); newCFHashes = NULL; }
1288
+
1289
+ return(returnCode);
1290
+ }
1291
+
1292
+ ////////////
1293
+ #pragma mark -
1294
+ #pragma mark Unicode related functions
1295
+
1296
+ JK_STATIC_INLINE ConversionResult isValidCodePoint(UTF32 *u32CodePoint) {
1297
+ ConversionResult result = conversionOK;
1298
+ UTF32 ch = *u32CodePoint;
1299
+
1300
+ 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; }
1301
+ 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; }
1302
+ if(JK_EXPECT_F(ch == 0U)) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
1303
+
1304
+ finished:
1305
+ *u32CodePoint = ch;
1306
+ return(result);
1307
+ }
1308
+
1309
+
1310
+ static int isLegalUTF8(const UTF8 *source, size_t length) {
1311
+ const UTF8 *srcptr = source + length;
1312
+ UTF8 a;
1313
+
1314
+ switch(length) {
1315
+ default: return(0); // Everything else falls through when "true"...
1316
+ case 4: if(JK_EXPECT_F(((a = (*--srcptr)) < 0x80) || (a > 0xBF))) { return(0); }
1317
+ case 3: if(JK_EXPECT_F(((a = (*--srcptr)) < 0x80) || (a > 0xBF))) { return(0); }
1318
+ case 2: if(JK_EXPECT_F( (a = (*--srcptr)) > 0xBF )) { return(0); }
1319
+
1320
+ switch(*source) { // no fall-through in this inner switch
1321
+ case 0xE0: if(JK_EXPECT_F(a < 0xA0)) { return(0); } break;
1322
+ case 0xED: if(JK_EXPECT_F(a > 0x9F)) { return(0); } break;
1323
+ case 0xF0: if(JK_EXPECT_F(a < 0x90)) { return(0); } break;
1324
+ case 0xF4: if(JK_EXPECT_F(a > 0x8F)) { return(0); } break;
1325
+ default: if(JK_EXPECT_F(a < 0x80)) { return(0); }
1326
+ }
1327
+
1328
+ case 1: if(JK_EXPECT_F((JK_EXPECT_T(*source < 0xC2)) && JK_EXPECT_F(*source >= 0x80))) { return(0); }
1329
+ }
1330
+
1331
+ if(JK_EXPECT_F(*source > 0xF4)) { return(0); }
1332
+
1333
+ return(1);
1334
+ }
1335
+
1336
+ static ConversionResult ConvertSingleCodePointInUTF8(const UTF8 *sourceStart, const UTF8 *sourceEnd, UTF8 const **nextUTF8, UTF32 *convertedUTF32) {
1337
+ ConversionResult result = conversionOK;
1338
+ const UTF8 *source = sourceStart;
1339
+ UTF32 ch = 0UL;
1340
+
1341
+ #if !defined(JK_FAST_TRAILING_BYTES)
1342
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
1343
+ #else
1344
+ unsigned short extraBytesToRead = __builtin_clz(((*source)^0xff) << 25);
1345
+ #endif
1346
+
1347
+ if(JK_EXPECT_F((source + extraBytesToRead + 1) > sourceEnd) || JK_EXPECT_F(!isLegalUTF8(source, extraBytesToRead + 1))) {
1348
+ source++;
1349
+ while((source < sourceEnd) && (((*source) & 0xc0) == 0x80) && ((source - sourceStart) < (extraBytesToRead + 1))) { source++; }
1350
+ NSCParameterAssert(source <= sourceEnd);
1351
+ result = ((source < sourceEnd) && (((*source) & 0xc0) != 0x80)) ? sourceIllegal : ((sourceStart + extraBytesToRead + 1) > sourceEnd) ? sourceExhausted : sourceIllegal;
1352
+ ch = UNI_REPLACEMENT_CHAR;
1353
+ goto finished;
1354
+ }
1355
+
1356
+ switch(extraBytesToRead) { // The cases all fall through.
1357
+ case 5: ch += *source++; ch <<= 6;
1358
+ case 4: ch += *source++; ch <<= 6;
1359
+ case 3: ch += *source++; ch <<= 6;
1360
+ case 2: ch += *source++; ch <<= 6;
1361
+ case 1: ch += *source++; ch <<= 6;
1362
+ case 0: ch += *source++;
1363
+ }
1364
+ ch -= offsetsFromUTF8[extraBytesToRead];
1365
+
1366
+ result = isValidCodePoint(&ch);
1367
+
1368
+ finished:
1369
+ *nextUTF8 = source;
1370
+ *convertedUTF32 = ch;
1371
+
1372
+ return(result);
1373
+ }
1374
+
1375
+
1376
+ static ConversionResult ConvertUTF32toUTF8 (UTF32 u32CodePoint, UTF8 **targetStart, UTF8 *targetEnd) {
1377
+ const UTF32 byteMask = 0xBF, byteMark = 0x80;
1378
+ ConversionResult result = conversionOK;
1379
+ UTF8 *target = *targetStart;
1380
+ UTF32 ch = u32CodePoint;
1381
+ unsigned short bytesToWrite = 0;
1382
+
1383
+ result = isValidCodePoint(&ch);
1384
+
1385
+ // Figure out how many bytes the result will require. Turn any illegally large UTF32 things (> Plane 17) into replacement chars.
1386
+ if(ch < (UTF32)0x80) { bytesToWrite = 1; }
1387
+ else if(ch < (UTF32)0x800) { bytesToWrite = 2; }
1388
+ else if(ch < (UTF32)0x10000) { bytesToWrite = 3; }
1389
+ else if(ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; }
1390
+ else { bytesToWrite = 3; ch = UNI_REPLACEMENT_CHAR; result = sourceIllegal; }
1391
+
1392
+ target += bytesToWrite;
1393
+ if (target > targetEnd) { target -= bytesToWrite; result = targetExhausted; goto finished; }
1394
+
1395
+ switch (bytesToWrite) { // note: everything falls through.
1396
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
1397
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
1398
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
1399
+ case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
1400
+ }
1401
+
1402
+ target += bytesToWrite;
1403
+
1404
+ finished:
1405
+ *targetStart = target;
1406
+ return(result);
1407
+ }
1408
+
1409
+ JK_STATIC_INLINE int jk_string_add_unicodeCodePoint(JKParseState *parseState, uint32_t unicodeCodePoint, size_t *tokenBufferIdx, JKHash *stringHash) {
1410
+ UTF8 *u8s = &parseState->token.tokenBuffer.bytes.ptr[*tokenBufferIdx];
1411
+ ConversionResult result;
1412
+
1413
+ if((result = ConvertUTF32toUTF8(unicodeCodePoint, &u8s, (parseState->token.tokenBuffer.bytes.ptr + parseState->token.tokenBuffer.bytes.length))) != conversionOK) { if(result == targetExhausted) { return(1); } }
1414
+ size_t utf8len = u8s - &parseState->token.tokenBuffer.bytes.ptr[*tokenBufferIdx], nextIdx = (*tokenBufferIdx) + utf8len;
1415
+
1416
+ while(*tokenBufferIdx < nextIdx) { *stringHash = jk_calculateHash(*stringHash, parseState->token.tokenBuffer.bytes.ptr[(*tokenBufferIdx)++]); }
1417
+
1418
+ return(0);
1419
+ }
1420
+
1421
+ ////////////
1422
+ #pragma mark -
1423
+ #pragma mark Decoding / parsing / deserializing functions
1424
+
1425
+ static int jk_parse_string(JKParseState *parseState) {
1426
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
1427
+ const unsigned char *stringStart = JK_AT_STRING_PTR(parseState) + 1;
1428
+ const unsigned char *endOfBuffer = JK_END_STRING_PTR(parseState);
1429
+ const unsigned char *atStringCharacter = stringStart;
1430
+ unsigned char *tokenBuffer = parseState->token.tokenBuffer.bytes.ptr;
1431
+ size_t tokenStartIndex = parseState->atIndex;
1432
+ size_t tokenBufferIdx = 0UL;
1433
+
1434
+ int onlySimpleString = 1, stringState = JSONStringStateStart;
1435
+ uint16_t escapedUnicode1 = 0U, escapedUnicode2 = 0U;
1436
+ uint32_t escapedUnicodeCodePoint = 0U;
1437
+ JKHash stringHash = JK_HASH_INIT;
1438
+
1439
+ while(1) {
1440
+ unsigned long currentChar;
1441
+
1442
+ if(JK_EXPECT_F(atStringCharacter == endOfBuffer)) { /* XXX Add error message */ stringState = JSONStringStateError; goto finishedParsing; }
1443
+
1444
+ if(JK_EXPECT_F((currentChar = *atStringCharacter++) >= 0x80UL)) {
1445
+ const unsigned char *nextValidCharacter = NULL;
1446
+ UTF32 u32ch = 0U;
1447
+ ConversionResult result;
1448
+
1449
+ if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(atStringCharacter - 1, endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { goto switchToSlowPath; }
1450
+ stringHash = jk_calculateHash(stringHash, currentChar);
1451
+ while(atStringCharacter < nextValidCharacter) { NSCParameterAssert(JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)); stringHash = jk_calculateHash(stringHash, *atStringCharacter++); }
1452
+ continue;
1453
+ } else {
1454
+ if(JK_EXPECT_F(currentChar == (unsigned long)'"')) { stringState = JSONStringStateFinished; goto finishedParsing; }
1455
+
1456
+ if(JK_EXPECT_F(currentChar == (unsigned long)'\\')) {
1457
+ switchToSlowPath:
1458
+ onlySimpleString = 0;
1459
+ stringState = JSONStringStateParsing;
1460
+ tokenBufferIdx = (atStringCharacter - stringStart) - 1L;
1461
+ 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; } }
1462
+ memcpy(tokenBuffer, stringStart, tokenBufferIdx);
1463
+ goto slowMatch;
1464
+ }
1465
+
1466
+ if(JK_EXPECT_F(currentChar < 0x20UL)) { jk_error(parseState, @"Invalid character < 0x20 found in string: 0x%2.2x.", currentChar); stringState = JSONStringStateError; goto finishedParsing; }
1467
+
1468
+ stringHash = jk_calculateHash(stringHash, currentChar);
1469
+ }
1470
+ }
1471
+
1472
+ slowMatch:
1473
+
1474
+ for(atStringCharacter = (stringStart + ((atStringCharacter - stringStart) - 1L)); (atStringCharacter < endOfBuffer) && (tokenBufferIdx < parseState->token.tokenBuffer.bytes.length); atStringCharacter++) {
1475
+ 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; } }
1476
+
1477
+ NSCParameterAssert(tokenBufferIdx < parseState->token.tokenBuffer.bytes.length);
1478
+
1479
+ unsigned long currentChar = (*atStringCharacter), escapedChar;
1480
+
1481
+ if(JK_EXPECT_T(stringState == JSONStringStateParsing)) {
1482
+ if(JK_EXPECT_T(currentChar >= 0x20UL)) {
1483
+ if(JK_EXPECT_T(currentChar < (unsigned long)0x80)) { // Not a UTF8 sequence
1484
+ if(JK_EXPECT_F(currentChar == (unsigned long)'"')) { stringState = JSONStringStateFinished; atStringCharacter++; goto finishedParsing; }
1485
+ if(JK_EXPECT_F(currentChar == (unsigned long)'\\')) { stringState = JSONStringStateEscape; continue; }
1486
+ stringHash = jk_calculateHash(stringHash, currentChar);
1487
+ tokenBuffer[tokenBufferIdx++] = currentChar;
1488
+ continue;
1489
+ } else { // UTF8 sequence
1490
+ const unsigned char *nextValidCharacter = NULL;
1491
+ UTF32 u32ch = 0U;
1492
+ ConversionResult result;
1493
+
1494
+ if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(atStringCharacter, endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) {
1495
+ if((result == sourceIllegal) && ((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0)) { jk_error(parseState, @"Illegal UTF8 sequence found in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; }
1496
+ if(result == sourceExhausted) { jk_error(parseState, @"End of buffer reached while parsing UTF8 in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; }
1497
+ 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; }
1498
+ atStringCharacter = nextValidCharacter - 1;
1499
+ continue;
1500
+ } else {
1501
+ while(atStringCharacter < nextValidCharacter) { tokenBuffer[tokenBufferIdx++] = *atStringCharacter; stringHash = jk_calculateHash(stringHash, *atStringCharacter++); }
1502
+ atStringCharacter--;
1503
+ continue;
1504
+ }
1505
+ }
1506
+ } else { // currentChar < 0x20
1507
+ jk_error(parseState, @"Invalid character < 0x20 found in string: 0x%2.2x.", currentChar); stringState = JSONStringStateError; goto finishedParsing;
1508
+ }
1509
+
1510
+ } else { // stringState != JSONStringStateParsing
1511
+ int isSurrogate = 1;
1512
+
1513
+ switch(stringState) {
1514
+ case JSONStringStateEscape:
1515
+ switch(currentChar) {
1516
+ case 'u': escapedUnicode1 = 0U; escapedUnicode2 = 0U; escapedUnicodeCodePoint = 0U; stringState = JSONStringStateEscapedUnicode1; break;
1517
+
1518
+ case 'b': escapedChar = '\b'; goto parsedEscapedChar;
1519
+ case 'f': escapedChar = '\f'; goto parsedEscapedChar;
1520
+ case 'n': escapedChar = '\n'; goto parsedEscapedChar;
1521
+ case 'r': escapedChar = '\r'; goto parsedEscapedChar;
1522
+ case 't': escapedChar = '\t'; goto parsedEscapedChar;
1523
+ case '\\': escapedChar = '\\'; goto parsedEscapedChar;
1524
+ case '/': escapedChar = '/'; goto parsedEscapedChar;
1525
+ case '"': escapedChar = '"'; goto parsedEscapedChar;
1526
+
1527
+ parsedEscapedChar:
1528
+ stringState = JSONStringStateParsing;
1529
+ stringHash = jk_calculateHash(stringHash, escapedChar);
1530
+ tokenBuffer[tokenBufferIdx++] = escapedChar;
1531
+ break;
1532
+
1533
+ default: jk_error(parseState, @"Invalid escape sequence found in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; break;
1534
+ }
1535
+ break;
1536
+
1537
+ case JSONStringStateEscapedUnicode1:
1538
+ case JSONStringStateEscapedUnicode2:
1539
+ case JSONStringStateEscapedUnicode3:
1540
+ case JSONStringStateEscapedUnicode4: isSurrogate = 0;
1541
+ case JSONStringStateEscapedUnicodeSurrogate1:
1542
+ case JSONStringStateEscapedUnicodeSurrogate2:
1543
+ case JSONStringStateEscapedUnicodeSurrogate3:
1544
+ case JSONStringStateEscapedUnicodeSurrogate4:
1545
+ {
1546
+ uint16_t hexValue = 0U;
1547
+
1548
+ switch(currentChar) {
1549
+ case '0' ... '9': hexValue = currentChar - '0'; goto parsedHex;
1550
+ case 'a' ... 'f': hexValue = (currentChar - 'a') + 10U; goto parsedHex;
1551
+ case 'A' ... 'F': hexValue = (currentChar - 'A') + 10U; goto parsedHex;
1552
+
1553
+ parsedHex:
1554
+ if(!isSurrogate) { escapedUnicode1 = (escapedUnicode1 << 4) | hexValue; } else { escapedUnicode2 = (escapedUnicode2 << 4) | hexValue; }
1555
+
1556
+ if(stringState == JSONStringStateEscapedUnicode4) {
1557
+ if(((escapedUnicode1 >= 0xD800U) && (escapedUnicode1 < 0xE000U))) {
1558
+ if((escapedUnicode1 >= 0xD800U) && (escapedUnicode1 < 0xDC00U)) { stringState = JSONStringStateEscapedNeedEscapeForSurrogate; }
1559
+ else if((escapedUnicode1 >= 0xDC00U) && (escapedUnicode1 < 0xE000U)) {
1560
+ if((parseState->parseOptionFlags & JKParseOptionLooseUnicode)) { escapedUnicodeCodePoint = UNI_REPLACEMENT_CHAR; }
1561
+ else { jk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
1562
+ }
1563
+ }
1564
+ else { escapedUnicodeCodePoint = escapedUnicode1; }
1565
+ }
1566
+
1567
+ if(stringState == JSONStringStateEscapedUnicodeSurrogate4) {
1568
+ if((escapedUnicode2 < 0xdc00) || (escapedUnicode2 > 0xdfff)) {
1569
+ if((parseState->parseOptionFlags & JKParseOptionLooseUnicode)) { escapedUnicodeCodePoint = UNI_REPLACEMENT_CHAR; }
1570
+ else { jk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
1571
+ }
1572
+ else { escapedUnicodeCodePoint = ((escapedUnicode1 - 0xd800) * 0x400) + (escapedUnicode2 - 0xdc00) + 0x10000; }
1573
+ }
1574
+
1575
+ if((stringState == JSONStringStateEscapedUnicode4) || (stringState == JSONStringStateEscapedUnicodeSurrogate4)) {
1576
+ if((isValidCodePoint(&escapedUnicodeCodePoint) == sourceIllegal) && ((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0)) { jk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
1577
+ stringState = JSONStringStateParsing;
1578
+ 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; }
1579
+ }
1580
+ else if((stringState >= JSONStringStateEscapedUnicode1) && (stringState <= JSONStringStateEscapedUnicodeSurrogate4)) { stringState++; }
1581
+ break;
1582
+
1583
+ 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;
1584
+ }
1585
+ }
1586
+ break;
1587
+
1588
+ case JSONStringStateEscapedNeedEscapeForSurrogate:
1589
+ if(currentChar == '\\') { stringState = JSONStringStateEscapedNeedEscapedUForSurrogate; }
1590
+ else {
1591
+ 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; }
1592
+ 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; } }
1593
+ }
1594
+ break;
1595
+
1596
+ case JSONStringStateEscapedNeedEscapedUForSurrogate:
1597
+ if(currentChar == 'u') { stringState = JSONStringStateEscapedUnicodeSurrogate1; }
1598
+ else {
1599
+ 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; }
1600
+ 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; } }
1601
+ }
1602
+ break;
1603
+
1604
+ default: jk_error(parseState, @"Internal error: Unknown stringState. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; break;
1605
+ }
1606
+ }
1607
+ }
1608
+
1609
+ finishedParsing:
1610
+
1611
+ if(JK_EXPECT_T(stringState == JSONStringStateFinished)) {
1612
+ NSCParameterAssert((parseState->stringBuffer.bytes.ptr + tokenStartIndex) < atStringCharacter);
1613
+
1614
+ parseState->token.tokenPtrRange.ptr = parseState->stringBuffer.bytes.ptr + tokenStartIndex;
1615
+ parseState->token.tokenPtrRange.length = (atStringCharacter - parseState->token.tokenPtrRange.ptr);
1616
+
1617
+ if(JK_EXPECT_T(onlySimpleString)) {
1618
+ NSCParameterAssert(((parseState->token.tokenPtrRange.ptr + 1) < endOfBuffer) && (parseState->token.tokenPtrRange.length >= 2UL) && (((parseState->token.tokenPtrRange.ptr + 1) + (parseState->token.tokenPtrRange.length - 2)) < endOfBuffer));
1619
+ parseState->token.value.ptrRange.ptr = parseState->token.tokenPtrRange.ptr + 1;
1620
+ parseState->token.value.ptrRange.length = parseState->token.tokenPtrRange.length - 2UL;
1621
+ } else {
1622
+ parseState->token.value.ptrRange.ptr = parseState->token.tokenBuffer.bytes.ptr;
1623
+ parseState->token.value.ptrRange.length = tokenBufferIdx;
1624
+ }
1625
+
1626
+ parseState->token.value.hash = stringHash;
1627
+ parseState->token.value.type = JKValueTypeString;
1628
+ parseState->atIndex = (atStringCharacter - parseState->stringBuffer.bytes.ptr);
1629
+ }
1630
+
1631
+ if(JK_EXPECT_F(stringState != JSONStringStateFinished)) { jk_error(parseState, @"Invalid string."); }
1632
+ return(JK_EXPECT_T(stringState == JSONStringStateFinished) ? 0 : 1);
1633
+ }
1634
+
1635
+ static int jk_parse_number(JKParseState *parseState) {
1636
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
1637
+ const unsigned char *numberStart = JK_AT_STRING_PTR(parseState);
1638
+ const unsigned char *endOfBuffer = JK_END_STRING_PTR(parseState);
1639
+ const unsigned char *atNumberCharacter = NULL;
1640
+ int numberState = JSONNumberStateWholeNumberStart, isFloatingPoint = 0, isNegative = 0, backup = 0;
1641
+ size_t startingIndex = parseState->atIndex;
1642
+
1643
+ for(atNumberCharacter = numberStart; (JK_EXPECT_T(atNumberCharacter < endOfBuffer)) && (JK_EXPECT_T(!(JK_EXPECT_F(numberState == JSONNumberStateFinished) || JK_EXPECT_F(numberState == JSONNumberStateError)))); atNumberCharacter++) {
1644
+ unsigned long currentChar = (unsigned long)(*atNumberCharacter), lowerCaseCC = currentChar | 0x20UL;
1645
+
1646
+ switch(numberState) {
1647
+ case JSONNumberStateWholeNumberStart: if (currentChar == '-') { numberState = JSONNumberStateWholeNumberMinus; isNegative = 1; break; }
1648
+ case JSONNumberStateWholeNumberMinus: if (currentChar == '0') { numberState = JSONNumberStateWholeNumberZero; break; }
1649
+ else if( (currentChar >= '1') && (currentChar <= '9')) { numberState = JSONNumberStateWholeNumber; break; }
1650
+ else { /* XXX Add error message */ numberState = JSONNumberStateError; break; }
1651
+ case JSONNumberStateExponentStart: if( (currentChar == '+') || (currentChar == '-')) { numberState = JSONNumberStateExponentPlusMinus; break; }
1652
+ case JSONNumberStateFractionalNumberStart:
1653
+ case JSONNumberStateExponentPlusMinus:if(!((currentChar >= '0') && (currentChar <= '9'))) { /* XXX Add error message */ numberState = JSONNumberStateError; break; }
1654
+ else { if(numberState == JSONNumberStateFractionalNumberStart) { numberState = JSONNumberStateFractionalNumber; }
1655
+ else { numberState = JSONNumberStateExponent; } break; }
1656
+ case JSONNumberStateWholeNumberZero:
1657
+ case JSONNumberStateWholeNumber: if (currentChar == '.') { numberState = JSONNumberStateFractionalNumberStart; isFloatingPoint = 1; break; }
1658
+ case JSONNumberStateFractionalNumber: if (lowerCaseCC == 'e') { numberState = JSONNumberStateExponentStart; isFloatingPoint = 1; break; }
1659
+ case JSONNumberStateExponent: if(!((currentChar >= '0') && (currentChar <= '9')) || (numberState == JSONNumberStateWholeNumberZero)) { numberState = JSONNumberStateFinished; backup = 1; break; }
1660
+ break;
1661
+ default: /* XXX Add error message */ numberState = JSONNumberStateError; break;
1662
+ }
1663
+ }
1664
+
1665
+ parseState->token.tokenPtrRange.ptr = parseState->stringBuffer.bytes.ptr + startingIndex;
1666
+ parseState->token.tokenPtrRange.length = (atNumberCharacter - parseState->token.tokenPtrRange.ptr) - backup;
1667
+ parseState->atIndex = (parseState->token.tokenPtrRange.ptr + parseState->token.tokenPtrRange.length) - parseState->stringBuffer.bytes.ptr;
1668
+
1669
+ if(JK_EXPECT_T(numberState == JSONNumberStateFinished)) {
1670
+ unsigned char numberTempBuf[parseState->token.tokenPtrRange.length + 4UL];
1671
+ unsigned char *endOfNumber = NULL;
1672
+
1673
+ memcpy(numberTempBuf, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length);
1674
+ numberTempBuf[parseState->token.tokenPtrRange.length] = 0;
1675
+
1676
+ errno = 0;
1677
+
1678
+ // Treat "-0" as a floating point number, which is capable of representing negative zeros.
1679
+ if(JK_EXPECT_F(parseState->token.tokenPtrRange.length == 2UL) && JK_EXPECT_F(numberTempBuf[1] == '0') && JK_EXPECT_F(isNegative)) { isFloatingPoint = 1; }
1680
+
1681
+ if(isFloatingPoint) {
1682
+ 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).
1683
+ parseState->token.value.type = JKValueTypeDouble;
1684
+ parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.doubleValue;
1685
+ parseState->token.value.ptrRange.length = sizeof(double);
1686
+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type);
1687
+ } else {
1688
+ if(isNegative) {
1689
+ parseState->token.value.number.longLongValue = strtoll((const char *)numberTempBuf, (char **)&endOfNumber, 10);
1690
+ parseState->token.value.type = JKValueTypeLongLong;
1691
+ parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.longLongValue;
1692
+ parseState->token.value.ptrRange.length = sizeof(long long);
1693
+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type) + (JKHash)parseState->token.value.number.longLongValue;
1694
+ } else {
1695
+ parseState->token.value.number.unsignedLongLongValue = strtoull((const char *)numberTempBuf, (char **)&endOfNumber, 10);
1696
+ parseState->token.value.type = JKValueTypeUnsignedLongLong;
1697
+ parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.unsignedLongLongValue;
1698
+ parseState->token.value.ptrRange.length = sizeof(unsigned long long);
1699
+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type) + (JKHash)parseState->token.value.number.unsignedLongLongValue;
1700
+ }
1701
+ }
1702
+
1703
+ if(JK_EXPECT_F(errno != 0)) {
1704
+ numberState = JSONNumberStateError;
1705
+ if(errno == ERANGE) {
1706
+ switch(parseState->token.value.type) {
1707
+ 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.
1708
+ case JKValueTypeLongLong: jk_error(parseState, @"The value '%s' exceeded the minimum value that could be represented: %lld.", numberTempBuf, parseState->token.value.number.longLongValue); break;
1709
+ case JKValueTypeUnsignedLongLong: jk_error(parseState, @"The value '%s' exceeded the maximum value that could be represented: %llu.", numberTempBuf, parseState->token.value.number.unsignedLongLongValue); break;
1710
+ default: jk_error(parseState, @"Internal error: Unknown token value type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
1711
+ }
1712
+ }
1713
+ }
1714
+ 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."); }
1715
+
1716
+ size_t hashIndex = 0UL;
1717
+ 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]); }
1718
+ }
1719
+
1720
+ if(JK_EXPECT_F(numberState != JSONNumberStateFinished)) { jk_error(parseState, @"Invalid number."); }
1721
+ return(JK_EXPECT_T((numberState == JSONNumberStateFinished)) ? 0 : 1);
1722
+ }
1723
+
1724
+ JK_STATIC_INLINE void jk_set_parsed_token(JKParseState *parseState, const unsigned char *ptr, size_t length, JKTokenType type, size_t advanceBy) {
1725
+ parseState->token.tokenPtrRange.ptr = ptr;
1726
+ parseState->token.tokenPtrRange.length = length;
1727
+ parseState->token.type = type;
1728
+ parseState->atIndex += advanceBy;
1729
+ }
1730
+
1731
+ static size_t jk_parse_is_newline(JKParseState *parseState, const unsigned char *atCharacterPtr) {
1732
+ NSCParameterAssert((parseState != NULL) && (atCharacterPtr != NULL) && (atCharacterPtr >= parseState->stringBuffer.bytes.ptr) && (atCharacterPtr < JK_END_STRING_PTR(parseState)));
1733
+ const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
1734
+
1735
+ if(JK_EXPECT_F(atCharacterPtr >= endOfStringPtr)) { return(0UL); }
1736
+
1737
+ if(JK_EXPECT_F((*(atCharacterPtr + 0)) == '\n')) { return(1UL); }
1738
+ if(JK_EXPECT_F((*(atCharacterPtr + 0)) == '\r')) { if((JK_EXPECT_T((atCharacterPtr + 1) < endOfStringPtr)) && ((*(atCharacterPtr + 1)) == '\n')) { return(2UL); } return(1UL); }
1739
+ if(parseState->parseOptionFlags & JKParseOptionUnicodeNewlines) {
1740
+ if((JK_EXPECT_F((*(atCharacterPtr + 0)) == 0xc2)) && (((atCharacterPtr + 1) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x85))) { return(2UL); }
1741
+ if((JK_EXPECT_F((*(atCharacterPtr + 0)) == 0xe2)) && (((atCharacterPtr + 2) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x80) && (((*(atCharacterPtr + 2)) == 0xa8) || ((*(atCharacterPtr + 2)) == 0xa9)))) { return(3UL); }
1742
+ }
1743
+
1744
+ return(0UL);
1745
+ }
1746
+
1747
+ JK_STATIC_INLINE int jk_parse_skip_newline(JKParseState *parseState) {
1748
+ size_t newlineAdvanceAtIndex = 0UL;
1749
+ 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); }
1750
+ return(0);
1751
+ }
1752
+
1753
+ JK_STATIC_INLINE void jk_parse_skip_whitespace(JKParseState *parseState) {
1754
+ #ifndef __clang_analyzer__
1755
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
1756
+ const unsigned char *atCharacterPtr = NULL;
1757
+ const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
1758
+
1759
+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) {
1760
+ if(((*(atCharacterPtr + 0)) == ' ') || ((*(atCharacterPtr + 0)) == '\t')) { continue; }
1761
+ if(jk_parse_skip_newline(parseState)) { continue; }
1762
+ if(parseState->parseOptionFlags & JKParseOptionComments) {
1763
+ if((JK_EXPECT_F((*(atCharacterPtr + 0)) == '/')) && (JK_EXPECT_T((atCharacterPtr + 1) < endOfStringPtr))) {
1764
+ if((*(atCharacterPtr + 1)) == '/') {
1765
+ parseState->atIndex++;
1766
+ 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; } }
1767
+ continue;
1768
+ }
1769
+ if((*(atCharacterPtr + 1)) == '*') {
1770
+ parseState->atIndex++;
1771
+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) {
1772
+ if(jk_parse_skip_newline(parseState)) { continue; }
1773
+ if(((*(atCharacterPtr + 0)) == '*') && (((atCharacterPtr + 1) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == '/'))) { parseState->atIndex++; break; }
1774
+ }
1775
+ continue;
1776
+ }
1777
+ }
1778
+ }
1779
+ break;
1780
+ }
1781
+ #endif
1782
+ }
1783
+
1784
+ static int jk_parse_next_token(JKParseState *parseState) {
1785
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
1786
+ const unsigned char *atCharacterPtr = NULL;
1787
+ const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
1788
+ unsigned char currentCharacter = 0U;
1789
+ int stopParsing = 0;
1790
+
1791
+ parseState->prev_atIndex = parseState->atIndex;
1792
+ parseState->prev_lineNumber = parseState->lineNumber;
1793
+ parseState->prev_lineStartIndex = parseState->lineStartIndex;
1794
+
1795
+ jk_parse_skip_whitespace(parseState);
1796
+
1797
+ if((JK_AT_STRING_PTR(parseState) == endOfStringPtr)) { stopParsing = 1; }
1798
+
1799
+ if((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr))) {
1800
+ currentCharacter = *atCharacterPtr;
1801
+
1802
+ 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); } }
1803
+ else if(JK_EXPECT_T(currentCharacter == ':')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeSeparator, 1UL); }
1804
+ else if(JK_EXPECT_T(currentCharacter == ',')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeComma, 1UL); }
1805
+ 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); } }
1806
+ else if(JK_EXPECT_T(currentCharacter == '{')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeObjectBegin, 1UL); }
1807
+ else if(JK_EXPECT_T(currentCharacter == '}')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeObjectEnd, 1UL); }
1808
+ else if(JK_EXPECT_T(currentCharacter == '[')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeArrayBegin, 1UL); }
1809
+ else if(JK_EXPECT_T(currentCharacter == ']')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeArrayEnd, 1UL); }
1810
+
1811
+ 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); } }
1812
+ 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); } }
1813
+ 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); } }
1814
+ else { stopParsing = 1; /* XXX Add error message */ }
1815
+ }
1816
+
1817
+ if(JK_EXPECT_F(stopParsing)) { jk_error(parseState, @"Unexpected token, wanted '{', '}', '[', ']', ',', ':', 'true', 'false', 'null', '\"STRING\"', 'NUMBER'."); }
1818
+ return(stopParsing);
1819
+ }
1820
+
1821
+ static void jk_error_parse_accept_or3(JKParseState *parseState, int state, NSString *or1String, NSString *or2String, NSString *or3String) {
1822
+ NSString *acceptStrings[16];
1823
+ int acceptIdx = 0;
1824
+ if(state & JKParseAcceptValue) { acceptStrings[acceptIdx++] = or1String; }
1825
+ if(state & JKParseAcceptComma) { acceptStrings[acceptIdx++] = or2String; }
1826
+ if(state & JKParseAcceptEnd) { acceptStrings[acceptIdx++] = or3String; }
1827
+ 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); }
1828
+ 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); }
1829
+ 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); }
1830
+ }
1831
+
1832
+ static void *jk_parse_array(JKParseState *parseState) {
1833
+ size_t startingObjectIndex = parseState->objectStack.index;
1834
+ int arrayState = JKParseAcceptValueOrEnd, stopParsing = 0;
1835
+ void *parsedArray = NULL;
1836
+
1837
+ while(JK_EXPECT_T((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length)))) {
1838
+ 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; } }
1839
+
1840
+ if(JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0)) {
1841
+ void *object = NULL;
1842
+ #ifndef NS_BLOCK_ASSERTIONS
1843
+ parseState->objectStack.objects[parseState->objectStack.index] = NULL;
1844
+ parseState->objectStack.keys [parseState->objectStack.index] = NULL;
1845
+ #endif
1846
+ switch(parseState->token.type) {
1847
+ case JKTokenTypeNumber:
1848
+ case JKTokenTypeString:
1849
+ case JKTokenTypeTrue:
1850
+ case JKTokenTypeFalse:
1851
+ case JKTokenTypeNull:
1852
+ case JKTokenTypeArrayBegin:
1853
+ case JKTokenTypeObjectBegin:
1854
+ if(JK_EXPECT_F((arrayState & JKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected value."); stopParsing = 1; break; }
1855
+ 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; }
1856
+ break;
1857
+ 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;
1858
+ case JKTokenTypeComma: if(JK_EXPECT_T(arrayState & JKParseAcceptComma)) { arrayState = JKParseAcceptValue; } else { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected ','."); stopParsing = 1; } break;
1859
+ default: parseState->errorIsPrev = 1; jk_error_parse_accept_or3(parseState, arrayState, @"a value", @"a comma", @"a ']'"); stopParsing = 1; break;
1860
+ }
1861
+ }
1862
+ }
1863
+
1864
+ 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; } } }
1865
+ #if !defined(NS_BLOCK_ASSERTIONS)
1866
+ else { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { parseState->objectStack.objects[idx] = NULL; parseState->objectStack.keys[idx] = NULL; } }
1867
+ #endif
1868
+
1869
+ parseState->objectStack.index = startingObjectIndex;
1870
+ return(parsedArray);
1871
+ }
1872
+
1873
+ static void *jk_create_dictionary(JKParseState *parseState, size_t startingObjectIndex) {
1874
+ void *parsedDictionary = NULL;
1875
+
1876
+ parseState->objectStack.index--;
1877
+
1878
+ parsedDictionary = _JKDictionaryCreate((id *)&parseState->objectStack.keys[startingObjectIndex], (NSUInteger *)&parseState->objectStack.cfHashes[startingObjectIndex], (id *)&parseState->objectStack.objects[startingObjectIndex], (parseState->objectStack.index - startingObjectIndex), parseState->mutableCollections);
1879
+
1880
+ return(parsedDictionary);
1881
+ }
1882
+
1883
+ static void *jk_parse_dictionary(JKParseState *parseState) {
1884
+ size_t startingObjectIndex = parseState->objectStack.index;
1885
+ int dictState = JKParseAcceptValueOrEnd, stopParsing = 0;
1886
+ void *parsedDictionary = NULL;
1887
+
1888
+ while(JK_EXPECT_T((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length)))) {
1889
+ 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; } }
1890
+
1891
+ size_t objectStackIndex = parseState->objectStack.index++;
1892
+ parseState->objectStack.keys[objectStackIndex] = NULL;
1893
+ parseState->objectStack.objects[objectStackIndex] = NULL;
1894
+ void *key = NULL, *object = NULL;
1895
+
1896
+ if(JK_EXPECT_T((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0)))) {
1897
+ switch(parseState->token.type) {
1898
+ case JKTokenTypeString:
1899
+ if(JK_EXPECT_F((dictState & JKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected string."); stopParsing = 1; break; }
1900
+ if(JK_EXPECT_F((key = jk_object_for_token(parseState)) == NULL)) { jk_error(parseState, @"Internal error: Key == NULL."); stopParsing = 1; break; }
1901
+ else {
1902
+ parseState->objectStack.keys[objectStackIndex] = key;
1903
+ 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; }
1904
+ else { parseState->objectStack.cfHashes[objectStackIndex] = CFHash(key); }
1905
+ }
1906
+ break;
1907
+
1908
+ 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;
1909
+ 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;
1910
+
1911
+ default: parseState->errorIsPrev = 1; jk_error_parse_accept_or3(parseState, dictState, @"a \"STRING\"", @"a comma", @"a '}'"); stopParsing = 1; break;
1912
+ }
1913
+ }
1914
+
1915
+ if(JK_EXPECT_T(stopParsing == 0)) {
1916
+ 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; } }
1917
+ }
1918
+
1919
+ if((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0))) {
1920
+ switch(parseState->token.type) {
1921
+ case JKTokenTypeNumber:
1922
+ case JKTokenTypeString:
1923
+ case JKTokenTypeTrue:
1924
+ case JKTokenTypeFalse:
1925
+ case JKTokenTypeNull:
1926
+ case JKTokenTypeArrayBegin:
1927
+ case JKTokenTypeObjectBegin:
1928
+ if(JK_EXPECT_F((dictState & JKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected value."); stopParsing = 1; break; }
1929
+ 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; }
1930
+ break;
1931
+ default: parseState->errorIsPrev = 1; jk_error_parse_accept_or3(parseState, dictState, @"a value", @"a comma", @"a '}'"); stopParsing = 1; break;
1932
+ }
1933
+ }
1934
+ }
1935
+
1936
+ 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; } } }
1937
+ #if !defined(NS_BLOCK_ASSERTIONS)
1938
+ else { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { parseState->objectStack.objects[idx] = NULL; parseState->objectStack.keys[idx] = NULL; } }
1939
+ #endif
1940
+
1941
+ parseState->objectStack.index = startingObjectIndex;
1942
+ return(parsedDictionary);
1943
+ }
1944
+
1945
+ static id json_parse_it(JKParseState *parseState) {
1946
+ id parsedObject = NULL;
1947
+ int stopParsing = 0;
1948
+
1949
+ while((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length))) {
1950
+ if((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0))) {
1951
+ switch(parseState->token.type) {
1952
+ case JKTokenTypeArrayBegin:
1953
+ case JKTokenTypeObjectBegin: parsedObject = [(id)jk_object_for_token(parseState) autorelease]; stopParsing = 1; break;
1954
+ default: jk_error(parseState, @"Expected either '[' or '{'."); stopParsing = 1; break;
1955
+ }
1956
+ }
1957
+ }
1958
+
1959
+ NSCParameterAssert((parseState->objectStack.index == 0) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
1960
+
1961
+ if((parsedObject == NULL) && (JK_AT_STRING_PTR(parseState) == JK_END_STRING_PTR(parseState))) { jk_error(parseState, @"Reached the end of the buffer."); }
1962
+ if(parsedObject == NULL) { jk_error(parseState, @"Unable to parse JSON."); }
1963
+
1964
+ if((parsedObject != NULL) && (JK_AT_STRING_PTR(parseState) < JK_END_STRING_PTR(parseState))) {
1965
+ jk_parse_skip_whitespace(parseState);
1966
+ if((parsedObject != NULL) && ((parseState->parseOptionFlags & JKParseOptionPermitTextAfterValidJSON) == 0) && (JK_AT_STRING_PTR(parseState) < JK_END_STRING_PTR(parseState))) {
1967
+ jk_error(parseState, @"A valid JSON object was parsed but there were additional non-white-space characters remaining.");
1968
+ parsedObject = NULL;
1969
+ }
1970
+ }
1971
+
1972
+ return(parsedObject);
1973
+ }
1974
+
1975
+ ////////////
1976
+ #pragma mark -
1977
+ #pragma mark Object cache
1978
+
1979
+ // 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.
1980
+ // 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:
1981
+ JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) {
1982
+ NSCParameterAssert((parseState != NULL) && (parseState->cache.prng_lfsr != 0U));
1983
+ parseState->cache.prng_lfsr = (parseState->cache.prng_lfsr >> 1) ^ ((0U - (parseState->cache.prng_lfsr & 1U)) & 0x80200003U);
1984
+ parseState->cache.age[parseState->cache.prng_lfsr & (parseState->cache.count - 1UL)] >>= 1;
1985
+ }
1986
+
1987
+ // The object cache is nothing more than a hash table with open addressing collision resolution that is bounded by JK_CACHE_PROBES attempts.
1988
+ //
1989
+ // 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].
1990
+ //
1991
+ // 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.
1992
+ // Thus, an items age is managed using an AIMD policy- additive increase, multiplicative decrease. All age calculations and manipulations are branchless.
1993
+ // The primitive C type MUST be unsigned. It is currently a "char", which allows (at a minimum and in practice) 8 bits.
1994
+ //
1995
+ // A "useable bucket" is a bucket that is not in use (never populated), or has an age == 0.
1996
+ //
1997
+ // When an item is found in the cache, it's age is incremented.
1998
+ // If a useable bucket hasn't been found, the current item (bucket) is aged along with two random items.
1999
+ //
2000
+ // If a value is not found in the cache, and no useable bucket has been found, that value is not added to the cache.
2001
+
2002
+ static void *jk_cachedObjects(JKParseState *parseState) {
2003
+ unsigned long bucket = parseState->token.value.hash & (parseState->cache.count - 1UL), setBucket = 0UL, useableBucket = 0UL, x = 0UL;
2004
+ void *parsedAtom = NULL;
2005
+
2006
+ if(JK_EXPECT_F(parseState->token.value.ptrRange.length == 0UL) && JK_EXPECT_T(parseState->token.value.type == JKValueTypeString)) { return(@""); }
2007
+
2008
+ for(x = 0UL; x < JK_CACHE_PROBES; x++) {
2009
+ if(JK_EXPECT_F(parseState->cache.items[bucket].object == NULL)) { setBucket = 1UL; useableBucket = bucket; break; }
2010
+
2011
+ 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))) {
2012
+ parseState->cache.age[bucket] = (((uint32_t)parseState->cache.age[bucket]) + 1U) - (((((uint32_t)parseState->cache.age[bucket]) + 1U) >> 31) ^ 1U);
2013
+ parseState->token.value.cacheItem = &parseState->cache.items[bucket];
2014
+ NSCParameterAssert(parseState->cache.items[bucket].object != NULL);
2015
+ return((void *)CFRetain(parseState->cache.items[bucket].object));
2016
+ } else {
2017
+ if(JK_EXPECT_F(setBucket == 0UL) && JK_EXPECT_F(parseState->cache.age[bucket] == 0U)) { setBucket = 1UL; useableBucket = bucket; }
2018
+ if(JK_EXPECT_F(setBucket == 0UL)) { parseState->cache.age[bucket] >>= 1; jk_cache_age(parseState); jk_cache_age(parseState); }
2019
+ // 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.
2020
+ // 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.
2021
+ 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);
2022
+ }
2023
+ }
2024
+
2025
+ switch(parseState->token.value.type) {
2026
+ case JKValueTypeString: parsedAtom = (void *)CFStringCreateWithBytes(NULL, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length, kCFStringEncodingUTF8, 0); break;
2027
+ case JKValueTypeLongLong: parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberLongLongType, &parseState->token.value.number.longLongValue); break;
2028
+ case JKValueTypeUnsignedLongLong:
2029
+ if(parseState->token.value.number.unsignedLongLongValue <= LLONG_MAX) { parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberLongLongType, &parseState->token.value.number.unsignedLongLongValue); }
2030
+ else { parsedAtom = (void *)parseState->objCImpCache.NSNumberInitWithUnsignedLongLong(parseState->objCImpCache.NSNumberAlloc(parseState->objCImpCache.NSNumberClass, @selector(alloc)), @selector(initWithUnsignedLongLong:), parseState->token.value.number.unsignedLongLongValue); }
2031
+ break;
2032
+ case JKValueTypeDouble: parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberDoubleType, &parseState->token.value.number.doubleValue); break;
2033
+ default: jk_error(parseState, @"Internal error: Unknown token value type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
2034
+ }
2035
+
2036
+ if(JK_EXPECT_T(setBucket) && (JK_EXPECT_T(parsedAtom != NULL))) {
2037
+ bucket = useableBucket;
2038
+ if(JK_EXPECT_T((parseState->cache.items[bucket].object != NULL))) { CFRelease(parseState->cache.items[bucket].object); parseState->cache.items[bucket].object = NULL; }
2039
+
2040
+ if(JK_EXPECT_T((parseState->cache.items[bucket].bytes = (unsigned char *)reallocf(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.length)) != NULL)) {
2041
+ memcpy(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length);
2042
+ parseState->cache.items[bucket].object = (void *)CFRetain(parsedAtom);
2043
+ parseState->cache.items[bucket].hash = parseState->token.value.hash;
2044
+ parseState->cache.items[bucket].cfHash = 0UL;
2045
+ parseState->cache.items[bucket].size = parseState->token.value.ptrRange.length;
2046
+ parseState->cache.items[bucket].type = parseState->token.value.type;
2047
+ parseState->token.value.cacheItem = &parseState->cache.items[bucket];
2048
+ parseState->cache.age[bucket] = JK_INIT_CACHE_AGE;
2049
+ } else { // The realloc failed, so clear the appropriate fields.
2050
+ parseState->cache.items[bucket].hash = 0UL;
2051
+ parseState->cache.items[bucket].cfHash = 0UL;
2052
+ parseState->cache.items[bucket].size = 0UL;
2053
+ parseState->cache.items[bucket].type = 0UL;
2054
+ }
2055
+ }
2056
+
2057
+ return(parsedAtom);
2058
+ }
2059
+
2060
+
2061
+ static void *jk_object_for_token(JKParseState *parseState) {
2062
+ void *parsedAtom = NULL;
2063
+
2064
+ parseState->token.value.cacheItem = NULL;
2065
+ switch(parseState->token.type) {
2066
+ case JKTokenTypeString: parsedAtom = jk_cachedObjects(parseState); break;
2067
+ case JKTokenTypeNumber: parsedAtom = jk_cachedObjects(parseState); break;
2068
+ case JKTokenTypeObjectBegin: parsedAtom = jk_parse_dictionary(parseState); break;
2069
+ case JKTokenTypeArrayBegin: parsedAtom = jk_parse_array(parseState); break;
2070
+ case JKTokenTypeTrue: parsedAtom = (void *)kCFBooleanTrue; break;
2071
+ case JKTokenTypeFalse: parsedAtom = (void *)kCFBooleanFalse; break;
2072
+ case JKTokenTypeNull: parsedAtom = (void *)kCFNull; break;
2073
+ default: jk_error(parseState, @"Internal error: Unknown token type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
2074
+ }
2075
+
2076
+ return(parsedAtom);
2077
+ }
2078
+
2079
+ #pragma mark -
2080
+ @implementation JSONDecoder
2081
+
2082
+ + (id)decoder
2083
+ {
2084
+ return([self decoderWithParseOptions:JKParseOptionStrict]);
2085
+ }
2086
+
2087
+ + (id)decoderWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2088
+ {
2089
+ return([[[self alloc] initWithParseOptions:parseOptionFlags] autorelease]);
2090
+ }
2091
+
2092
+ - (id)init
2093
+ {
2094
+ return([self initWithParseOptions:JKParseOptionStrict]);
2095
+ }
2096
+
2097
+ - (id)initWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2098
+ {
2099
+ if((self = [super init]) == NULL) { return(NULL); }
2100
+
2101
+ if(parseOptionFlags & ~JKParseOptionValidFlags) { [self autorelease]; [NSException raise:NSInvalidArgumentException format:@"Invalid parse options."]; }
2102
+
2103
+ if((parseState = (JKParseState *)calloc(1UL, sizeof(JKParseState))) == NULL) { goto errorExit; }
2104
+
2105
+ parseState->parseOptionFlags = parseOptionFlags;
2106
+
2107
+ parseState->token.tokenBuffer.roundSizeUpToMultipleOf = 4096UL;
2108
+ parseState->objectStack.roundSizeUpToMultipleOf = 2048UL;
2109
+
2110
+ parseState->objCImpCache.NSNumberClass = _jk_NSNumberClass;
2111
+ parseState->objCImpCache.NSNumberAlloc = _jk_NSNumberAllocImp;
2112
+ parseState->objCImpCache.NSNumberInitWithUnsignedLongLong = _jk_NSNumberInitWithUnsignedLongLongImp;
2113
+
2114
+ parseState->cache.prng_lfsr = 1U;
2115
+ parseState->cache.count = JK_CACHE_SLOTS;
2116
+ if((parseState->cache.items = (JKTokenCacheItem *)calloc(1UL, sizeof(JKTokenCacheItem) * parseState->cache.count)) == NULL) { goto errorExit; }
2117
+
2118
+ return(self);
2119
+
2120
+ errorExit:
2121
+ if(self) { [self autorelease]; self = NULL; }
2122
+ return(NULL);
2123
+ }
2124
+
2125
+ // 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.
2126
+ static void _JSONDecoderCleanup(JSONDecoder *decoder) {
2127
+ if((decoder != NULL) && (decoder->parseState != NULL)) {
2128
+ jk_managedBuffer_release(&decoder->parseState->token.tokenBuffer);
2129
+ jk_objectStack_release(&decoder->parseState->objectStack);
2130
+
2131
+ [decoder clearCache];
2132
+ if(decoder->parseState->cache.items != NULL) { free(decoder->parseState->cache.items); decoder->parseState->cache.items = NULL; }
2133
+
2134
+ free(decoder->parseState); decoder->parseState = NULL;
2135
+ }
2136
+ }
2137
+
2138
+ - (void)dealloc
2139
+ {
2140
+ _JSONDecoderCleanup(self);
2141
+ [super dealloc];
2142
+ }
2143
+
2144
+ - (void)clearCache
2145
+ {
2146
+ if(JK_EXPECT_T(parseState != NULL)) {
2147
+ if(JK_EXPECT_T(parseState->cache.items != NULL)) {
2148
+ size_t idx = 0UL;
2149
+ for(idx = 0UL; idx < parseState->cache.count; idx++) {
2150
+ if(JK_EXPECT_T(parseState->cache.items[idx].object != NULL)) { CFRelease(parseState->cache.items[idx].object); parseState->cache.items[idx].object = NULL; }
2151
+ if(JK_EXPECT_T(parseState->cache.items[idx].bytes != NULL)) { free(parseState->cache.items[idx].bytes); parseState->cache.items[idx].bytes = NULL; }
2152
+ memset(&parseState->cache.items[idx], 0, sizeof(JKTokenCacheItem));
2153
+ parseState->cache.age[idx] = 0U;
2154
+ }
2155
+ }
2156
+ }
2157
+ }
2158
+
2159
+ // This needs to be completely rewritten.
2160
+ static id _JKParseUTF8String(JKParseState *parseState, BOOL mutableCollections, const unsigned char *string, size_t length, NSError **error) {
2161
+ NSCParameterAssert((parseState != NULL) && (string != NULL) && (parseState->cache.prng_lfsr != 0U));
2162
+ parseState->stringBuffer.bytes.ptr = string;
2163
+ parseState->stringBuffer.bytes.length = length;
2164
+ parseState->atIndex = 0UL;
2165
+ parseState->lineNumber = 1UL;
2166
+ parseState->lineStartIndex = 0UL;
2167
+ parseState->prev_atIndex = 0UL;
2168
+ parseState->prev_lineNumber = 1UL;
2169
+ parseState->prev_lineStartIndex = 0UL;
2170
+ parseState->error = NULL;
2171
+ parseState->errorIsPrev = 0;
2172
+ parseState->mutableCollections = (mutableCollections == NO) ? NO : YES;
2173
+
2174
+ unsigned char stackTokenBuffer[JK_TOKENBUFFER_SIZE] JK_ALIGNED(64);
2175
+ jk_managedBuffer_setToStackBuffer(&parseState->token.tokenBuffer, stackTokenBuffer, sizeof(stackTokenBuffer));
2176
+
2177
+ void *stackObjects [JK_STACK_OBJS] JK_ALIGNED(64);
2178
+ void *stackKeys [JK_STACK_OBJS] JK_ALIGNED(64);
2179
+ CFHashCode stackCFHashes[JK_STACK_OBJS] JK_ALIGNED(64);
2180
+ jk_objectStack_setToStackBuffer(&parseState->objectStack, stackObjects, stackKeys, stackCFHashes, JK_STACK_OBJS);
2181
+
2182
+ id parsedJSON = json_parse_it(parseState);
2183
+
2184
+ if((error != NULL) && (parseState->error != NULL)) { *error = parseState->error; }
2185
+
2186
+ jk_managedBuffer_release(&parseState->token.tokenBuffer);
2187
+ jk_objectStack_release(&parseState->objectStack);
2188
+
2189
+ parseState->stringBuffer.bytes.ptr = NULL;
2190
+ parseState->stringBuffer.bytes.length = 0UL;
2191
+ parseState->atIndex = 0UL;
2192
+ parseState->lineNumber = 1UL;
2193
+ parseState->lineStartIndex = 0UL;
2194
+ parseState->prev_atIndex = 0UL;
2195
+ parseState->prev_lineNumber = 1UL;
2196
+ parseState->prev_lineStartIndex = 0UL;
2197
+ parseState->error = NULL;
2198
+ parseState->errorIsPrev = 0;
2199
+ parseState->mutableCollections = NO;
2200
+
2201
+ return(parsedJSON);
2202
+ }
2203
+
2204
+ ////////////
2205
+ #pragma mark Deprecated as of v1.4
2206
+ ////////////
2207
+
2208
+ // Deprecated in JSONKit v1.4. Use objectWithUTF8String:length: instead.
2209
+ - (id)parseUTF8String:(const unsigned char *)string length:(size_t)length
2210
+ {
2211
+ return([self objectWithUTF8String:string length:length error:NULL]);
2212
+ }
2213
+
2214
+ // Deprecated in JSONKit v1.4. Use objectWithUTF8String:length:error: instead.
2215
+ - (id)parseUTF8String:(const unsigned char *)string length:(size_t)length error:(NSError **)error
2216
+ {
2217
+ return([self objectWithUTF8String:string length:length error:error]);
2218
+ }
2219
+
2220
+ // Deprecated in JSONKit v1.4. Use objectWithData: instead.
2221
+ - (id)parseJSONData:(NSData *)jsonData
2222
+ {
2223
+ return([self objectWithData:jsonData error:NULL]);
2224
+ }
2225
+
2226
+ // Deprecated in JSONKit v1.4. Use objectWithData:error: instead.
2227
+ - (id)parseJSONData:(NSData *)jsonData error:(NSError **)error
2228
+ {
2229
+ return([self objectWithData:jsonData error:error]);
2230
+ }
2231
+
2232
+ ////////////
2233
+ #pragma mark Methods that return immutable collection objects
2234
+ ////////////
2235
+
2236
+ - (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length
2237
+ {
2238
+ return([self objectWithUTF8String:string length:length error:NULL]);
2239
+ }
2240
+
2241
+ - (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error
2242
+ {
2243
+ if(parseState == NULL) { [NSException raise:NSInternalInconsistencyException format:@"parseState is NULL."]; }
2244
+ if(string == NULL) { [NSException raise:NSInvalidArgumentException format:@"The string argument is NULL."]; }
2245
+
2246
+ return(_JKParseUTF8String(parseState, NO, string, (size_t)length, error));
2247
+ }
2248
+
2249
+ - (id)objectWithData:(NSData *)jsonData
2250
+ {
2251
+ return([self objectWithData:jsonData error:NULL]);
2252
+ }
2253
+
2254
+ - (id)objectWithData:(NSData *)jsonData error:(NSError **)error
2255
+ {
2256
+ if(jsonData == NULL) { [NSException raise:NSInvalidArgumentException format:@"The jsonData argument is NULL."]; }
2257
+ return([self objectWithUTF8String:(const unsigned char *)[jsonData bytes] length:[jsonData length] error:error]);
2258
+ }
2259
+
2260
+ ////////////
2261
+ #pragma mark Methods that return mutable collection objects
2262
+ ////////////
2263
+
2264
+ - (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length
2265
+ {
2266
+ return([self mutableObjectWithUTF8String:string length:length error:NULL]);
2267
+ }
2268
+
2269
+ - (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error
2270
+ {
2271
+ if(parseState == NULL) { [NSException raise:NSInternalInconsistencyException format:@"parseState is NULL."]; }
2272
+ if(string == NULL) { [NSException raise:NSInvalidArgumentException format:@"The string argument is NULL."]; }
2273
+
2274
+ return(_JKParseUTF8String(parseState, YES, string, (size_t)length, error));
2275
+ }
2276
+
2277
+ - (id)mutableObjectWithData:(NSData *)jsonData
2278
+ {
2279
+ return([self mutableObjectWithData:jsonData error:NULL]);
2280
+ }
2281
+
2282
+ - (id)mutableObjectWithData:(NSData *)jsonData error:(NSError **)error
2283
+ {
2284
+ if(jsonData == NULL) { [NSException raise:NSInvalidArgumentException format:@"The jsonData argument is NULL."]; }
2285
+ return([self mutableObjectWithUTF8String:(const unsigned char *)[jsonData bytes] length:[jsonData length] error:error]);
2286
+ }
2287
+
2288
+ @end
2289
+
2290
+ /*
2291
+ The NSString and NSData convenience methods need a little bit of explanation.
2292
+
2293
+ Prior to JSONKit v1.4, the NSString -objectFromJSONStringWithParseOptions:error: method looked like
2294
+
2295
+ const unsigned char *utf8String = (const unsigned char *)[self UTF8String];
2296
+ if(utf8String == NULL) { return(NULL); }
2297
+ size_t utf8Length = strlen((const char *)utf8String);
2298
+ return([[JSONDecoder decoderWithParseOptions:parseOptionFlags] parseUTF8String:utf8String length:utf8Length error:error]);
2299
+
2300
+ This changed with v1.4 to a more complicated method. The reason for this is to keep the amount of memory that is
2301
+ allocated, but not yet freed because it is dependent on the autorelease pool to pop before it can be reclaimed.
2302
+
2303
+ In the simpler v1.3 code, this included all the bytes used to store the -UTF8String along with the JSONDecoder and all its overhead.
2304
+
2305
+ 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
2306
+ conversion of said string.
2307
+
2308
+ Once parsed, the CFMutableData has its length set to 0. This should, hopefully, allow the CFMutableData to realloc and/or free
2309
+ the buffer.
2310
+
2311
+ Another change made was a slight modification to JSONDecoder so that most of the cleanup work that was done in -dealloc was moved
2312
+ to a private, internal function. These convenience routines keep the pointer to the autoreleased JSONDecoder and calls
2313
+ _JSONDecoderCleanup() to early release the decoders resources since we already know that particular decoder is not going to be used
2314
+ again.
2315
+
2316
+ If everything goes smoothly, this will most likely result in perhaps a few hundred bytes that are allocated but waiting for the
2317
+ autorelease pool to pop. This is compared to the thousands and easily hundreds of thousands of bytes that would have been in
2318
+ autorelease limbo. It's more complicated for us, but a win for the user.
2319
+
2320
+ Autorelease objects are used in case things don't go smoothly. By having them autoreleased, we effectively guarantee that our
2321
+ requirement to -release the object is always met, not matter what goes wrong. The downside is having a an object or two in
2322
+ autorelease limbo, but we've done our best to minimize that impact, so it all balances out.
2323
+ */
2324
+
2325
+ @implementation NSString (JSONKitDeserializing)
2326
+
2327
+ static id _NSStringObjectFromJSONString(NSString *jsonString, JKParseOptionFlags parseOptionFlags, NSError **error, BOOL mutableCollection) {
2328
+ id returnObject = NULL;
2329
+ CFMutableDataRef mutableData = NULL;
2330
+ JSONDecoder *decoder = NULL;
2331
+
2332
+ CFIndex stringLength = CFStringGetLength((CFStringRef)jsonString);
2333
+ NSUInteger stringUTF8Length = [jsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
2334
+
2335
+ if((mutableData = (CFMutableDataRef)[(id)CFDataCreateMutable(NULL, (NSUInteger)stringUTF8Length) autorelease]) != NULL) {
2336
+ UInt8 *utf8String = CFDataGetMutableBytePtr(mutableData);
2337
+ CFIndex usedBytes = 0L, convertedCount = 0L;
2338
+
2339
+ convertedCount = CFStringGetBytes((CFStringRef)jsonString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', NO, utf8String, (NSUInteger)stringUTF8Length, &usedBytes);
2340
+ 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; }
2341
+
2342
+ if(mutableCollection == NO) { returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) objectWithUTF8String:(const unsigned char *)utf8String length:(size_t)usedBytes error:error]; }
2343
+ else { returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) mutableObjectWithUTF8String:(const unsigned char *)utf8String length:(size_t)usedBytes error:error]; }
2344
+ }
2345
+
2346
+ exitNow:
2347
+ if(mutableData != NULL) { CFDataSetLength(mutableData, 0L); }
2348
+ if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
2349
+ return(returnObject);
2350
+ }
2351
+
2352
+ - (id)objectFromJSONString
2353
+ {
2354
+ return([self objectFromJSONStringWithParseOptions:JKParseOptionStrict error:NULL]);
2355
+ }
2356
+
2357
+ - (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2358
+ {
2359
+ return([self objectFromJSONStringWithParseOptions:parseOptionFlags error:NULL]);
2360
+ }
2361
+
2362
+ - (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
2363
+ {
2364
+ return(_NSStringObjectFromJSONString(self, parseOptionFlags, error, NO));
2365
+ }
2366
+
2367
+
2368
+ - (id)mutableObjectFromJSONString
2369
+ {
2370
+ return([self mutableObjectFromJSONStringWithParseOptions:JKParseOptionStrict error:NULL]);
2371
+ }
2372
+
2373
+ - (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2374
+ {
2375
+ return([self mutableObjectFromJSONStringWithParseOptions:parseOptionFlags error:NULL]);
2376
+ }
2377
+
2378
+ - (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
2379
+ {
2380
+ return(_NSStringObjectFromJSONString(self, parseOptionFlags, error, YES));
2381
+ }
2382
+
2383
+ @end
2384
+
2385
+ @implementation NSData (JSONKitDeserializing)
2386
+
2387
+ - (id)objectFromJSONData
2388
+ {
2389
+ return([self objectFromJSONDataWithParseOptions:JKParseOptionStrict error:NULL]);
2390
+ }
2391
+
2392
+ - (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2393
+ {
2394
+ return([self objectFromJSONDataWithParseOptions:parseOptionFlags error:NULL]);
2395
+ }
2396
+
2397
+ - (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
2398
+ {
2399
+ JSONDecoder *decoder = NULL;
2400
+ id returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) objectWithData:self error:error];
2401
+ if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
2402
+ return(returnObject);
2403
+ }
2404
+
2405
+ - (id)mutableObjectFromJSONData
2406
+ {
2407
+ return([self mutableObjectFromJSONDataWithParseOptions:JKParseOptionStrict error:NULL]);
2408
+ }
2409
+
2410
+ - (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags
2411
+ {
2412
+ return([self mutableObjectFromJSONDataWithParseOptions:parseOptionFlags error:NULL]);
2413
+ }
2414
+
2415
+ - (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
2416
+ {
2417
+ JSONDecoder *decoder = NULL;
2418
+ id returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) mutableObjectWithData:self error:error];
2419
+ if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
2420
+ return(returnObject);
2421
+ }
2422
+
2423
+
2424
+ @end
2425
+
2426
+ ////////////
2427
+ #pragma mark -
2428
+ #pragma mark Encoding / deserializing functions
2429
+
2430
+ static void jk_encode_error(JKEncodeState *encodeState, NSString *format, ...) {
2431
+ NSCParameterAssert((encodeState != NULL) && (format != NULL));
2432
+
2433
+ va_list varArgsList;
2434
+ va_start(varArgsList, format);
2435
+ NSString *formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease];
2436
+ va_end(varArgsList);
2437
+
2438
+ if(encodeState->error == NULL) {
2439
+ encodeState->error = [NSError errorWithDomain:@"JKErrorDomain" code:-1L userInfo:
2440
+ [NSDictionary dictionaryWithObjectsAndKeys:
2441
+ formatString, NSLocalizedDescriptionKey,
2442
+ NULL]];
2443
+ }
2444
+ }
2445
+
2446
+ JK_STATIC_INLINE void jk_encode_updateCache(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object) {
2447
+ NSCParameterAssert(encodeState != NULL);
2448
+ if(JK_EXPECT_T(cacheSlot != NULL)) {
2449
+ NSCParameterAssert((object != NULL) && (startingAtIndex <= encodeState->atIndex));
2450
+ cacheSlot->object = object;
2451
+ cacheSlot->offset = startingAtIndex;
2452
+ cacheSlot->length = (size_t)(encodeState->atIndex - startingAtIndex);
2453
+ }
2454
+ }
2455
+
2456
+ static int jk_encode_printf(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, ...) {
2457
+ va_list varArgsList, varArgsListCopy;
2458
+ va_start(varArgsList, format);
2459
+ va_copy(varArgsListCopy, varArgsList);
2460
+
2461
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex) && (format != NULL));
2462
+
2463
+ ssize_t formattedStringLength = 0L;
2464
+ int returnValue = 0;
2465
+
2466
+ 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))) {
2467
+ NSCParameterAssert(((encodeState->atIndex + (formattedStringLength * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length));
2468
+ 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; }
2469
+ 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; }
2470
+ }
2471
+
2472
+ exitNow:
2473
+ va_end(varArgsList);
2474
+ va_end(varArgsListCopy);
2475
+ if(JK_EXPECT_T(returnValue == 0)) { encodeState->atIndex += formattedStringLength; jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object); }
2476
+ return(returnValue);
2477
+ }
2478
+
2479
+ static int jk_encode_write(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format) {
2480
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex) && (format != NULL));
2481
+ 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); }
2482
+
2483
+ size_t formatIdx = 0UL;
2484
+ for(formatIdx = 0UL; format[formatIdx] != 0; formatIdx++) { NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length); encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[formatIdx]; }
2485
+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object);
2486
+ return(0);
2487
+ }
2488
+
2489
+ static int jk_encode_writePrettyPrintWhiteSpace(JKEncodeState *encodeState) {
2490
+ NSCParameterAssert((encodeState != NULL) && ((encodeState->serializeOptionFlags & JKSerializeOptionPretty) != 0UL));
2491
+ 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); }
2492
+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\n';
2493
+ size_t depthWhiteSpace = 0UL;
2494
+ for(depthWhiteSpace = 0UL; depthWhiteSpace < (encodeState->depth * 2UL); depthWhiteSpace++) { NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length); encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = ' '; }
2495
+ return(0);
2496
+ }
2497
+
2498
+ static int jk_encode_write1slow(JKEncodeState *encodeState, ssize_t depthChange, const char *format) {
2499
+ 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));
2500
+ 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); }
2501
+ encodeState->depth += depthChange;
2502
+ if(JK_EXPECT_T(format[0] == ':')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = ' '; }
2503
+ else {
2504
+ if(JK_EXPECT_F(depthChange == -1L)) { if(JK_EXPECT_F(jk_encode_writePrettyPrintWhiteSpace(encodeState))) { return(1); } }
2505
+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0];
2506
+ if(JK_EXPECT_T(depthChange != -1L)) { if(JK_EXPECT_F(jk_encode_writePrettyPrintWhiteSpace(encodeState))) { return(1); } }
2507
+ }
2508
+ NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
2509
+ return(0);
2510
+ }
2511
+
2512
+ static int jk_encode_write1fast(JKEncodeState *encodeState, ssize_t depthChange JK_UNUSED_ARG, const char *format) {
2513
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && ((encodeState->serializeOptionFlags & JKSerializeOptionPretty) == 0UL));
2514
+ if(JK_EXPECT_T((encodeState->atIndex + 4UL) < encodeState->stringBuffer.bytes.length)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; }
2515
+ else { return(jk_encode_write(encodeState, NULL, 0UL, NULL, format)); }
2516
+ return(0);
2517
+ }
2518
+
2519
+ static int jk_encode_writen(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, size_t length) {
2520
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex));
2521
+ 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); } }
2522
+ memcpy(encodeState->stringBuffer.bytes.ptr + encodeState->atIndex, format, length);
2523
+ encodeState->atIndex += length;
2524
+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object);
2525
+ return(0);
2526
+ }
2527
+
2528
+ JK_STATIC_INLINE JKHash jk_encode_object_hash(void *objectPtr) {
2529
+ return( ( (((JKHash)objectPtr) >> 21) ^ (((JKHash)objectPtr) >> 9) ) + (((JKHash)objectPtr) >> 4) );
2530
+ }
2531
+
2532
+ static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *objectPtr) {
2533
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (objectPtr != NULL));
2534
+
2535
+ id object = (id)objectPtr, encodeCacheObject = object;
2536
+ int isClass = JKClassUnknown;
2537
+ size_t startingAtIndex = encodeState->atIndex;
2538
+
2539
+ JKHash objectHash = jk_encode_object_hash(objectPtr);
2540
+ JKEncodeCache *cacheSlot = &encodeState->cache[objectHash % JK_ENCODE_CACHE_SLOTS];
2541
+
2542
+ if(JK_EXPECT_T(cacheSlot->object == object)) {
2543
+ NSCParameterAssert((cacheSlot->object != NULL) &&
2544
+ (cacheSlot->offset < encodeState->atIndex) && ((cacheSlot->offset + cacheSlot->length) < encodeState->atIndex) &&
2545
+ (cacheSlot->offset < encodeState->stringBuffer.bytes.length) && ((cacheSlot->offset + cacheSlot->length) < encodeState->stringBuffer.bytes.length) &&
2546
+ ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2547
+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2548
+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)));
2549
+ 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); }
2550
+ NSCParameterAssert(((encodeState->atIndex + cacheSlot->length) < encodeState->stringBuffer.bytes.length) &&
2551
+ ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2552
+ ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2553
+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2554
+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
2555
+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->atIndex)));
2556
+ memcpy(encodeState->stringBuffer.bytes.ptr + encodeState->atIndex, encodeState->stringBuffer.bytes.ptr + cacheSlot->offset, cacheSlot->length);
2557
+ encodeState->atIndex += cacheSlot->length;
2558
+ return(0);
2559
+ }
2560
+
2561
+ // 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,
2562
+ // 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
2563
+ // supported classes, we fail the second time (i.e., double fault error).
2564
+ BOOL rerunningAfterClassFormatter = NO;
2565
+ rerunAfterClassFormatter:;
2566
+
2567
+ // XXX XXX XXX XXX
2568
+ //
2569
+ // 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.
2570
+ //
2571
+ // 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".
2572
+ //
2573
+ // This is euphemistically called "tagged pointers". There are a number of highly technical problems with this, most involving long passages from
2574
+ // 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
2575
+ // fundamentally Wrong and Broken, and should have never been implemented. Assuming those points are glossed over, because the change is very clearly
2576
+ // breaking ABI compatibility, this should have resulted in a minimum of a "minimum version required" bump in various shared libraries to prevent
2577
+ // causes code that used to work just fine to suddenly break without warning.
2578
+ //
2579
+ // 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
2580
+ // "lower, unused bits" must be preserved when casting the result to an integer type, but this "works" because for most architectures
2581
+ // `sizeof(long) == sizeof(void *)` and the compiler uses the same representation for both. (note: this is informal, not meant to be
2582
+ // normative or pedantically correct).
2583
+ //
2584
+ // 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
2585
+ // is not required in any way to produce the same results or behavior that earlier versions of the compiler did for the statement below.
2586
+ //
2587
+ // Fan-fucking-tastic.
2588
+ //
2589
+ // Why not just use `object_getClass()`? Because `object->isa` reduces to (typically) a *single* instruction. Calling `object_getClass()` requires
2590
+ // that the compiler potentially spill registers, establish a function call frame / environment, and finally execute a "jump subroutine" instruction.
2591
+ // Then, the called subroutine must spend half a dozen instructions in its prolog, however many instructions doing whatever it does, then half a dozen
2592
+ // instructions in its prolog. One instruction compared to dozens, maybe a hundred instructions.
2593
+ //
2594
+ // 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
2595
+ // gains in the two to three percent range.
2596
+ //
2597
+ // XXX XXX XXX XXX
2598
+
2599
+
2600
+ BOOL workAroundMacOSXABIBreakingBug = (JK_EXPECT_F(((NSUInteger)object) & 0x1)) ? YES : NO;
2601
+ void *objectISA = (JK_EXPECT_F(workAroundMacOSXABIBreakingBug)) ? NULL : *((void **)objectPtr);
2602
+ if(JK_EXPECT_F(workAroundMacOSXABIBreakingBug)) { goto slowClassLookup; }
2603
+
2604
+ if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.stringClass)) { isClass = JKClassString; }
2605
+ else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.numberClass)) { isClass = JKClassNumber; }
2606
+ else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.dictionaryClass)) { isClass = JKClassDictionary; }
2607
+ else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.arrayClass)) { isClass = JKClassArray; }
2608
+ else if(JK_EXPECT_T(objectISA == encodeState->fastClassLookup.nullClass)) { isClass = JKClassNull; }
2609
+ else {
2610
+ slowClassLookup:
2611
+ if(JK_EXPECT_T([object isKindOfClass:[NSString class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.stringClass = objectISA; } isClass = JKClassString; }
2612
+ else if(JK_EXPECT_T([object isKindOfClass:[NSNumber class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.numberClass = objectISA; } isClass = JKClassNumber; }
2613
+ else if(JK_EXPECT_T([object isKindOfClass:[NSDictionary class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.dictionaryClass = objectISA; } isClass = JKClassDictionary; }
2614
+ else if(JK_EXPECT_T([object isKindOfClass:[NSArray class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.arrayClass = objectISA; } isClass = JKClassArray; }
2615
+ else if(JK_EXPECT_T([object isKindOfClass:[NSNull class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.nullClass = objectISA; } isClass = JKClassNull; }
2616
+ else {
2617
+ if((rerunningAfterClassFormatter == NO) && (
2618
+ #ifdef __BLOCKS__
2619
+ ((encodeState->classFormatterBlock) && ((object = encodeState->classFormatterBlock(object)) != NULL)) ||
2620
+ #endif
2621
+ ((encodeState->classFormatterIMP) && ((object = encodeState->classFormatterIMP(encodeState->classFormatterDelegate, encodeState->classFormatterSelector, object)) != NULL)) )) { rerunningAfterClassFormatter = YES; goto rerunAfterClassFormatter; }
2622
+
2623
+ if(rerunningAfterClassFormatter == NO) { jk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([encodeCacheObject class])); return(1); }
2624
+ 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); }
2625
+ }
2626
+ }
2627
+
2628
+ // This is here for the benefit of the optimizer. It allows the optimizer to do loop invariant code motion for the JKClassArray
2629
+ // and JKClassDictionary cases when printing simple, single characters via jk_encode_write(), which is actually a macro:
2630
+ // #define jk_encode_write1(es, dc, f) (_jk_encode_prettyPrint ? jk_encode_write1slow(es, dc, f) : jk_encode_write1fast(es, dc, f))
2631
+ int _jk_encode_prettyPrint = JK_EXPECT_T((encodeState->serializeOptionFlags & JKSerializeOptionPretty) == 0) ? 0 : 1;
2632
+
2633
+ switch(isClass) {
2634
+ case JKClassString:
2635
+ {
2636
+ {
2637
+ const unsigned char *cStringPtr = (const unsigned char *)CFStringGetCStringPtr((CFStringRef)object, kCFStringEncodingMacRoman);
2638
+ if(cStringPtr != NULL) {
2639
+ const unsigned char *utf8String = cStringPtr;
2640
+ size_t utf8Idx = 0UL;
2641
+
2642
+ CFIndex stringLength = CFStringGetLength((CFStringRef)object);
2643
+ 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); }
2644
+
2645
+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
2646
+ for(utf8Idx = 0UL; utf8String[utf8Idx] != 0U; utf8Idx++) {
2647
+ NSCParameterAssert(((&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex]) - encodeState->stringBuffer.bytes.ptr) < (ssize_t)encodeState->stringBuffer.bytes.length);
2648
+ NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
2649
+ if(JK_EXPECT_F(utf8String[utf8Idx] >= 0x80U)) { encodeState->atIndex = startingAtIndex; goto slowUTF8Path; }
2650
+ if(JK_EXPECT_F(utf8String[utf8Idx] < 0x20U)) {
2651
+ switch(utf8String[utf8Idx]) {
2652
+ case '\b': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'b'; break;
2653
+ case '\f': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'f'; break;
2654
+ case '\n': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'n'; break;
2655
+ case '\r': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'r'; break;
2656
+ case '\t': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 't'; break;
2657
+ default: if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break;
2658
+ }
2659
+ } else {
2660
+ 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++] = '\\'; }
2661
+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx];
2662
+ }
2663
+ }
2664
+ NSCParameterAssert((encodeState->atIndex + 1UL) < encodeState->stringBuffer.bytes.length);
2665
+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
2666
+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, encodeCacheObject);
2667
+ return(0);
2668
+ }
2669
+ }
2670
+
2671
+ slowUTF8Path:
2672
+ {
2673
+ CFIndex stringLength = CFStringGetLength((CFStringRef)object);
2674
+ CFIndex maxStringUTF8Length = CFStringGetMaximumSizeForEncoding(stringLength, kCFStringEncodingUTF8) + 32L;
2675
+
2676
+ 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); }
2677
+
2678
+ CFIndex usedBytes = 0L, convertedCount = 0L;
2679
+ convertedCount = CFStringGetBytes((CFStringRef)object, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', NO, encodeState->utf8ConversionBuffer.bytes.ptr, encodeState->utf8ConversionBuffer.bytes.length - 16L, &usedBytes);
2680
+ 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); }
2681
+
2682
+ 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); }
2683
+
2684
+ const unsigned char *utf8String = encodeState->utf8ConversionBuffer.bytes.ptr;
2685
+
2686
+ size_t utf8Idx = 0UL;
2687
+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
2688
+ for(utf8Idx = 0UL; utf8Idx < (size_t)usedBytes; utf8Idx++) {
2689
+ NSCParameterAssert(((&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex]) - encodeState->stringBuffer.bytes.ptr) < (ssize_t)encodeState->stringBuffer.bytes.length);
2690
+ NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
2691
+ NSCParameterAssert((CFIndex)utf8Idx < usedBytes);
2692
+ if(JK_EXPECT_F(utf8String[utf8Idx] < 0x20U)) {
2693
+ switch(utf8String[utf8Idx]) {
2694
+ case '\b': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'b'; break;
2695
+ case '\f': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'f'; break;
2696
+ case '\n': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'n'; break;
2697
+ case '\r': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'r'; break;
2698
+ case '\t': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 't'; break;
2699
+ default: if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break;
2700
+ }
2701
+ } else {
2702
+ if(JK_EXPECT_F(utf8String[utf8Idx] >= 0x80U) && (encodeState->serializeOptionFlags & JKSerializeOptionEscapeUnicode)) {
2703
+ const unsigned char *nextValidCharacter = NULL;
2704
+ UTF32 u32ch = 0U;
2705
+ ConversionResult result;
2706
+
2707
+ if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(&utf8String[utf8Idx], &utf8String[usedBytes], (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { jk_encode_error(encodeState, @"Error converting UTF8."); return(1); }
2708
+ else {
2709
+ utf8Idx = (nextValidCharacter - utf8String) - 1UL;
2710
+ if(JK_EXPECT_T(u32ch <= 0xffffU)) { if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", u32ch))) { return(1); } }
2711
+ 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); } }
2712
+ }
2713
+ } else {
2714
+ 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++] = '\\'; }
2715
+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx];
2716
+ }
2717
+ }
2718
+ }
2719
+ NSCParameterAssert((encodeState->atIndex + 1UL) < encodeState->stringBuffer.bytes.length);
2720
+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
2721
+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, encodeCacheObject);
2722
+ return(0);
2723
+ }
2724
+ }
2725
+ break;
2726
+
2727
+ case JKClassNumber:
2728
+ {
2729
+ if(object == (id)kCFBooleanTrue) { return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "true", 4UL)); }
2730
+ else if(object == (id)kCFBooleanFalse) { return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "false", 5UL)); }
2731
+
2732
+ const char *objCType = [object objCType];
2733
+ char anum[256], *aptr = &anum[255];
2734
+ int isNegative = 0;
2735
+ unsigned long long ullv;
2736
+ long long llv;
2737
+
2738
+ 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); }
2739
+
2740
+ switch(objCType[0]) {
2741
+ case 'c': case 'i': case 's': case 'l': case 'q':
2742
+ if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberLongLongType, &llv))) {
2743
+ if(llv < 0LL) { ullv = -llv; isNegative = 1; } else { ullv = llv; isNegative = 0; }
2744
+ goto convertNumber;
2745
+ } else { jk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); }
2746
+ break;
2747
+ case 'C': case 'I': case 'S': case 'L': case 'Q': case 'B':
2748
+ if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberLongLongType, &ullv))) {
2749
+ convertNumber:
2750
+ 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); } }
2751
+ if(isNegative) { *--aptr = '-'; }
2752
+ NSCParameterAssert(aptr > anum);
2753
+ return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, aptr, &anum[255] - aptr));
2754
+ } else { jk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); }
2755
+ break;
2756
+ case 'f': case 'd':
2757
+ {
2758
+ double dv;
2759
+ if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberDoubleType, &dv))) {
2760
+ 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); }
2761
+ return(jk_encode_printf(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "%.17g", dv));
2762
+ } else { jk_encode_error(encodeState, @"Unable to get floating point value from number object."); return(1); }
2763
+ }
2764
+ break;
2765
+ default: jk_encode_error(encodeState, @"NSNumber conversion error, unknown type. Type: '%c' / 0x%2.2x", objCType[0], objCType[0]); return(1); break;
2766
+ }
2767
+ }
2768
+ break;
2769
+
2770
+ case JKClassArray:
2771
+ {
2772
+ int printComma = 0;
2773
+ CFIndex arrayCount = CFArrayGetCount((CFArrayRef)object), idx = 0L;
2774
+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 1L, "["))) { return(1); }
2775
+ if(JK_EXPECT_F(arrayCount > 1020L)) {
2776
+ 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); } }
2777
+ } else {
2778
+ void *objects[1024];
2779
+ CFArrayGetValues((CFArrayRef)object, CFRangeMake(0L, arrayCount), (const void **)objects);
2780
+ 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); } }
2781
+ }
2782
+ return(jk_encode_write1(encodeState, -1L, "]"));
2783
+ }
2784
+ break;
2785
+
2786
+ case JKClassDictionary:
2787
+ {
2788
+ int printComma = 0;
2789
+ CFIndex dictionaryCount = CFDictionaryGetCount((CFDictionaryRef)object), idx = 0L;
2790
+ id enumerateObject = JK_EXPECT_F(_jk_encode_prettyPrint) ? [[object allKeys] sortedArrayUsingSelector:@selector(compare:)] : object;
2791
+
2792
+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 1L, "{"))) { return(1); }
2793
+ if(JK_EXPECT_F(_jk_encode_prettyPrint) || JK_EXPECT_F(dictionaryCount > 1020L)) {
2794
+ for(id keyObject in enumerateObject) {
2795
+ if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } }
2796
+ printComma = 1;
2797
+ void *keyObjectISA = *((void **)keyObject);
2798
+ 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); }
2799
+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, keyObject))) { return(1); }
2800
+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ":"))) { return(1); }
2801
+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, (void *)CFDictionaryGetValue((CFDictionaryRef)object, keyObject)))) { return(1); }
2802
+ }
2803
+ } else {
2804
+ void *keys[1024], *objects[1024];
2805
+ CFDictionaryGetKeysAndValues((CFDictionaryRef)object, (const void **)keys, (const void **)objects);
2806
+ for(idx = 0L; idx < dictionaryCount; idx++) {
2807
+ if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } }
2808
+ printComma = 1;
2809
+ void *keyObjectISA = *((void **)keys[idx]);
2810
+ 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); }
2811
+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, keys[idx]))) { return(1); }
2812
+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ":"))) { return(1); }
2813
+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, objects[idx]))) { return(1); }
2814
+ }
2815
+ }
2816
+ return(jk_encode_write1(encodeState, -1L, "}"));
2817
+ }
2818
+ break;
2819
+
2820
+ case JKClassNull: return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "null", 4UL)); break;
2821
+
2822
+ default: jk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([object class])); return(1); break;
2823
+ }
2824
+
2825
+ return(0);
2826
+ }
2827
+
2828
+
2829
+ @implementation JKSerializer
2830
+
2831
+ + (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error
2832
+ {
2833
+ return([[[[self alloc] init] autorelease] serializeObject:object options:optionFlags encodeOption:encodeOption block:block delegate:delegate selector:selector error:error]);
2834
+ }
2835
+
2836
+ - (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error
2837
+ {
2838
+ #ifndef __BLOCKS__
2839
+ #pragma unused(block)
2840
+ #endif
2841
+ NSParameterAssert((object != NULL) && (encodeState == NULL) && ((delegate != NULL) ? (block == NULL) : 1) && ((block != NULL) ? (delegate == NULL) : 1) &&
2842
+ (((encodeOption & JKEncodeOptionCollectionObj) != 0UL) ? (((encodeOption & JKEncodeOptionStringObj) == 0UL) && ((encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) : 1) &&
2843
+ (((encodeOption & JKEncodeOptionStringObj) != 0UL) ? ((encodeOption & JKEncodeOptionCollectionObj) == 0UL) : 1));
2844
+
2845
+ id returnObject = NULL;
2846
+
2847
+ if(encodeState != NULL) { [self releaseState]; }
2848
+ if((encodeState = (struct JKEncodeState *)calloc(1UL, sizeof(JKEncodeState))) == NULL) { [NSException raise:NSMallocException format:@"Unable to allocate state structure."]; return(NULL); }
2849
+
2850
+ if((error != NULL) && (*error != NULL)) { *error = NULL; }
2851
+
2852
+ if(delegate != NULL) {
2853
+ if(selector == NULL) { [NSException raise:NSInvalidArgumentException format:@"The delegate argument is not NULL, but the selector argument is NULL."]; }
2854
+ if([delegate respondsToSelector:selector] == NO) { [NSException raise:NSInvalidArgumentException format:@"The serializeUnsupportedClassesUsingDelegate: delegate does not respond to the selector argument."]; }
2855
+ encodeState->classFormatterDelegate = delegate;
2856
+ encodeState->classFormatterSelector = selector;
2857
+ encodeState->classFormatterIMP = (JKClassFormatterIMP)[delegate methodForSelector:selector];
2858
+ NSCParameterAssert(encodeState->classFormatterIMP != NULL);
2859
+ }
2860
+
2861
+ #ifdef __BLOCKS__
2862
+ encodeState->classFormatterBlock = block;
2863
+ #endif
2864
+ encodeState->serializeOptionFlags = optionFlags;
2865
+ encodeState->encodeOption = encodeOption;
2866
+ encodeState->stringBuffer.roundSizeUpToMultipleOf = (1024UL * 32UL);
2867
+ encodeState->utf8ConversionBuffer.roundSizeUpToMultipleOf = 4096UL;
2868
+
2869
+ unsigned char stackJSONBuffer[JK_JSONBUFFER_SIZE] JK_ALIGNED(64);
2870
+ jk_managedBuffer_setToStackBuffer(&encodeState->stringBuffer, stackJSONBuffer, sizeof(stackJSONBuffer));
2871
+
2872
+ unsigned char stackUTF8Buffer[JK_UTF8BUFFER_SIZE] JK_ALIGNED(64);
2873
+ jk_managedBuffer_setToStackBuffer(&encodeState->utf8ConversionBuffer, stackUTF8Buffer, sizeof(stackUTF8Buffer));
2874
+
2875
+ 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; }
2876
+ 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; }
2877
+
2878
+ if(jk_encode_add_atom_to_buffer(encodeState, object) == 0) {
2879
+ BOOL stackBuffer = ((encodeState->stringBuffer.flags & JKManagedBufferMustFree) == 0UL) ? YES : NO;
2880
+
2881
+ if((encodeState->atIndex < 2UL))
2882
+ 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; }
2883
+
2884
+ switch((encodeOption & JKEncodeOptionAsTypeMask)) {
2885
+ case JKEncodeOptionAsData:
2886
+ 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"); } }
2887
+ 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"); } }
2888
+ break;
2889
+
2890
+ case JKEncodeOptionAsString:
2891
+ 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"); } }
2892
+ 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"); } }
2893
+ break;
2894
+
2895
+ default: jk_encode_error(encodeState, @"Unknown encode as type."); break;
2896
+ }
2897
+
2898
+ if((returnObject != NULL) && (stackBuffer == NO)) { encodeState->stringBuffer.flags &= ~JKManagedBufferMustFree; encodeState->stringBuffer.bytes.ptr = NULL; encodeState->stringBuffer.bytes.length = 0UL; }
2899
+ }
2900
+
2901
+ errorExit:
2902
+ if((encodeState != NULL) && (error != NULL) && (encodeState->error != NULL)) { *error = encodeState->error; encodeState->error = NULL; }
2903
+ [self releaseState];
2904
+
2905
+ return(returnObject);
2906
+ }
2907
+
2908
+ - (void)releaseState
2909
+ {
2910
+ if(encodeState != NULL) {
2911
+ jk_managedBuffer_release(&encodeState->stringBuffer);
2912
+ jk_managedBuffer_release(&encodeState->utf8ConversionBuffer);
2913
+ free(encodeState); encodeState = NULL;
2914
+ }
2915
+ }
2916
+
2917
+ - (void)dealloc
2918
+ {
2919
+ [self releaseState];
2920
+ [super dealloc];
2921
+ }
2922
+
2923
+ @end
2924
+
2925
+ @implementation NSString (JSONKitSerializing)
2926
+
2927
+ ////////////
2928
+ #pragma mark Methods for serializing a single NSString.
2929
+ ////////////
2930
+
2931
+ // 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.
2932
+
2933
+ // NSData returning methods...
2934
+
2935
+ - (NSData *)JSONData
2936
+ {
2937
+ return([self JSONDataWithOptions:JKSerializeOptionNone includeQuotes:YES error:NULL]);
2938
+ }
2939
+
2940
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error
2941
+ {
2942
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | ((includeQuotes == NO) ? JKEncodeOptionStringObjTrimQuotes : 0UL) | JKEncodeOptionStringObj) block:NULL delegate:NULL selector:NULL error:error]);
2943
+ }
2944
+
2945
+ // NSString returning methods...
2946
+
2947
+ - (NSString *)JSONString
2948
+ {
2949
+ return([self JSONStringWithOptions:JKSerializeOptionNone includeQuotes:YES error:NULL]);
2950
+ }
2951
+
2952
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error
2953
+ {
2954
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | ((includeQuotes == NO) ? JKEncodeOptionStringObjTrimQuotes : 0UL) | JKEncodeOptionStringObj) block:NULL delegate:NULL selector:NULL error:error]);
2955
+ }
2956
+
2957
+ @end
2958
+
2959
+ @implementation NSArray (JSONKitSerializing)
2960
+
2961
+ // NSData returning methods...
2962
+
2963
+ - (NSData *)JSONData
2964
+ {
2965
+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
2966
+ }
2967
+
2968
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
2969
+ {
2970
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
2971
+ }
2972
+
2973
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
2974
+ {
2975
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
2976
+ }
2977
+
2978
+ // NSString returning methods...
2979
+
2980
+ - (NSString *)JSONString
2981
+ {
2982
+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
2983
+ }
2984
+
2985
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
2986
+ {
2987
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
2988
+ }
2989
+
2990
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
2991
+ {
2992
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
2993
+ }
2994
+
2995
+ @end
2996
+
2997
+ @implementation NSDictionary (JSONKitSerializing)
2998
+
2999
+ // NSData returning methods...
3000
+
3001
+ - (NSData *)JSONData
3002
+ {
3003
+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
3004
+ }
3005
+
3006
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
3007
+ {
3008
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
3009
+ }
3010
+
3011
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
3012
+ {
3013
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
3014
+ }
3015
+
3016
+ // NSString returning methods...
3017
+
3018
+ - (NSString *)JSONString
3019
+ {
3020
+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
3021
+ }
3022
+
3023
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
3024
+ {
3025
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
3026
+ }
3027
+
3028
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
3029
+ {
3030
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
3031
+ }
3032
+
3033
+ @end
3034
+
3035
+
3036
+ #ifdef __BLOCKS__
3037
+
3038
+ @implementation NSArray (JSONKitSerializingBlockAdditions)
3039
+
3040
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
3041
+ {
3042
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
3043
+ }
3044
+
3045
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
3046
+ {
3047
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
3048
+ }
3049
+
3050
+ @end
3051
+
3052
+ @implementation NSDictionary (JSONKitSerializingBlockAdditions)
3053
+
3054
+ - (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
3055
+ {
3056
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
3057
+ }
3058
+
3059
+ - (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
3060
+ {
3061
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
3062
+ }
3063
+
3064
+ @end
3065
+
3066
+ #endif // __BLOCKS__
3067
+