Permalink
Browse files

Updated SBJson parser version

  • Loading branch information...
1 parent 4836923 commit 0ef76c4fedd18e6a7f807fddd14586bc88f187ee @erndev erndev committed with May 1, 2011
View
31 JSON/JSON.h 100644 → 100755
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2009 Stig Brautaset. All rights reserved.
+ 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:
@@ -35,16 +35,31 @@
JSON. One standard object-based and a higher level api consisting of
categories added to existing Objective-C classes.
- Learn more on the http://code.google.com/p/json-framework project site.
-
- 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.
+ 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>.
*/
-#import "SBJSON.h"
+
+// 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 "NSObject+SBJSON.h"
#import "NSString+SBJSON.h"
+#else
+
+#import <JSON/SBJsonParser.h>
+#import <JSON/SBJsonWriter.h>
+#import <JSON/NSObject+SBJSON.h>
+#import <JSON/NSString+SBJSON.h>
+
+#endif
View
15 JSON/NSObject+SBJSON.h 100644 → 100755
@@ -40,21 +40,6 @@
@interface NSObject (NSObject_SBJSON)
/**
- @brief Returns a string containing the receiver encoded as a JSON fragment.
-
- This method is added as a category on NSObject but is only actually
- supported for the following objects:
- @li NSDictionary
- @li NSArray
- @li NSString
- @li NSNumber (also used for booleans)
- @li NSNull
-
- @deprecated Given we bill ourselves as a "strict" JSON library, this method should be removed.
- */
-- (NSString *)JSONFragment;
-
-/**
@brief Returns a string containing the receiver encoded in JSON.
This method is added as a category on NSObject but is only actually
View
9 JSON/NSObject+SBJSON.m 100644 → 100755
@@ -32,15 +32,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
@implementation NSObject (NSObject_SBJSON)
-- (NSString *)JSONFragment {
- SBJsonWriter *jsonWriter = [SBJsonWriter new];
- NSString *json = [jsonWriter stringWithFragment:self];
- if (!json)
- NSLog(@"-JSONFragment failed. Error trace is: %@", [jsonWriter errorTrace]);
- [jsonWriter release];
- return json;
-}
-
- (NSString *)JSONRepresentation {
SBJsonWriter *jsonWriter = [SBJsonWriter new];
NSString *json = [jsonWriter stringWithObject:self];
View
10 JSON/NSString+SBJSON.h 100644 → 100755
@@ -36,16 +36,6 @@ This is a category on NSString that adds methods for parsing the target string.
*/
@interface NSString (NSString_SBJSON)
-
-/**
- @brief Returns the object represented in the receiver, or nil on error.
-
- Returns a a scalar object represented by the string's JSON fragment representation.
-
- @deprecated Given we bill ourselves as a "strict" JSON library, this method should be removed.
- */
-- (id)JSONFragmentValue;
-
/**
@brief Returns the NSDictionary or NSArray represented by the current string's JSON representation.
View
10 JSON/NSString+SBJSON.m 100644 → 100755
@@ -32,16 +32,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
@implementation NSString (NSString_SBJSON)
-- (id)JSONFragmentValue
-{
- SBJsonParser *jsonParser = [SBJsonParser new];
- id repr = [jsonParser fragmentWithString:self];
- if (!repr)
- NSLog(@"-JSONFragmentValue failed. Error trace is: %@", [jsonParser errorTrace]);
- [jsonParser release];
- return repr;
-}
-
- (id)JSONValue
{
SBJsonParser *jsonParser = [SBJsonParser new];
View
0 JSON/SBJsonBase.h 100644 → 100755
No changes.
View
0 JSON/SBJsonBase.m 100644 → 100755
No changes.
View
55 JSON/SBJsonParser.h 100644 → 100755
@@ -31,26 +31,6 @@
#import "SBJsonBase.h"
/**
- @brief Options for the parser class.
-
- This exists so the SBJSON facade can implement the options in the parser without having to re-declare them.
- */
-@protocol SBJsonParser
-
-/**
- @brief Return the object represented by the given string.
-
- Returns the object represented by the passed-in string or nil on error. The returned object can be
- a string, number, boolean, null, array or dictionary.
-
- @param repr the json string to parse
- */
-- (id)objectWithString:(NSString *)repr;
-
-@end
-
-
-/**
@brief The JSON parser class.
JSON is mapped to Objective-C types in the following way:
@@ -60,28 +40,47 @@
@li Array -> NSMutableArray
@li Object -> NSMutableDictionary
@li Boolean -> NSNumber (initialised with -initWithBool:)
- @li Number -> NSDecimalNumber
+ @li Number -> (NSNumber | NSDecimalNumber)
Since Objective-C doesn't have a dedicated class for boolean values, these turns into NSNumber
instances. These are initialised with the -initWithBool: method, and
round-trip back to JSON properly. (They won't silently suddenly become 0 or 1; they'll be
represented as 'true' and 'false' again.)
- JSON numbers turn into NSDecimalNumber instances,
- as we can thus avoid any loss of precision. (JSON allows ridiculously large numbers.)
+ As an optimisation short JSON 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.
*/
-@interface SBJsonParser : SBJsonBase <SBJsonParser> {
+@interface SBJsonParser : SBJsonBase {
@private
const char *c;
}
-@end
+/**
+ @brief Return the object represented by the given string
+
+ Returns the object represented by the passed-in string or nil on error. The returned object can be
+ a string, number, boolean, null, array or dictionary.
+
+ @param repr the json string to parse
+ */
+- (id)objectWithString:(NSString *)repr;
+
+/**
+ @brief Return the object represented by the given string
+
+ Returns the object represented by the passed-in string or nil on error. The returned object can be
+ a string, number, boolean, null, array or dictionary.
+
+ @param jsonText the json string to parse
+ @param error pointer to an NSError object to populate on error
+ */
+
+- (id)objectWithString:(NSString*)jsonText
+ error:(NSError**)error;
+
-// don't use - exists for backwards compatibility with 2.1.x only. Will be removed in 2.3.
-@interface SBJsonParser (Private)
-- (id)fragmentWithString:(id)repr;
@end
View
96 JSON/SBJsonParser.m 100644 → 100755
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2009 Stig Brautaset. All rights reserved.
+ 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:
@@ -67,11 +67,7 @@ + (void)initialize {
ctrl[0x21] = 0;
}
-/**
- @deprecated This exists in order to provide fragment support in older APIs in one more version.
- It should be removed in the next major version.
- */
-- (id)fragmentWithString:(id)repr {
+- (id)objectWithString:(NSString *)repr {
[self clearErrorTrace];
if (!repr) {
@@ -92,26 +88,29 @@ - (id)fragmentWithString:(id)repr {
[self addErrorWithCode:ETRAILGARBAGE description:@"Garbage after JSON"];
return nil;
}
-
+
NSAssert1(o, @"Should have a valid object from %@", repr);
- return o;
-}
-
-- (id)objectWithString:(NSString *)repr {
-
- id o = [self fragmentWithString:repr];
- if (!o)
- return nil;
// Check that the object we've found is a valid JSON container.
if (![o isKindOfClass:[NSDictionary class]] && ![o isKindOfClass:[NSArray class]]) {
[self addErrorWithCode:EFRAGMENT description:@"Valid fragment, but not JSON"];
return nil;
}
-
+
return o;
}
+- (id)objectWithString:(NSString*)repr error:(NSError**)error {
+ id tmp = [self objectWithString:repr];
+ if (tmp)
+ return tmp;
+
+ if (error)
+ *error = [self.errorTrace lastObject];
+ return nil;
+}
+
+
/*
In contrast to the public methods, it is an error to omit the error parameter here.
*/
@@ -156,7 +155,6 @@ - (BOOL)scanValue:(NSObject **)o
return NO;
break;
}
-
NSAssert(0, @"Should never get here");
return NO;
}
@@ -285,11 +283,20 @@ - (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o
- (BOOL)scanRestOfString:(NSMutableString **)o
{
+ // if the string has no control characters in it, return it in one go, without any temporary allocations.
+ size_t len = strcspn(c, ctrl);
+ if (len && *(c + len) == '\"')
+ {
+ *o = [[[NSMutableString alloc] initWithBytes:(char*)c length:len encoding:NSUTF8StringEncoding] autorelease];
+ c += len + 1;
+ return YES;
+ }
+
*o = [NSMutableString stringWithCapacity:16];
do {
// First see if there's a portion we can grab in one go.
// Doing this caused a massive speedup on the long string.
- size_t len = strcspn(c, ctrl);
+ len = strcspn(c, ctrl);
if (len) {
// check for
id t = [[NSString alloc] initWithBytesNoCopy:(char*)c
@@ -406,6 +413,8 @@ - (BOOL)scanHexQuad:(unichar *)x
- (BOOL)scanNumber:(NSNumber **)o
{
+ BOOL simple = YES;
+
const char *ns = c;
// The logic to test for validity of the number formatting is relicensed
@@ -431,7 +440,7 @@ - (BOOL)scanNumber:(NSNumber **)o
// Fractional part
if ('.' == *c && c++) {
-
+ simple = NO;
if (!isdigit(*c)) {
[self addErrorWithCode:EPARSENUM description: @"No digits after decimal point"];
return NO;
@@ -441,6 +450,7 @@ - (BOOL)scanNumber:(NSNumber **)o
// Exponential part
if ('e' == *c || 'E' == *c) {
+ simple = NO;
c++;
if ('-' == *c || '+' == *c)
@@ -453,16 +463,46 @@ - (BOOL)scanNumber:(NSNumber **)o
skipDigits(c);
}
- id str = [[NSString alloc] initWithBytesNoCopy:(char*)ns
- length:c - ns
- encoding:NSUTF8StringEncoding
- freeWhenDone:NO];
- [str autorelease];
- if (str && (*o = [NSDecimalNumber decimalNumberWithString:str]))
+ // If we are only reading integers, don't go through the expense of creating an NSDecimal.
+ // This ends up being a very large perf win.
+ if (simple) {
+ BOOL negate = NO;
+ long long val = 0;
+ const char *d = ns;
+
+ if (*d == '-') {
+ negate = YES;
+ d++;
+ }
+
+ while (isdigit(*d)) {
+ val *= 10;
+ if (val < 0)
+ goto longlong_overflow;
+ val += *d - '0';
+ if (val < 0)
+ goto longlong_overflow;
+ d++;
+ }
+
+ *o = [NSNumber numberWithLongLong:negate ? -val : val];
return YES;
-
- [self addErrorWithCode:EPARSENUM description: @"Failed creating decimal instance"];
- return NO;
+
+ } else {
+ // jumped to by simple branch, if an overflow occured
+ longlong_overflow:;
+
+ id str = [[NSString alloc] initWithBytesNoCopy:(char*)ns
+ length:c - ns
+ encoding:NSUTF8StringEncoding
+ freeWhenDone:NO];
+ [str autorelease];
+ if (str && (*o = [NSDecimalNumber decimalNumberWithString:str]))
+ return YES;
+
+ [self addErrorWithCode:EPARSENUM description: @"Failed creating decimal instance"];
+ return NO;
+ }
}
- (BOOL)scanIsAtEnd
Oops, something went wrong.

0 comments on commit 0ef76c4

Please sign in to comment.