Skip to content
Browse files

Switch to SBJsonParser 3.2.

This lets us use ARC on SBJsonParser, as well.
  • Loading branch information...
1 parent 92ed45e commit aa08c14c7928414f47b637d474d99e2af47804fc @Shadowfiend Shadowfiend committed
View
10 Makefile
@@ -15,21 +15,20 @@ VPATH = app app/en.lproj json oniguruma oniguruma/enc universalchardet lemon \
NO_ARC_OBJC_SRCS = \
NSObject+SPInvocationGrabbing.m \
- Nu.m \
- $(JSON_OBJC_SRCS)
+ Nu.m
JSON_OBJC_SRCS = \
- NSObject+JSON.m \
SBJsonParser.m \
SBJsonStreamParser.m \
SBJsonStreamParserAdapter.m \
SBJsonStreamParserAccumulator.m \
- SBJsonStreamParserTokeniser.m \
- SBJsonStreamParserWriterAccumulator.m \
+ SBJsonStreamParserAdapter.m \
SBJsonStreamParserState.m \
SBJsonStreamWriter.m \
+ SBJsonStreamWriterAccumulator.m \
SBJsonStreamWriterState.m \
SBJsonTokeniser.m \
+ SBJsonUTF8Stream.m \
SBJsonWriter.m
OBJC_SRCS = \
@@ -145,6 +144,7 @@ OBJC_SRCS = \
ViWindow.m \
ViWindowController.m \
ViWordCompletion.m \
+ $(JSON_OBJC_SRCS) \
main.m
OBJCXX_SRCS = \
View
6 app/ViAppController.m
@@ -37,7 +37,7 @@
#import "ViPreferencePaneAdvanced.h"
#import "TMFileURLProtocol.h"
#import "TxmtURLProtocol.h"
-#import "JSON.h"
+#import "SBJson.h"
#import "ViError.h"
#import "ViCommandMenuItemView.h"
#import "ViEventManager.h"
@@ -649,7 +649,9 @@ - (NSString *)eval:(NSString *)script
if ([result isKindOfClass:[NSNull class]])
return nil;
- return [result JSONRepresentation];
+
+ SBJsonWriter *writer = [[SBJsonWriter alloc] init];
+ return [writer stringWithObject:result];
}
- (NSError *)openURL:(NSString *)pathOrURL andWait:(BOOL)waitFlag backChannel:(NSString *)channelName
View
21 app/ViPreferencePaneBundles.m
@@ -25,7 +25,7 @@
#import "ViPreferencePaneBundles.h"
#import "ViBundleStore.h"
-#import "JSON.h"
+#import "SBJson.h"
#include "logging.h"
@implementation repoUserTransformer
@@ -167,15 +167,20 @@ - (void)loadBundlesFromRepo:(NSString *)username
return;
NSData *jsonData = [NSData dataWithContentsOfFile:path];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
- NSArray *arry = [jsonString JSONValue];
+
+ SBJsonParser *parser = [[SBJsonParser alloc] init];
+ NSArray *arry = [parser objectWithString:jsonString];
if (![arry isKindOfClass:[NSArray class]]) {
- INFO(@"%s", "failed to parse JSON");
+ INFO(@"%s: %@", "failed to parse JSON, error was ", parser.error);
return;
}
[_repositories addObjectsFromArray: arry];
} else {
NSString *path = [self repoPathForUser:username readonly:NO];
- [[_repoJson JSONRepresentation] writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
+
+ SBJsonWriter *writer = [[SBJsonWriter alloc] init];
+ NSString *json =[writer stringWithObject:_repoJson];
+ [json writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
[_repositories addObjectsFromArray:_repoJson];
}
@@ -457,7 +462,9 @@ - (void)connectionDidFinishLoading:(NSURLConnection *)connection
NSString *jsonString = [[NSString alloc] initWithData:_userData encoding:NSUTF8StringEncoding];
_userData = nil;
- NSDictionary *dict = [jsonString JSONValue];
+
+ SBJsonParser *parser = [[SBJsonParser alloc] init];
+ NSDictionary *dict = [parser objectWithString:jsonString];
if (![dict isKindOfClass:[NSDictionary class]]) {
[self cancelProgressSheet:nil];
[progressDescription setStringValue:[NSString stringWithFormat:@"Failed to parse data for user %@.", username]];
@@ -492,7 +499,9 @@ - (void)connectionDidFinishLoading:(NSURLConnection *)connection
if (connection == _repoConnection) {
NSString *jsonString = [[NSString alloc] initWithData:_repoData encoding:NSUTF8StringEncoding];
- NSArray *repoJson = [jsonString JSONValue];
+
+ SBJsonParser *parser = [[SBJsonParser alloc] init];
+ NSArray *repoJson = [parser objectWithString:jsonString];
[_repoJson addObjectsFromArray:repoJson];
NSDictionary *repoUser = [_processQueue lastObject];
View
69 json/JSON.h
@@ -1,69 +0,0 @@
-/*
- Copyright (C) 2009-2010 Stig Brautaset. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- * Neither the name of the author nor the names of its contributors may be used
- to endorse or promote products derived from this software without specific
- prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- @mainpage A strict JSON parser and generator for Objective-C
-
- JSON (JavaScript Object Notation) is a lightweight data-interchange
- format. This framework provides two apis for parsing and generating
- JSON. One standard object-based and a higher level api consisting of
- categories added to existing Objective-C classes.
-
- This framework does its best to be as strict as possible, both in what it accepts and what it generates. For example, it does not support trailing commas in arrays or objects. Nor does it support embedded comments, or anything else not in the JSON specification. This is considered a feature.
-
- @section Links
-
- @li <a href="http://stig.github.com/json-framework">Project home page</a>.
- @li Online version of the <a href="http://stig.github.com/json-framework/api">API documentation</a>.
-
-*/
-
-
-// This setting of 1 is best if you copy the source into your project.
-// The build transforms the 1 to a 0 when building the framework and static lib.
-
-#if 1
-
-#import "SBJsonParser.h"
-#import "SBJsonWriter.h"
-#import "SBJsonStreamWriter.h"
-#import "SBJsonStreamParser.h"
-#import "SBJsonStreamParserAdapter.h"
-#import "NSObject+JSON.h"
-
-#else
-
-#import <JSON/SBJsonParser.h>
-#import <JSON/SBJsonWriter.h>
-#import <JSON/SBJsonStreamParser.h>
-#import <JSON/SBJsonStreamParserAdapter.h>
-#import <JSON/SBJsonStreamWriter.h>
-#import <JSON/NSObject+JSON.h>
-
-#endif
View
61 json/NSObject+JSON.h
@@ -1,61 +0,0 @@
-/*
- Copyright (C) 2009 Stig Brautaset. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- * Neither the name of the author nor the names of its contributors may be used
- to endorse or promote products derived from this software without specific
- prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#import <Foundation/Foundation.h>
-
-#pragma mark JSON Writing
-
-/// Adds JSON generation to NSArray
-@interface NSArray (NSArray_SBJsonWriting)
-
-/// Returns a string containing the receiver encoded in JSON.
-- (NSString *)JSONRepresentation;
-
-@end
-
-
-/// Adds JSON generation to NSArray
-@interface NSDictionary (NSDictionary_SBJsonWriting)
-
-/// Returns a string containing the receiver encoded in JSON.
-- (NSString *)JSONRepresentation;
-
-@end
-
-#pragma mark JSON Parsing
-
-/// Adds JSON parsing methods to NSString
-@interface NSString (NSString_SBJsonParsing)
-
-/// Returns the NSDictionary or NSArray represented by the receiver's JSON representation, or nil on error
-- (id)JSONValue;
-
-@end
-
-
View
58 json/NSObject+JSON.m
@@ -1,58 +0,0 @@
-/*
- Copyright (C) 2009 Stig Brautaset. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- * Neither the name of the author nor the names of its contributors may be used
- to endorse or promote products derived from this software without specific
- prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#import "NSObject+JSON.h"
-#import "SBJsonWriter.h"
-#import "SBJsonParser.h"
-
-@implementation NSObject (NSObject_SBJsonWriting)
-
-- (NSString *)JSONRepresentation {
- SBJsonWriter *jsonWriter = [SBJsonWriter new];
- NSString *json = [jsonWriter stringWithObject:self];
- if (!json)
- NSLog(@"-JSONRepresentation failed. Error is: %@", jsonWriter.error);
- return json;
-}
-
-@end
-
-
-
-@implementation NSString (NSString_SBJsonParsing)
-
-- (id)JSONValue {
- SBJsonParser *jsonParser = [SBJsonParser new];
- id repr = [jsonParser objectWithString:self];
- if (!repr)
- NSLog(@"-JSONValue failed. Error is: %@", jsonParser.error);
- return repr;
-}
-
-@end
View
2 json/SBJson.h
@@ -32,5 +32,3 @@
#import "SBJsonStreamParser.h"
#import "SBJsonStreamParserAdapter.h"
#import "SBJsonStreamWriter.h"
-#import "SBJsonStreamTokeniser.h"
-
View
24 json/SBJsonParser.h
@@ -71,15 +71,31 @@
- (id)objectWithData:(NSData*)data;
/**
- Parse string and return the represented dictionary or array.
+ Return the object represented by the given string
- Calls objectWithData: internally.
+ This method converts its input to an NSData object containing UTF8 and calls -objectWithData: with it.
- @param string An NSString containing JSON text.
+ @return The NSArray or NSDictionary represented by the object, or nil if an error occured.
+ */
+- (id)objectWithString:(NSString *)repr;
+
+/**
+ Return the object represented by the given string
+
+ This method calls objectWithString: internally. If an error occurs, and if error
+ is not nil, it creates an NSError object and returns this through its second argument.
+
+ @param jsonText the json string to parse
+ @param error pointer to an NSError object to populate on error
@return The NSArray or NSDictionary represented by the object, or nil if an error occured.
+
+ @warning Deprecated in Version 3.2; will be removed in 4.0
+
*/
-- (id)objectWithString:(NSString *)string;
+
+- (id)objectWithString:(NSString*)jsonText
+ error:(NSError**)error __attribute__ ((deprecated));
@end
View
23 json/SBJsonParser.m
@@ -27,9 +27,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-//#if !__has_feature(objc_arc)
-//#error "This source file must be compiled with ARC enabled!"
-//#endif
+#if !__has_feature(objc_arc)
+#error "This source file must be compiled with ARC enabled!"
+#endif
#import "SBJsonParser.h"
#import "SBJsonStreamParser.h"
@@ -84,8 +84,21 @@ - (id)objectWithData:(NSData *)data {
return nil;
}
-- (id)objectWithString:(NSString *)string {
- return [self objectWithData:[string dataUsingEncoding:NSUTF8StringEncoding]];
+- (id)objectWithString:(NSString *)repr {
+ return [self objectWithData:[repr dataUsingEncoding:NSUTF8StringEncoding]];
+}
+
+- (id)objectWithString:(NSString*)repr error:(NSError**)error_ {
+ id tmp = [self objectWithString:repr];
+ if (tmp)
+ return tmp;
+
+ if (error_) {
+ NSDictionary *ui = [NSDictionary dictionaryWithObjectsAndKeys:error, NSLocalizedDescriptionKey, nil];
+ *error_ = [NSError errorWithDomain:@"org.brautaset.SBJsonParser.ErrorDomain" code:0 userInfo:ui];
+ }
+
+ return nil;
}
@end
View
19 json/SBJsonStreamParser.h
@@ -32,6 +32,7 @@
#import <Foundation/Foundation.h>
+@class SBJsonTokeniser;
@class SBJsonStreamParser;
@class SBJsonStreamParserState;
@@ -99,7 +100,8 @@ typedef enum {
- object -> NSMutableDictionary
- true -> NSNumber's -numberWithBool:YES
- false -> NSNumber's -numberWithBool:NO
- - number -> NSNumber
+ - integer up to 19 digits -> NSNumber's -numberWithLongLong:
+ - all other numbers -> NSDecimalNumber
Since Objective-C doesn't have a dedicated class for boolean values,
these turns into NSNumber instances. However, since these are
@@ -107,15 +109,18 @@ typedef enum {
properly. In other words, they won't silently suddenly become 0 or 1;
they'll be represented as 'true' and 'false' again.
- Integers are parsed into either a `long long` or `unsigned long long`
- type if they fit, else a `double` is used. All real & exponential numbers
- are represented using a `double`. Previous versions of this library used
- an NSDecimalNumber in some cases, but this is no longer the case.
-
+ As an optimisation integers up to 19 digits in length (the max length
+ for signed long long integers) turn into NSNumber instances, while
+ complex ones turn into NSDecimalNumber instances. We can thus avoid any
+ loss of precision as JSON allows ridiculously large numbers.
+
See also SBJsonStreamParserAdapter for more information.
*/
-@interface SBJsonStreamParser : NSObject
+@interface SBJsonStreamParser : NSObject {
+@private
+ SBJsonTokeniser *tokeniser;
+}
@property (nonatomic, unsafe_unretained) SBJsonStreamParserState *state; // Private
@property (nonatomic, readonly, strong) NSMutableArray *stateStack; // Private
View
161 json/SBJsonStreamParser.m
@@ -30,19 +30,15 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-//#if !__has_feature(objc_arc)
-//#error "This source file must be compiled with ARC enabled!"
-//#endif
+#if !__has_feature(objc_arc)
+#error "This source file must be compiled with ARC enabled!"
+#endif
#import "SBJsonStreamParser.h"
-#import "SBJsonStreamTokeniser.h"
+#import "SBJsonTokeniser.h"
#import "SBJsonStreamParserState.h"
-#define SBStringIsSurrogateHighCharacter(character) ((character >= 0xD800UL) && (character <= 0xDBFFUL))
-
-@implementation SBJsonStreamParser {
- SBJsonStreamTokeniser *tokeniser;
-}
+@implementation SBJsonStreamParser
@synthesize supportMultipleDocuments;
@synthesize error;
@@ -59,7 +55,7 @@ - (id)init {
maxDepth = 32u;
stateStack = [[NSMutableArray alloc] initWithCapacity:maxDepth];
state = [SBJsonStreamParserStateStart sharedInstance];
- tokeniser = [[SBJsonStreamTokeniser alloc] init];
+ tokeniser = [[SBJsonTokeniser alloc] init];
}
return self;
}
@@ -69,25 +65,24 @@ - (id)init {
- (NSString*)tokenName:(sbjson_token_t)token {
switch (token) {
- case sbjson_token_array_open:
+ case sbjson_token_array_start:
return @"start of array";
break;
- case sbjson_token_array_close:
+ case sbjson_token_array_end:
return @"end of array";
break;
- case sbjson_token_integer:
- case sbjson_token_real:
+ case sbjson_token_number:
return @"number";
break;
- case sbjson_token_string:
- case sbjson_token_encoded:
+ case sbjson_token_string:
return @"string";
break;
- case sbjson_token_bool:
+ case sbjson_token_true:
+ case sbjson_token_false:
return @"boolean";
break;
@@ -95,19 +90,19 @@ - (NSString*)tokenName:(sbjson_token_t)token {
return @"null";
break;
- case sbjson_token_entry_sep:
+ case sbjson_token_keyval_separator:
return @"key-value separator";
break;
- case sbjson_token_value_sep:
+ case sbjson_token_separator:
return @"value separator";
break;
- case sbjson_token_object_open:
+ case sbjson_token_object_start:
return @"start of object";
break;
- case sbjson_token_object_close:
+ case sbjson_token_object_end:
return @"end of object";
break;
@@ -177,10 +172,8 @@ - (SBJsonStreamParserStatus)parse:(NSData *)data_ {
if ([state isError])
return SBJsonStreamParserError;
- char *token;
- NSUInteger token_len;
- sbjson_token_t tok = [tokeniser getToken:&token length:&token_len];
-
+ NSObject *token;
+ sbjson_token_t tok = [tokeniser getToken:&token];
switch (tok) {
case sbjson_token_eof:
return [state parserShouldReturn:self];
@@ -200,78 +193,55 @@ - (SBJsonStreamParserStatus)parse:(NSData *)data_ {
}
switch (tok) {
- case sbjson_token_object_open:
+ case sbjson_token_object_start:
[self handleObjectStart];
break;
- case sbjson_token_object_close:
+ case sbjson_token_object_end:
[self handleObjectEnd: tok];
break;
- case sbjson_token_array_open:
+ case sbjson_token_array_start:
[self handleArrayStart];
break;
- case sbjson_token_array_close:
+ case sbjson_token_array_end:
[self handleArrayEnd: tok];
break;
- case sbjson_token_value_sep:
- case sbjson_token_entry_sep:
+ case sbjson_token_separator:
+ case sbjson_token_keyval_separator:
[state parser:self shouldTransitionTo:tok];
break;
- case sbjson_token_bool:
- [delegate parser:self foundBoolean:token[0] == 't'];
+ case sbjson_token_true:
+ [delegate parser:self foundBoolean:YES];
[state parser:self shouldTransitionTo:tok];
break;
-
- case sbjson_token_null:
- [delegate parserFoundNull:self];
+ case sbjson_token_false:
+ [delegate parser:self foundBoolean:NO];
[state parser:self shouldTransitionTo:tok];
break;
-
- case sbjson_token_integer: {
- const int UNSIGNED_LONG_LONG_MAX_DIGITS = 20;
- if (token_len <= UNSIGNED_LONG_LONG_MAX_DIGITS) {
- if (*token == '-')
- [delegate parser:self foundNumber: @(strtoll(token, NULL, 10))];
- else
- [delegate parser:self foundNumber: @(strtoull(token, NULL, 10))];
-
- [state parser:self shouldTransitionTo:tok];
- break;
- }
- }
- // FALLTHROUGH
-
- case sbjson_token_real: {
- [delegate parser:self foundNumber: @(strtod(token, NULL))];
+
+ case sbjson_token_null:
+ [delegate parserFoundNull:self];
[state parser:self shouldTransitionTo:tok];
break;
- }
-
- case sbjson_token_string: {
- NSString *string = [[NSString alloc] initWithBytes:token length:token_len encoding:NSUTF8StringEncoding];
- if ([state needKey])
- [delegate parser:self foundObjectKey:string];
- else
- [delegate parser:self foundString:string];
+
+ case sbjson_token_number:
+ [delegate parser:self foundNumber:(NSNumber*)token];
[state parser:self shouldTransitionTo:tok];
break;
- }
-
- case sbjson_token_encoded: {
- NSString *string = [self decodeStringToken:token length:token_len];
+
+ case sbjson_token_string:
if ([state needKey])
- [delegate parser:self foundObjectKey:string];
+ [delegate parser:self foundObjectKey:(NSString*)token];
else
- [delegate parser:self foundString:string];
+ [delegate parser:self foundString:(NSString*)token];
[state parser:self shouldTransitionTo:tok];
break;
- }
-
+
default:
break;
}
@@ -282,57 +252,4 @@ - (SBJsonStreamParserStatus)parse:(NSData *)data_ {
}
}
-- (unichar)decodeHexQuad:(char *)quad {
- unichar ch = 0;
- for (NSUInteger i = 0; i < 4; i++) {
- int c = quad[i];
- ch *= 16;
- switch (c) {
- case '0' ... '9': ch += c - '0'; break;
- case 'a' ... 'f': ch += 10 + c - 'a'; break;
- case 'A' ... 'F': ch += 10 + c - 'A'; break;
- default: @throw @"FUT FUT FUT";
- }
- }
- return ch;
-}
-
-- (NSString*)decodeStringToken:(char*)bytes length:(NSUInteger)len {
- NSMutableString *string = [NSMutableString stringWithCapacity:len];
-
- for (NSUInteger i = 0; i < len;) {
- switch (bytes[i]) {
- case '\\': {
- switch (bytes[++i]) {
- case '"': [string appendString:@"\""]; i++; break;
- case '/': [string appendString:@"/"]; i++; break;
- case '\\': [string appendString:@"\\"]; i++; break;
- case 'b': [string appendString:@"\b"]; i++; break;
- case 'f': [string appendString:@"\f"]; i++; break;
- case 'n': [string appendString:@"\n"]; i++; break;
- case 'r': [string appendString:@"\r"]; i++; break;
- case 't': [string appendString:@"\t"]; i++; break;
- case 'u': {
- unichar hi = [self decodeHexQuad:bytes + i + 1];
- i += 5;
- if (SBStringIsSurrogateHighCharacter(hi)) {
- // Skip past \u that we know is there..
- unichar lo = [self decodeHexQuad:bytes + i + 2];
- i += 6;
- [string appendFormat:@"%C%C", hi, lo];
- } else {
- [string appendFormat:@"%C", hi];
- }
- break;
- }
- default: @throw @"FUT FUT FUT";
- }
- break;
- }
- default: [string appendFormat:@"%c", bytes[i++]]; break;
- }
- }
- return string;
-}
-
@end
View
6 json/SBJsonStreamParserAccumulator.m
@@ -27,9 +27,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-//#if !__has_feature(objc_arc)
-//#error "This source file must be compiled with ARC enabled!"
-//#endif
+#if !__has_feature(objc_arc)
+#error "This source file must be compiled with ARC enabled!"
+#endif
#import "SBJsonStreamParserAccumulator.h"
View
6 json/SBJsonStreamParserAdapter.m
@@ -30,9 +30,9 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-//#if !__has_feature(objc_arc)
-//#error "This source file must be compiled with ARC enabled!"
-//#endif
+#if !__has_feature(objc_arc)
+#error "This source file must be compiled with ARC enabled!"
+#endif
#import "SBJsonStreamParserAdapter.h"
View
2 json/SBJsonStreamParserState.h
@@ -32,7 +32,7 @@
#import <Foundation/Foundation.h>
-#import "SBJsonStreamTokeniser.h"
+#import "SBJsonTokeniser.h"
#import "SBJsonStreamParser.h"
@interface SBJsonStreamParserState : NSObject
View
58 json/SBJsonStreamParserState.m
@@ -30,9 +30,9 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-//#if !__has_feature(objc_arc)
-//#error "This source file must be compiled with ARC enabled!"
-//#endif
+#if !__has_feature(objc_arc)
+#error "This source file must be compiled with ARC enabled!"
+#endif
#import "SBJsonStreamParserState.h"
@@ -82,23 +82,23 @@ @implementation SBJsonStreamParserStateStart
SINGLETON
- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token {
- return token == sbjson_token_array_open || token == sbjson_token_object_open;
+ return token == sbjson_token_array_start || token == sbjson_token_object_start;
}
- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok {
SBJsonStreamParserState *state = nil;
switch (tok) {
- case sbjson_token_array_open:
+ case sbjson_token_array_start:
state = [SBJsonStreamParserStateArrayStart sharedInstance];
break;
- case sbjson_token_object_open:
+ case sbjson_token_object_start:
state = [SBJsonStreamParserStateObjectStart sharedInstance];
break;
- case sbjson_token_array_close:
- case sbjson_token_object_close:
+ case sbjson_token_array_end:
+ case sbjson_token_object_end:
if (parser.supportMultipleDocuments)
state = parser.state;
else
@@ -163,9 +163,8 @@ - (NSString*)name { return @"at beginning of object"; }
- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token {
switch (token) {
- case sbjson_token_object_close:
+ case sbjson_token_object_end:
case sbjson_token_string:
- case sbjson_token_encoded:
return YES;
break;
default:
@@ -193,7 +192,7 @@ @implementation SBJsonStreamParserStateObjectGotKey
- (NSString*)name { return @"after object key"; }
- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token {
- return token == sbjson_token_entry_sep;
+ return token == sbjson_token_keyval_separator;
}
- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok {
@@ -212,14 +211,13 @@ - (NSString*)name { return @"as object value"; }
- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token {
switch (token) {
- case sbjson_token_object_open:
- case sbjson_token_array_open:
- case sbjson_token_bool:
+ case sbjson_token_object_start:
+ case sbjson_token_array_start:
+ case sbjson_token_true:
+ case sbjson_token_false:
case sbjson_token_null:
- case sbjson_token_integer:
- case sbjson_token_real:
- case sbjson_token_string:
- case sbjson_token_encoded:
+ case sbjson_token_number:
+ case sbjson_token_string:
return YES;
break;
@@ -245,8 +243,8 @@ - (NSString*)name { return @"after object value"; }
- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token {
switch (token) {
- case sbjson_token_object_close:
- case sbjson_token_value_sep:
+ case sbjson_token_object_end:
+ case sbjson_token_separator:
return YES;
break;
default:
@@ -271,7 +269,7 @@ @implementation SBJsonStreamParserStateObjectNeedKey
- (NSString*)name { return @"in place of object key"; }
- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token {
- return sbjson_token_string == token || sbjson_token_encoded == token;
+ return sbjson_token_string == token;
}
- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok {
@@ -294,9 +292,9 @@ - (NSString*)name { return @"at array start"; }
- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token {
switch (token) {
- case sbjson_token_object_close:
- case sbjson_token_entry_sep:
- case sbjson_token_value_sep:
+ case sbjson_token_object_end:
+ case sbjson_token_keyval_separator:
+ case sbjson_token_separator:
return NO;
break;
@@ -322,11 +320,11 @@ - (NSString*)name { return @"after array value"; }
- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token {
- return token == sbjson_token_array_close || token == sbjson_token_value_sep;
+ return token == sbjson_token_array_end || token == sbjson_token_separator;
}
- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok {
- if (tok == sbjson_token_value_sep)
+ if (tok == sbjson_token_separator)
parser.state = [SBJsonStreamParserStateArrayNeedValue sharedInstance];
}
@@ -343,10 +341,10 @@ - (NSString*)name { return @"as array value"; }
- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token {
switch (token) {
- case sbjson_token_array_close:
- case sbjson_token_entry_sep:
- case sbjson_token_object_close:
- case sbjson_token_value_sep:
+ case sbjson_token_array_end:
+ case sbjson_token_keyval_separator:
+ case sbjson_token_object_end:
+ case sbjson_token_separator:
return NO;
break;
View
21 json/SBJsonStreamWriter.m
@@ -30,13 +30,14 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-//#if !__has_feature(objc_arc)
-//#error "This source file must be compiled with ARC enabled!"
-//#endif
+#if !__has_feature(objc_arc)
+#error "This source file must be compiled with ARC enabled!"
+#endif
#import "SBJsonStreamWriter.h"
#import "SBJsonStreamWriterState.h"
+static NSNumber *kNotANumber;
static NSNumber *kTrue;
static NSNumber *kFalse;
static NSNumber *kPositiveInfinity;
@@ -54,6 +55,7 @@ @implementation SBJsonStreamWriter
@synthesize sortKeysComparator;
+ (void)initialize {
+ kNotANumber = [NSDecimalNumber notANumber];
kPositiveInfinity = [NSNumber numberWithDouble:+HUGE_VAL];
kNegativeInfinity = [NSNumber numberWithDouble:-HUGE_VAL];
kTrue = [NSNumber numberWithBool:YES];
@@ -338,7 +340,7 @@ - (BOOL)writeNumber:(NSNumber*)number {
self.error = @"-Infinity is not a valid number in JSON";
return NO;
- } else if (isnan([number doubleValue])) {
+ } else if ([kNotANumber isEqualToNumber:number]) {
self.error = @"NaN is not a valid number in JSON";
return NO;
}
@@ -354,10 +356,15 @@ - (BOOL)writeNumber:(NSNumber*)number {
case 'C': case 'I': case 'S': case 'L': case 'Q':
len = snprintf(num, sizeof num, "%llu", [number unsignedLongLongValue]);
break;
- case 'f': case 'd': default: {
- len = snprintf(num, sizeof num, "%.17g", [number doubleValue]);
+ case 'f': case 'd': default:
+ if ([number isKindOfClass:[NSDecimalNumber class]]) {
+ char const *utf8 = [[number stringValue] UTF8String];
+ [delegate writer:self appendBytes:utf8 length: strlen(utf8)];
+ [state transitionState:self];
+ return YES;
+ }
+ len = snprintf(num, sizeof num, "%.17g", [number doubleValue]);
break;
- }
}
[delegate writer:self appendBytes:num length: len];
[state transitionState:self];
View
6 json/SBJsonStreamWriterAccumulator.m
@@ -27,9 +27,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-//#if !__has_feature(objc_arc)
-//#error "This source file must be compiled with ARC enabled!"
-//#endif
+#if !__has_feature(objc_arc)
+#error "This source file must be compiled with ARC enabled!"
+#endif
#import "SBJsonStreamWriterAccumulator.h"
View
6 json/SBJsonStreamWriterState.m
@@ -30,9 +30,9 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-//#if !__has_feature(objc_arc)
-//#error "This source file must be compiled with ARC enabled!"
-//#endif
+#if !__has_feature(objc_arc)
+#error "This source file must be compiled with ARC enabled!"
+#endif
#import "SBJsonStreamWriterState.h"
#import "SBJsonStreamWriter.h"
View
71 json/SBJsonTokeniser.h
@@ -6,16 +6,16 @@
modification, are permitted provided that the following conditions are
met:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
- Neither the name of the the author nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ Neither the name of the the author nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -33,38 +33,35 @@
#import <Foundation/Foundation.h>
typedef enum {
- sbjson_token_eof,
- sbjson_token_error,
- sbjson_token_object_start,
- sbjson_token_key_value_separator,
- sbjson_token_object_end,
- sbjson_token_array_start,
- sbjson_token_array_end,
- sbjson_token_separator,
- sbjson_token_string,
- sbjson_token_string_encoded,
- sbjson_token_integer,
- sbjson_token_double,
- sbjson_token_true,
- sbjson_token_false,
- sbjson_token_null,
+ sbjson_token_error = -1,
+ sbjson_token_eof,
+
+ sbjson_token_array_start,
+ sbjson_token_array_end,
+
+ sbjson_token_object_start,
+ sbjson_token_object_end,
+
+ sbjson_token_separator,
+ sbjson_token_keyval_separator,
+
+ sbjson_token_number,
+ sbjson_token_string,
+ sbjson_token_true,
+ sbjson_token_false,
+ sbjson_token_null,
+
} sbjson_token_t;
-@interface SBJsonTokeniser : NSObject {
- NSUInteger tokenStart, tokenLength;
- NSMutableData *buf;
- const char *bufbytes;
- NSUInteger bufbytesLength;
- NSString *error;
- NSCharacterSet *illegalCharacterSet;
-}
+@class SBJsonUTF8Stream;
+
+@interface SBJsonTokeniser : NSObject
-@property(copy, readonly) NSString *error;
+@property (strong) SBJsonUTF8Stream *stream;
+@property (copy) NSString *error;
-- (void)appendData:(NSData*)data;
+- (void)appendData:(NSData*)data_;
-- (sbjson_token_t)next;
-- (BOOL)getToken:(const char **)utf8 length:(NSUInteger*)length;
-- (NSString*)getDecodedStringToken;
+- (sbjson_token_t)getToken:(NSObject**)token;
@end
View
874 json/SBJsonTokeniser.m
@@ -1,22 +1,21 @@
/*
- Copyright (c) 2010, Stig Brautaset.
- All rights reserved.
-
+ Copyright (c) 2010-2011, Stig Brautaset. All rights reserved.
+
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
-
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- Neither the name of the the author nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
+
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ Neither the name of the the author nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
@@ -30,480 +29,449 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#if !__has_feature(objc_arc)
+#error "This source file must be compiled with ARC enabled!"
+#endif
+
#import "SBJsonTokeniser.h"
+#import "SBJsonUTF8Stream.h"
+#define SBStringIsIllegalSurrogateHighCharacter(character) (((character) >= 0xD800UL) && ((character) <= 0xDFFFUL))
+#define SBStringIsSurrogateLowCharacter(character) ((character >= 0xDC00UL) && (character <= 0xDFFFUL))
+#define SBStringIsSurrogateHighCharacter(character) ((character >= 0xD800UL) && (character <= 0xDBFFUL))
-#define isDigit(x) (*x >= '0' && *x <= '9')
-#define skipDigits(x) while (isDigit(x)) x++
+static int const DECIMAL_MAX_PRECISION = 38;
+static int const DECIMAL_EXPONENT_MAX = 127;
+static short const DECIMAL_EXPONENT_MIN = -128;
+static int const LONG_LONG_DIGITS = 19;
-#define SBStringIsIllegalSurrogateHighCharacter(x) (((x) >= 0xd800) && ((x) <= 0xdfff))
+static NSCharacterSet *kDecimalDigitCharacterSet;
+@implementation SBJsonTokeniser
-@interface SBJsonTokeniser ()
+@synthesize error = _error;
+@synthesize stream = _stream;
-@property (copy) NSString *error;
++ (void)initialize {
+ kDecimalDigitCharacterSet = [NSCharacterSet decimalDigitCharacterSet];
+}
-- (void)skipWhitespace;
+- (id)init {
+ self = [super init];
+ if (self) {
+ _stream = [[SBJsonUTF8Stream alloc] init];
-- (sbjson_token_t)match:(const char *)utf8 ofLength:(NSUInteger)len andReturn:(sbjson_token_t)tok;
-- (sbjson_token_t)matchString;
-- (sbjson_token_t)matchNumber;
+ }
-- (int)parseUnicodeEscape:(const char *)bytes index:(NSUInteger *)anIndex;
-- (NSString*)decodeUnicodeEscape:(const char *)bytes index:(NSUInteger *)anIndex;
+ return self;
+}
-@end
-@implementation SBJsonTokeniser
+- (void)appendData:(NSData *)data_ {
+ [_stream appendData:data_];
+}
-@synthesize error;
-#pragma mark Housekeeping
+- (sbjson_token_t)match:(const char *)pattern length:(NSUInteger)len retval:(sbjson_token_t)token {
+ if (![_stream haveRemainingCharacters:len])
+ return sbjson_token_eof;
-- (id)init {
- self = [super init];
- if (self) {
- tokenStart = tokenLength = 0;
- buf = [[NSMutableData alloc] initWithCapacity:4096];
- illegalCharacterSet = [[NSCharacterSet illegalCharacterSet] copy];
- }
- return self;
-}
+ if ([_stream skipCharacters:pattern length:len])
+ return token;
-- (void)dealloc {
- self.error = nil;
-
- [super dealloc];
+ self.error = [NSString stringWithFormat:@"Expected '%s' after initial '%.1s'", pattern, pattern];
+ return sbjson_token_error;
}
-#pragma mark Methods
-
-- (void)appendData:(NSData *)data {
-
- // Remove previous NUL char
- if (buf.length)
- buf.length = buf.length - 1;
-
- if (tokenStart) {
- // Remove stuff in the front of the offset
- [buf replaceBytesInRange:NSMakeRange(0, tokenStart) withBytes:"" length:0];
- tokenStart = 0;
- }
-
- [buf appendData:data];
-
- // Append NUL byte to simplify logic
- [buf appendBytes:"\0" length:1];
-
- bufbytes = [buf bytes];
- bufbytesLength = [buf length];
+- (BOOL)decodeEscape:(unichar)ch into:(unichar*)decoded {
+ switch (ch) {
+ case '\\':
+ case '/':
+ case '"':
+ *decoded = ch;
+ break;
+
+ case 'b':
+ *decoded = '\b';
+ break;
+
+ case 'n':
+ *decoded = '\n';
+ break;
+
+ case 'r':
+ *decoded = '\r';
+ break;
+
+ case 't':
+ *decoded = '\t';
+ break;
+
+ case 'f':
+ *decoded = '\f';
+ break;
+
+ default:
+ self.error = @"Illegal escape character";
+ return NO;
+ break;
+ }
+ return YES;
}
-- (BOOL)getToken:(const char **)utf8 length:(NSUInteger *)len {
- if (!tokenLength)
- return NO;
-
- *len = tokenLength;
- *utf8 = bufbytes + tokenStart;
- return YES;
-}
+- (BOOL)decodeHexQuad:(unichar*)quad {
+ unichar c, tmp = 0;
-- (NSString*)getDecodedStringToken {
- NSUInteger len;
- const char *bytes;
- [self getToken:&bytes length:&len];
-
- len -= 1;
-
- NSMutableData *data = [NSMutableData dataWithCapacity:len * 1.1];
-
- char c;
- NSUInteger i = 1;
-again: while (i < len) {
- switch (c = bytes[i++]) {
- case '\\':
- switch (c = bytes[i++]) {
- case '\\':
- case '/':
- case '"':
- break;
-
- case 'b':
- c = '\b';
- break;
-
- case 'n':
- c = '\n';
- break;
-
- case 'r':
- c = '\r';
- break;
-
- case 't':
- c = '\t';
- break;
-
- case 'f':
- c = '\f';
- break;
-
- case 'u': {
- NSString *s = [self decodeUnicodeEscape:bytes index:&i];
- NSAssert(s, @"Illegal unicode escape");
- [data appendData:[s dataUsingEncoding:NSUTF8StringEncoding]];
- goto again;
- break;
- }
-
- default:
- NSAssert(NO, @"Should never get here");
- break;
- }
- break;
-
- case 0 ... 0x1F:
- self.error = @"Unescaped control chars";
- return nil;
- break;
-
- default:
- break;
- }
- [data appendBytes:&c length:1];
- }
-
- return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
-}
+ for (int i = 0; i < 4; i++) {
+ (void)[_stream getNextUnichar:&c];
+ tmp *= 16;
+ switch (c) {
+ case '0' ... '9':
+ tmp += c - '0';
+ break;
+ case 'a' ... 'f':
+ tmp += 10 + c - 'a';
+ break;
-- (sbjson_token_t)next {
- tokenStart += tokenLength;
- tokenLength = 0;
-
- [self skipWhitespace];
-
- switch (*(bufbytes + tokenStart)) {
- case '\0':
- return sbjson_token_eof;
- break;
-
- case '[':
- tokenLength = 1;
- return sbjson_token_array_start;
- break;
-
- case ']':
- tokenLength = 1;
- return sbjson_token_array_end;
- break;
-
- case '{':
- tokenLength = 1;
- return sbjson_token_object_start;
- break;
-
- case ':':
- tokenLength = 1;
- return sbjson_token_key_value_separator;
- break;
-
- case '}':
- tokenLength = 1;
- return sbjson_token_object_end;
- break;
-
- case ',':
- tokenLength = 1;
- return sbjson_token_separator;
- break;
-
- case 'n':
- return [self match:"null" ofLength:4 andReturn:sbjson_token_null];
- break;
-
- case 't':
- return [self match:"true" ofLength:4 andReturn:sbjson_token_true];
- break;
-
- case 'f':
- return [self match:"false" ofLength:5 andReturn:sbjson_token_false];
- break;
-
- case '"':
- return [self matchString];
- break;
-
- case '-':
- case '0' ... '9':
- return [self matchNumber];
- break;
-
- case '+':
- self.error = [NSString stringWithFormat:@"Leading + is illegal in numbers at offset %lu", tokenStart];
- return sbjson_token_error;
- break;
- }
-
- self.error = [NSString stringWithFormat:@"Unrecognised leading character at offset %lu", tokenStart];
- return sbjson_token_error;
-}
+ case 'A' ... 'F':
+ tmp += 10 + c - 'A';
+ break;
-#pragma mark Private methods
-
-- (void)skipWhitespace {
- while (tokenStart < bufbytesLength) {
- switch (bufbytes[tokenStart]) {
- case ' ':
- case '\t':
- case '\n':
- case '\r':
- case '\f':
- case '\v':
- tokenStart++;
- break;
- default:
- return;
- break;
- }
- }
+ default:
+ return NO;
+ }
+ }
+ *quad = tmp;
+ return YES;
}
-- (sbjson_token_t)match:(const char *)utf8 ofLength:(NSUInteger)len andReturn:(sbjson_token_t)tok {
- if (buf.length - tokenStart - 1 < len)
- return sbjson_token_eof;
-
- if (strncmp(bufbytes + tokenStart, utf8, len)) {
- NSString *format = [NSString stringWithFormat:@"Expected '%%s' but found '%%.%lus'.", len];
- self.error = [NSString stringWithFormat:format, utf8, bufbytes + tokenStart];
- return sbjson_token_error;
- }
-
- tokenLength = len;
- return tok;
+- (sbjson_token_t)getStringToken:(NSObject**)token {
+ NSMutableString *acc = nil;
+
+ for (;;) {
+ [_stream skip];
+
+ unichar ch;
+ {
+ NSMutableString *string = nil;
+
+ if (![_stream getStringFragment:&string])
+ return sbjson_token_eof;
+
+ if (!string) {
+ self.error = @"Broken Unicode encoding";
+ return sbjson_token_error;
+ }
+
+ if (![_stream getUnichar:&ch])
+ return sbjson_token_eof;
+
+ if (acc) {
+ [acc appendString:string];
+
+ } else if (ch == '"') {
+ *token = [string copy];
+ [_stream skip];
+ return sbjson_token_string;
+
+ } else {
+ acc = [string mutableCopy];
+ }
+ }
+
+
+ switch (ch) {
+ case 0 ... 0x1F:
+ self.error = [NSString stringWithFormat:@"Unescaped control character [0x%0.2X]", (int)ch];
+ return sbjson_token_error;
+ break;
+
+ case '"':
+ *token = acc;
+ [_stream skip];
+ return sbjson_token_string;
+ break;
+
+ case '\\':
+ if (![_stream getNextUnichar:&ch])
+ return sbjson_token_eof;
+
+ if (ch == 'u') {
+ if (![_stream haveRemainingCharacters:5])
+ return sbjson_token_eof;
+
+ unichar hi;
+ if (![self decodeHexQuad:&hi]) {
+ self.error = @"Invalid hex quad";
+ return sbjson_token_error;
+ }
+
+ if (SBStringIsSurrogateHighCharacter(hi)) {
+ unichar lo;
+
+ if (![_stream haveRemainingCharacters:6])
+ return sbjson_token_eof;
+
+ (void)[_stream getNextUnichar:&ch];
+ (void)[_stream getNextUnichar:&lo];
+ if (ch != '\\' || lo != 'u' || ![self decodeHexQuad:&lo]) {
+ self.error = @"Missing low character in surrogate pair";
+ return sbjson_token_error;
+ }
+
+ if (!SBStringIsSurrogateLowCharacter(lo)) {
+ self.error = @"Invalid low character in surrogate pair";
+ return sbjson_token_error;
+ }
+
+ [acc appendFormat:@"%C%C", hi, lo];
+ } else if (SBStringIsIllegalSurrogateHighCharacter(hi)) {
+ self.error = @"Invalid high character in surrogate pair";
+ return sbjson_token_error;
+ } else {
+ [acc appendFormat:@"%C", hi];
+ }
+
+
+ } else {
+ unichar decoded;
+ if (![self decodeEscape:ch into:&decoded])
+ return sbjson_token_error;
+ [acc appendFormat:@"%C", decoded];
+ }
+
+ break;
+
+ default: {
+ self.error = [NSString stringWithFormat:@"Invalid UTF-8: '%x'", (int)ch];
+ return sbjson_token_error;
+ break;
+ }
+ }
+ }
+ return sbjson_token_eof;
}
+- (sbjson_token_t)getNumberToken:(NSObject**)token {
-- (int)decodeHexQuad:(const char *)hexQuad {
- char c;
- int ret = 0;
- int i;
- for (i = 0; i < 4; i++) {
- ret *= 16;
- switch (c = hexQuad[i]) {
- case '\0':
- return -2;
- break;
-
- case '0' ... '9':
- ret += c - '0';
- break;
-
- case 'a' ... 'f':
- ret += 10 + c - 'a';
- break;
-
- case 'A' ... 'F':
- ret += 10 + c - 'A';
- break;
-
- default:
- self.error = @"XXX illegal digit in hex char";
- return -1;
- break;
- }
+ NSUInteger numberStart = _stream.index;
+
+ unichar ch;
+ if (![_stream getUnichar:&ch])
+ return sbjson_token_eof;
+
+ BOOL isNegative = NO;
+ if (ch == '-') {
+ isNegative = YES;
+ if (![_stream getNextUnichar:&ch])
+ return sbjson_token_eof;
}
- return ret;
-}
-- (int)parseUnicodeEscape:(const char *)bytes index:(NSUInteger *)anIndex {
- int hi = [self decodeHexQuad:bytes + *anIndex];
- if (hi == -2) return -2; // EOF
- if (hi < 0) {
- self.error = @"Missing hex quad";
- return -1;
- }
- *anIndex += 4;
-
- if (CFStringIsSurrogateHighCharacter(hi)) {
- int lo = -1;
- if (bytes[(*anIndex)++] == '\\' && bytes[(*anIndex)++] == 'u')
- lo = [self decodeHexQuad:bytes + *anIndex];
-
- if (lo < 0) {
- self.error = @"Missing low character in surrogate pair";
- return -1;
- }
- *anIndex += 4;
-
- if (!CFStringIsSurrogateLowCharacter(lo)) {
- self.error = @"Invalid low surrogate char";
- return -1;
- }
- } else if (SBStringIsIllegalSurrogateHighCharacter(hi)) {
- self.error = @"Invalid high character in surrogate pair";
- return -1;
- }
-
-
- return hi;
-}
+ unsigned long long mantissa = 0;
+ int mantissa_length = 0;
+
+ if (ch == '0') {
+ mantissa_length++;
+ if (![_stream getNextUnichar:&ch])
+ return sbjson_token_eof;
+
+ if ([kDecimalDigitCharacterSet characterIsMember:ch]) {
+ self.error = @"Leading zero is illegal in number";
+ return sbjson_token_error;
+ }
+ }
-- (NSString*)decodeUnicodeEscape:(const char *)bytes index:(NSUInteger *)anIndex {
- int hi_int = [self decodeHexQuad:bytes + *anIndex];
- if (hi_int < 0) {
- self.error = @"Missing hex quad";
- return nil;
- }
- unichar hi = (unichar)hi_int;
- *anIndex += 4;
-
- if (CFStringIsSurrogateHighCharacter(hi)) { // high surrogate char?
- int lo = -1;
- if (bytes[(*anIndex)++] == '\\' && bytes[(*anIndex)++] == 'u')
- lo = [self decodeHexQuad:bytes + *anIndex];
-
- if (lo < 0) {
- self.error = @"Missing low character in surrogate pair";
- return nil;
- }
- *anIndex += 4;
-
- if (!CFStringIsSurrogateLowCharacter(lo)) {
- self.error = @"Invalid low surrogate char";
- return nil;
- }
-
- unichar pair[2] = {hi, lo};
- return [NSString stringWithCharacters:pair length:2];
-
- } else if (SBStringIsIllegalSurrogateHighCharacter(hi)) {
- self.error = @"Invalid high character in surrogate pair";
- return nil;
- }
-
- return [NSString stringWithCharacters:&hi length:1];
-}
+ while ([kDecimalDigitCharacterSet characterIsMember:ch]) {
+ mantissa *= 10;
+ mantissa += (ch - '0');
+ mantissa_length++;
+
+ if (![_stream getNextUnichar:&ch])
+ return sbjson_token_eof;
+ }
+
+ short exponent = 0;
+ BOOL isFloat = NO;
+
+ if (ch == '.') {
+ isFloat = YES;
+ if (![_stream getNextUnichar:&ch])
+ return sbjson_token_eof;
+
+ while ([kDecimalDigitCharacterSet characterIsMember:ch]) {
+ mantissa *= 10;
+ mantissa += (ch - '0');
+ mantissa_length++;
+ exponent--;
+
+ if (![_stream getNextUnichar:&ch])
+ return sbjson_token_eof;
+ }
+
+ if (!exponent) {
+ self.error = @"No digits after decimal point";
+ return sbjson_token_error;
+ }
+ }
+
+ BOOL hasExponent = NO;
+ if (ch == 'e' || ch == 'E') {
+ hasExponent = YES;
+
+ if (![_stream getNextUnichar:&ch])
+ return sbjson_token_eof;
+
+ BOOL expIsNegative = NO;
+ if (ch == '-') {
+ expIsNegative = YES;
+ if (![_stream getNextUnichar:&ch])
+ return sbjson_token_eof;
+
+ } else if (ch == '+') {
+ if (![_stream getNextUnichar:&ch])
+ return sbjson_token_eof;
+ }
+
+ short explicit_exponent = 0;
+ short explicit_exponent_length = 0;
+ while ([kDecimalDigitCharacterSet characterIsMember:ch]) {
+ explicit_exponent *= 10;
+ explicit_exponent += (ch - '0');
+ explicit_exponent_length++;
+
+ if (![_stream getNextUnichar:&ch])
+ return sbjson_token_eof;
+ }
+
+ if (explicit_exponent_length == 0) {
+ self.error = @"No digits in exponent";
+ return sbjson_token_error;
+ }
+
+ if (expIsNegative)
+ exponent -= explicit_exponent;
+ else
+ exponent += explicit_exponent;
+ }
+
+ if (!mantissa_length && isNegative) {
+ self.error = @"No digits after initial minus";
+ return sbjson_token_error;
+
+ } else if (mantissa_length > DECIMAL_MAX_PRECISION) {
+ self.error = @"Precision is too high";
+ return sbjson_token_error;
+
+ } else if (exponent > DECIMAL_EXPONENT_MAX || exponent < DECIMAL_EXPONENT_MIN) {
+ self.error = @"Exponent out of range";
+ return sbjson_token_error;
+ }
+
+ if (mantissa_length <= LONG_LONG_DIGITS) {
+ if (!isFloat && !hasExponent) {
+ *token = [NSNumber numberWithLongLong: isNegative ? -mantissa : mantissa];
+ } else if (mantissa == 0) {
+ *token = [NSNumber numberWithFloat:-0.0f];
+ } else {
+ *token = [NSDecimalNumber decimalNumberWithMantissa:mantissa
+ exponent:exponent
+ isNegative:isNegative];
+ }
+
+ } else {
+ NSString *number = [_stream stringWithRange:NSMakeRange(numberStart, _stream.index - numberStart)];
+ *token = [NSDecimalNumber decimalNumberWithString:number];
+ }
-- (sbjson_token_t)matchString {
- sbjson_token_t ret = sbjson_token_string;
-
- const char *bytes = bufbytes + tokenStart;
- NSUInteger idx = 1;
- NSUInteger maxIdx = buf.length - 2 - tokenStart;
-
- while (idx <= maxIdx) {
- switch (bytes[idx++]) {
- case 0 ... 0x1F:
- self.error = [NSString stringWithFormat:@"Unescaped control char 0x%0.2X", (int)bytes[idx-1]];
- return sbjson_token_error;
- break;
-
- case '\\':
- ret = sbjson_token_string_encoded;
-
- if (idx >= maxIdx)
- return sbjson_token_eof;
-
- switch (bytes[idx++]) {
- case 'b':
- case 't':
- case 'n':
- case 'r':
- case 'f':
- case 'v':
- case '"':
- case '\\':
- case '/':
- // Valid escape sequence
- break;
-
- case 'u': {
- int ch = [self parseUnicodeEscape:bytes index:&idx];
- if (ch == -2)
- return sbjson_token_eof;
- if (ch == -1)
- return sbjson_token_error;
- break;
- }
- default:
- self.error = [NSString stringWithFormat:@"Broken escape character at index %lu in token starting at offset %lu", idx-1, tokenStart];
- return sbjson_token_error;
- break;
- }
- break;
-
- case '"':
- tokenLength = idx;
- return ret;
- break;
-
- default:
- // any other character
- break;
- }
- }
-
- return sbjson_token_eof;
+ return sbjson_token_number;
}
-- (sbjson_token_t)matchNumber {
-
- sbjson_token_t ret = sbjson_token_integer;
- const char *c = bufbytes + tokenStart;
-
- if (*c == '-') {
- c++;
- if (!isDigit(c)) {
- self.error = @"No digits after initial minus";
- return sbjson_token_error;
- }
- }
-
- if (*c == '0') {
- c++;
- if (isDigit(c)) {
- self.error = [NSString stringWithFormat:@"Leading zero is illegal in number at offset %lu", tokenStart];
- return sbjson_token_error;
- }
- }
-
- skipDigits(c);
-
-
- if (*c == '.') {
- ret = sbjson_token_double;
- c++;
-
- if (!isDigit(c) && *c) {
- self.error = [NSString stringWithFormat:@"No digits after decimal point at offset %lu", tokenStart];
- return sbjson_token_error;
- }
-
- skipDigits(c);
- }
-
- if (*c == 'e' || *c == 'E') {
- ret = sbjson_token_double;
- c++;
-
- if (*c == '-' || *c == '+')
- c++;
-
- if (!isDigit(c) && *c) {
- self.error = [NSString stringWithFormat:@"No digits after exponent mark at offset %lu", tokenStart];
- return sbjson_token_error;
- }
-
- skipDigits(c);
- }
-
- if (!*c)
- return sbjson_token_eof;
-
- tokenLength = c - (bufbytes + tokenStart);
- return ret;
+- (sbjson_token_t)getToken:(NSObject **)token {
+
+ [_stream skipWhitespace];
+
+ unichar ch;
+ if (![_stream getUnichar:&ch])
+ return sbjson_token_eof;
+
+ NSUInteger oldIndexLocation = _stream.index;
+ sbjson_token_t tok;
+
+ switch (ch) {
+ case '[':
+ tok = sbjson_token_array_start;
+ [_stream skip];
+ break;
+
+ case ']':
+ tok = sbjson_token_array_end;
+ [_stream skip];
+ break;
+
+ case '{':
+ tok = sbjson_token_object_start;
+ [_stream skip];
+ break;
+
+ case ':':
+ tok = sbjson_token_keyval_separator;
+ [_stream skip];
+ break;
+
+ case '}':
+ tok = sbjson_token_object_end;
+ [_stream skip];
+ break;
+
+ case ',':
+ tok = sbjson_token_separator;
+ [_stream skip];
+ break;
+
+ case 'n':
+ tok = [self match:"null" length:4 retval:sbjson_token_null];
+ break;
+
+ case 't':
+ tok = [self match:"true" length:4 retval:sbjson_token_true];
+ break;
+
+ case 'f':
+ tok = [self match:"false" length:5 retval:sbjson_token_false];
+ break;
+
+ case '"':
+ tok = [self getStringToken:token];
+ break;
+
+ case '0' ... '9':
+ case '-':
+ tok = [self getNumberToken:token];
+ break;
+
+ case '+':
+ self.error = @"Leading + is illegal in number";
+ tok = sbjson_token_error;
+ break;
+
+ default:
+ self.error = [NSString stringWithFormat:@"Illegal start of token [%c]", ch];
+ tok = sbjson_token_error;
+ break;
+ }
+
+ if (tok == sbjson_token_eof) {
+ // We ran out of bytes in the middle of a token.
+ // We don't know how to restart in mid-flight, so
+ // rewind to the start of the token for next attempt.
+ // Hopefully we'll have more data then.
+ _stream.index = oldIndexLocation;
+ }
+
+ return tok;
}
+
@end
View
58 json/SBJsonUTF8Stream.h
@@ -0,0 +1,58 @@
+/*
+ Copyright (c) 2011, Stig Brautaset. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ Neither the name of the the author nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Foundation/Foundation.h>
+
+
+@interface SBJsonUTF8Stream : NSObject {
+@private
+ const char *_bytes;
+ NSMutableData *_data;
+ NSUInteger _length;
+}
+
+@property (assign) NSUInteger index;
+
+- (void)appendData:(NSData*)data_;
+
+- (BOOL)haveRemainingCharacters:(NSUInteger)chars;
+
+- (void)skip;
+- (void)skipWhitespace;
+- (BOOL)skipCharacters:(const char *)chars length:(NSUInteger)len;
+
+- (BOOL)getUnichar:(unichar*)ch;
+- (BOOL)getNextUnichar:(unichar*)ch;
+- (BOOL)getStringFragment:(NSString**)string;
+
+- (NSString*)stringWithRange:(NSRange)range;
+
+@end
View
145 json/SBJsonUTF8Stream.m
@@ -0,0 +1,145 @@
+/*
+ Copyright (c) 2011, Stig Brautaset. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ Neither the name of the the author nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if !__has_feature(objc_arc)
+#error "This source file must be compiled with ARC enabled!"
+#endif
+
+#import "SBJsonUTF8Stream.h"
+
+
+@implementation SBJsonUTF8Stream
+
+@synthesize index = _index;
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ _data = [[NSMutableData alloc] initWithCapacity:4096u];
+ }
+ return self;
+}
+
+
+- (void)appendData:(NSData *)data_ {
+
+ if (_index) {
+ // Discard data we've already parsed
+ [_data replaceBytesInRange:NSMakeRange(0, _index) withBytes:"" length:0];
+
+ // Reset index to point to current position
+ _index = 0;
+ }
+
+ [_data appendData:data_];
+
+ // This is an optimisation.
+ _bytes = (const char*)[_data bytes];
+ _length = [_data length];
+}
+
+
+- (BOOL)getUnichar:(unichar*)ch {
+ if (_index < _length) {
+ *ch = (unichar)_bytes[_index];
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)getNextUnichar:(unichar*)ch {
+ if (++_index < _length) {
+ *ch = (unichar)_bytes[_index];
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)getStringFragment:(NSString **)string {
+ NSUInteger start = _index;
+ while (_index < _length) {
+ switch (_bytes[_index]) {
+ case '"':
+ case '\\':
+ case 0 ... 0x1f:
+ *string = [[NSString alloc] initWithBytes:(_bytes + start)
+ length:(_index - start)
+ encoding:NSUTF8StringEncoding];
+ return YES;
+ break;
+ default:
+ _index++;
+ break;
+ }
+ }
+ return NO;
+}
+
+- (void)skip {
+ _index++;
+}
+
+- (void)skipWhitespace {
+ while (_index < _length) {
+ switch (_bytes[_index]) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ _index++;
+ break;
+ default:
+ return;
+ break;
+ }
+ }
+}
+
+- (BOOL)haveRemainingCharacters:(NSUInteger)chars {
+ return [_data length] - _index >= chars;
+}
+
+- (BOOL)skipCharacters:(const char *)chars length:(NSUInteger)len {
+ const void *bytes = ((const char*)[_data bytes]) + _index;
+ if (!memcmp(bytes, chars, len)) {
+ _index += len;
+ return YES;
+ }
+ return NO;
+}
+
+- (NSString*)stringWithRange:(NSRange)range {
+ return [[NSString alloc] initWithBytes:_bytes + range.location length:range.length encoding:NSUTF8StringEncoding];
+
+}
+
+
+@end
View
20 json/SBJsonWriter.h
@@ -82,7 +82,7 @@
@property (copy) NSComparator sortKeysComparator;
/**
- Generates string with JSON representation for the given object.
+ Return JSON representation for the given object.
Returns a string containing JSON representation of the passed in value, or nil on error.
If nil is returned and error is not NULL, *error can be interrogated to find the cause of the error.
@@ -92,7 +92,7 @@
- (NSString*)stringWithObject:(id)value;
/**
- Generates JSON representation for the given object.
+ Return JSON representation for the given object.
Returns an NSData object containing JSON represented as UTF8 text, or nil on error.