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