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