PageRenderTime 3ms CodeModel.GetById 2ms app.highlight 161ms RepoModel.GetById 1ms app.codeStats 0ms

/build/iphone/Classes/SBJSON.m

https://bitbucket.org/crack_oso/local-storage
Objective C | 948 lines | 749 code | 131 blank | 68 comment | 226 complexity | e35cee365e3be0a7979cf0b243a2814d MD5 | raw file
  1/*
  2 Copyright (C) 2008 Stig Brautaset. All rights reserved.
  3 
  4 Redistribution and use in source and binary forms, with or without
  5 modification, are permitted provided that the following conditions are met:
  6 
  7 Redistributions of source code must retain the above copyright notice, this
  8 list of conditions and the following disclaimer.
  9 
 10 Redistributions in binary form must reproduce the above copyright notice,
 11 this list of conditions and the following disclaimer in the documentation
 12 and/or other materials provided with the distribution.
 13 
 14 Neither the name of the author nor the names of its contributors may be used
 15 to endorse or promote products derived from this software without specific
 16 prior written permission.
 17 
 18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 19 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 20 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 22 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 23 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 25 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 26 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28 */
 29
 30#import "SBJSON.h"
 31#import "TiProxy.h"
 32#import "TiHost.h"
 33
 34NSString * TI_SBJSONErrorDomain = @"org.brautaset.JSON.ErrorDomain";
 35
 36@interface SBJSON (Generator)
 37
 38- (BOOL)appendValue:(id)fragment into:(NSMutableString*)json error:(NSError**)error;
 39- (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json error:(NSError**)error;
 40- (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json error:(NSError**)error;
 41- (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json error:(NSError**)error;
 42
 43- (NSString*)indent;
 44
 45@end
 46
 47@interface SBJSON (Scanner)
 48
 49- (BOOL)scanValue:(NSObject **)o error:(NSError **)error;
 50
 51- (BOOL)scanRestOfArray:(NSMutableArray **)o error:(NSError **)error;
 52- (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o error:(NSError **)error;
 53- (BOOL)scanRestOfNull:(NSNull **)o error:(NSError **)error;
 54- (BOOL)scanRestOfFalse:(NSNumber **)o error:(NSError **)error;
 55- (BOOL)scanRestOfTrue:(NSNumber **)o error:(NSError **)error;
 56- (BOOL)scanRestOfString:(NSMutableString **)o error:(NSError **)error;
 57- (BOOL)scanRestOfEncodedString:(NSMutableString **)o error:(NSError **)error;
 58
 59// Cannot manage without looking at the first digit
 60- (BOOL)scanNumber:(NSNumber **)o error:(NSError **)error;
 61
 62- (BOOL)scanHexQuad:(unichar *)x error:(NSError **)error;
 63- (BOOL)scanUnicodeChar:(unichar *)x error:(NSError **)error;
 64
 65- (BOOL)scanIsAtEnd;
 66
 67@end
 68
 69#pragma mark Private utilities
 70
 71#define skipWhitespace(c) while (isspace(*c)) c++
 72#define skipDigits(c) while (isdigit(*c)) c++
 73
 74static NSError *err(int code, NSString *str) {
 75    NSDictionary *ui = [NSDictionary dictionaryWithObject:str forKey:NSLocalizedDescriptionKey];
 76    return [NSError errorWithDomain:TI_SBJSONErrorDomain code:code userInfo:ui];
 77}
 78
 79static NSError *errWithUnderlier(int code, NSError **u, NSString *str) {
 80    if (!u)
 81        return err(code, str);
 82    
 83    NSDictionary *ui = [NSDictionary dictionaryWithObjectsAndKeys:
 84                        str, NSLocalizedDescriptionKey,
 85                        *u, NSUnderlyingErrorKey,
 86                        nil];
 87    return [NSError errorWithDomain:TI_SBJSONErrorDomain code:code userInfo:ui];
 88}
 89
 90
 91@implementation SBJSON
 92
 93static char ctrl[0x24];
 94
 95+ (void)initialize
 96{
 97    ctrl[0] = '\"';
 98    ctrl[1] = '\\';
 99    for (int i = 1; i < 0x20; i++)
100		ctrl[i+1] = i;
101    ctrl[0x21] = 0;    
102}
103
104+ (id)decodeUrlQuery:(NSURL *) inputUrl;
105{
106	NSString * queryString = [inputUrl query];
107	if([queryString length]>0){
108		id result;  
109		NSString *urlString = [inputUrl absoluteString];
110		// we need to manually pull out the query string since the way we're encoding from JS
111		// makes some cases not pull our query part correctly but this seems to work at all times
112		NSRange range = [urlString rangeOfString:@"?"];
113		NSString *prequery = range.location!=NSNotFound ? [urlString substringFromIndex:range.location+1] : urlString;
114		NSString *query = [prequery stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
115		queryString = [NSString stringWithFormat:@"[%@]",query];
116		SBJSON * jsonDecoder = [[SBJSON alloc] init];
117		NSError * error = nil;
118		result = [jsonDecoder fragmentWithString:queryString error:&error];
119		if (error != nil)
120		{
121			// we have to check for the interior values of the JSON object which can have " in them and the 
122			// above decoding will also decode them, which we need to rescape before we evaluate as JSON! 
123			range = [query rangeOfString:@","];
124			if (range.location!=NSNotFound)
125			{
126				NSString *before = [query substringToIndex:range.location];
127				NSString *after = [query substringFromIndex:range.location+1];
128				if ([after hasPrefix:@"\""])
129				{
130					NSString *contents = [after substringFromIndex:1];
131					contents = [contents substringToIndex:[contents length]-1];
132					// replace them " which got decoded above
133					contents = [contents stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
134					queryString = [NSString stringWithFormat:@"[%@,\"%@\"]",before,contents];
135					
136					result = [jsonDecoder fragmentWithString:queryString error:&error];
137					if (result!=nil && error!=nil)
138					{
139						[jsonDecoder release];
140						return result;
141					}
142				}
143			}
144			
145			
146			// ATTEMPT TO FIGURE OUT WHAT WENT WRONG
147			DebugLog(@"[DEBUG] QUERY URL = %@",inputUrl);
148			DebugLog(@"[DEBUG] QUERY STRING = %@",queryString);
149			DebugLog(@"[DEBUG] QUERY STRING PRE-ESCAPED = %@",prequery);
150			DebugLog(@"[DEBUG] QUERY STRING ESCAPED = %@",query);
151			DebugLog(@"[ERROR] Error in decodeUrlQuery(%@): %@",queryString,error);
152		}
153		[jsonDecoder release];
154		return result;
155	}
156	return nil;	
157}
158
159+ (NSString *) stringify: (id) inputObject;
160{
161	NSError * error = nil;
162	SBJSON * stringer = [[SBJSON alloc] init];
163	NSString * result = [stringer stringWithFragment:inputObject error:&error];
164	[stringer release];
165	if (error != nil) {
166		DebugLog(@"[ERROR] Error in stringify(%@): %@",inputObject,error);
167	}
168	return result;
169}
170
171- (id)init {
172    if (self = [super init]) {
173        [self setMaxDepth:512];
174    }
175    return self;
176}
177
178#pragma mark Generator
179
180
181/**
182 Returns a string containing JSON representation of the passed in value, or nil on error.
183 If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error.
184 
185 @param value any instance that can be represented as a JSON fragment
186 @param allowScalar wether to return json fragments for scalar objects
187 @param error used to return an error by reference (pass NULL if this is not desired)
188 */
189- (NSString*)stringWithObject:(id)value allowScalar:(BOOL)allowScalar error:(NSError**)error {
190    depth = 0;
191    NSMutableString *json = [NSMutableString stringWithCapacity:128];
192    
193    NSError *err2 = nil;
194    if (!allowScalar && ![value isKindOfClass:[NSDictionary class]] && ![value isKindOfClass:[NSArray class]]) {
195        err2 = err(EFRAGMENT, @"Not valid type for JSON");        
196        
197    } else if ([self appendValue:value into:json error:&err2]) {
198        return json;
199    }
200    
201    if (error)
202        *error = err2;
203    return nil;
204}
205
206/**
207 Returns a string containing JSON representation of the passed in value, or nil on error.
208 If nil is returned and @p error is not NULL, @p error can be interrogated to find the cause of the error.
209 
210 @param value any instance that can be represented as a JSON fragment
211 @param error used to return an error by reference (pass NULL if this is not desired)
212 */
213- (NSString*)stringWithFragment:(id)value error:(NSError**)error {
214    return [self stringWithObject:value allowScalar:YES error:error];
215}
216
217/**
218 Returns a string containing JSON representation of the passed in value, or nil on error.
219 If nil is returned and @p error is not NULL, @p error can be interrogated to find the cause of the error.
220 
221 @param value a NSDictionary or NSArray instance
222 @param error used to return an error by reference (pass NULL if this is not desired)
223 */
224- (NSString*)stringWithObject:(id)value error:(NSError**)error {
225    return [self stringWithObject:value allowScalar:NO error:error];
226}
227
228
229- (NSString*)indent {
230    return [@"\n" stringByPaddingToLength:1 + 2 * depth withString:@" " startingAtIndex:0];
231}
232
233// SPT: We need this so that we can 'skip' over proxies, etc. in dictionaries and arrays by
234// performing a limited amount of lookahead.  Ridiculous, yes...
235-(BOOL)supportedFragment:(id)fragment
236{
237	return ([fragment isKindOfClass:[NSDictionary class]] || [fragment isKindOfClass:[NSArray class]] ||
238			[fragment isKindOfClass:[NSString class]] || [fragment isKindOfClass:[NSNumber class]] ||
239			[fragment isKindOfClass:[NSDate class]] || [fragment isKindOfClass:[NSNull class]] ||
240			fragment == nil);
241}
242
243- (BOOL)appendValue:(id)fragment into:(NSMutableString*)json error:(NSError**)error {
244    if ([fragment isKindOfClass:[NSDictionary class]]) {
245        if (![self appendDictionary:fragment into:json error:error])
246            return NO;
247        
248    } else if ([fragment isKindOfClass:[NSArray class]]) {
249        if (![self appendArray:fragment into:json error:error])
250            return NO;
251		
252    } else if ([fragment isKindOfClass:[NSString class]]) {
253        if (![self appendString:fragment into:json error:error])
254            return NO;
255		
256    } else if ([fragment isKindOfClass:[NSNumber class]]) {
257        if ('c' == *[fragment objCType])
258            [json appendString:[fragment boolValue] ? @"true" : @"false"];
259        else
260            [json appendString:[fragment stringValue]];
261		
262    } else if ([fragment isKindOfClass:[NSDate class]]) {
263        [json appendFormat:@"new Date(%f)",[(NSDate *)fragment timeIntervalSince1970]*1000.0];
264		
265    } else if ([fragment isKindOfClass:[NSNull class]] || (fragment == nil)) {
266        [json appendString:@"null"];
267    }
268    return YES;
269}
270
271- (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json error:(NSError**)error {
272    [json appendString:@"["];
273    depth++;
274    
275    BOOL addComma = NO;    
276    for (id value in fragment) {
277		if (![self supportedFragment:value]) {
278			continue;
279		}
280		
281        if (addComma)
282            [json appendString:@","];
283        else
284            addComma = YES;
285		
286        if ([self humanReadable])
287            [json appendString:[self indent]];
288        
289        if (![self appendValue:value into:json error:error]) {
290            return NO;
291        }
292    }
293	
294    depth--;
295    if ([self humanReadable] && [fragment count])
296        [json appendString:[self indent]];
297    [json appendString:@"]"];
298    return YES;
299}
300
301- (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json error:(NSError**)error {
302    [json appendString:@"{"];
303    depth++;
304	
305    NSString *colon = [self humanReadable] ? @" : " : @":";
306    BOOL addComma = NO;
307    NSArray *keys = [fragment allKeys];
308    if (self.sortKeys)
309        keys = [keys sortedArrayUsingSelector:@selector(compare:)];
310    
311    for (id key in keys) {
312		id value = [fragment objectForKey:key];
313		if (![self supportedFragment:value]) {
314			continue;
315		}
316		
317        if (addComma)
318            [json appendString:@","];
319        else
320            addComma = YES;
321		
322        if ([self humanReadable])
323            [json appendString:[self indent]];
324        
325        if (![key isKindOfClass:[NSString class]]) {
326            if(error) *error = err(EUNSUPPORTED, @"JSON object key must be string");
327            return NO;
328        }
329        
330        if (![self appendString:key into:json error:error])
331            return NO;
332		
333        [json appendString:colon];
334        if (![self appendValue:value into:json error:error]) {
335            if(error) *error = err(EUNSUPPORTED, [NSString stringWithFormat:@"Unsupported value for key %@ in object", value]);
336            return NO;
337        }
338    }
339	
340    depth--;
341    if ([self humanReadable] && [fragment count])
342        [json appendString:[self indent]];
343    [json appendString:@"}"];
344    return YES;    
345}
346
347- (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json error:(NSError**)error {
348	
349    static NSMutableCharacterSet *kEscapeChars;
350    if( ! kEscapeChars ) {
351        kEscapeChars = [[NSMutableCharacterSet characterSetWithRange: NSMakeRange(0,32)] retain];
352        [kEscapeChars addCharactersInString: @"\"\\"];
353    }
354    
355    [json appendString:@"\""];
356    
357    NSRange esc = [fragment rangeOfCharacterFromSet:kEscapeChars];
358    if ( !esc.length ) {
359        // No special chars -- can just add the raw string:
360        [json appendString:fragment];
361        
362    } else {
363        NSUInteger length = [fragment length];
364        for (NSUInteger i = 0; i < length; i++) {
365            unichar uc = [fragment characterAtIndex:i];
366            switch (uc) {
367                case '"':   [json appendString:@"\\\""];       break;
368                case '\\':  [json appendString:@"\\\\"];       break;
369                case '\t':  [json appendString:@"\\t"];        break;
370                case '\n':  [json appendString:@"\\n"];        break;
371                case '\r':  [json appendString:@"\\r"];        break;
372                case '\b':  [json appendString:@"\\b"];        break;
373                case '\f':  [json appendString:@"\\f"];        break;
374                default:    
375                    if (uc < 0x20) {
376                        [json appendFormat:@"\\u%04x", uc];
377                    } else {
378                        [json appendFormat:@"%C", uc];
379                    }
380                    break;
381                    
382            }
383        }
384    }
385	
386    [json appendString:@"\""];
387    return YES;
388}
389
390#pragma mark Parser
391
392/**
393 Returns the object represented by the passed-in string or nil on error. The returned object can be
394 a string, number, boolean, null, array or dictionary.
395 
396 @param repr the json string to parse
397 @param allowScalar whether to return objects for JSON fragments
398 @param error used to return an error by reference (pass NULL if this is not desired)
399 */
400- (id)objectWithString:(id)repr allowScalar:(BOOL)allowScalar error:(NSError**)error {
401	
402    if (!repr) {
403        if (error)
404            *error = err(EINPUT, @"Input was 'nil'");
405        return nil;
406    }
407    
408    depth = 0;
409    c = [repr UTF8String];
410    
411    id o;
412    NSError *err2 = nil;
413    if (![self scanValue:&o error:&err2]) {
414        if (error)
415            *error = err2;
416        return nil;
417    }
418	
419    // We found some valid JSON. But did it also contain something else?
420    if (![self scanIsAtEnd]) {
421        if (error)
422            *error = err(ETRAILGARBAGE, @"Garbage after JSON");
423        return nil;
424    }
425	
426    // If we don't allow scalars, check that the object we've found is a valid JSON container.
427    if (!allowScalar && ![o isKindOfClass:[NSDictionary class]] && ![o isKindOfClass:[NSArray class]]) {
428        if (error)
429            *error = err(EFRAGMENT, @"Valid fragment, but not JSON");
430        return nil;
431    }
432	
433    NSAssert1(o, @"Should have a valid object from %@", repr);
434    return o;
435}
436
437/**
438 Returns the object represented by the passed-in string or nil on error. The returned object can be
439 a string, number, boolean, null, array or dictionary.
440 
441 @param repr the json string to parse
442 @param error used to return an error by reference (pass NULL if this is not desired)
443 */
444- (id)fragmentWithString:(NSString*)repr error:(NSError**)error {
445    return [self objectWithString:repr allowScalar:YES error:error];
446}
447
448/**
449 Returns the object represented by the passed-in string or nil on error. The returned object
450 will be either a dictionary or an array.
451 
452 @param repr the json string to parse
453 @param error used to return an error by reference (pass NULL if this is not desired)
454 */
455- (id)objectWithString:(NSString*)repr error:(NSError**)error {
456    return [self objectWithString:repr allowScalar:NO error:error];
457}
458
459/*
460 In contrast to the public methods, it is an error to omit the error parameter here.
461 */
462- (BOOL)scanValue:(NSObject **)o error:(NSError **)error
463{
464	
465	skipWhitespace(c);
466	
467	BOOL result;	
468	char ch = *c++;
469	
470    switch (ch) {
471        case '{':
472            result = [self scanRestOfDictionary:(NSMutableDictionary **)o error:error];
473			return result;
474            break;
475        case '[':
476            return [self scanRestOfArray:(NSMutableArray **)o error:error];
477            break;
478        case '<':
479            return [self scanRestOfEncodedString:(NSMutableString **)o error:error];
480            break;			
481        case '"':
482            return [self scanRestOfString:(NSMutableString **)o error:error];
483            break;
484        case 'f':
485            return [self scanRestOfFalse:(NSNumber **)o error:error];
486            break;
487        case 't':
488            return [self scanRestOfTrue:(NSNumber **)o error:error];
489            break;
490        case 'n':
491			if (!strncmp(c, "ull", 3)) {
492				c += 3;
493				*o = [NSNull null];
494				return YES;
495			}
496			if (!strncmp(c, "ew Date(", 8)){
497				c += 8;
498				NSNumber * dateMillis;
499				if(![self scanNumber:&dateMillis error:error]){
500					return NO;
501				}
502				if(*c++ != ')'){
503					if(error) *error = err(EPARSE, @"new Date is missing trailing parens");
504					return NO;
505				}
506				*o = [NSDate dateWithTimeIntervalSince1970:[dateMillis floatValue]/1000.0];
507				return YES;
508			}
509			if(error) *error = err(EPARSE, @"neither null nor new now");
510			return NO;
511			break;
512        case '-':
513        case '0'...'9':
514            c--; // cannot verify number correctly without the first character
515            return [self scanNumber:(NSNumber **)o error:error];
516            break;
517        case '+':
518            if(error) *error = err(EPARSENUM, @"Leading + disallowed in number");
519            return NO;
520            break;
521        case 0x0:
522            if(error) *error = err(EEOF, @"Unexpected end of string");
523            return NO;
524            break;
525        default:
526            if(error) *error = err(EPARSE, [NSString stringWithFormat:@"Unrecognised leading character '%c'",ch]);
527            return NO;
528            break;
529    }
530    
531    NSAssert(0, @"Should never get here");
532    return NO;
533}
534
535- (BOOL)scanRestOfTrue:(NSNumber **)o error:(NSError **)error
536{
537    if (!strncmp(c, "rue", 3)) {
538        c += 3;
539        *o = [NSNumber numberWithBool:YES];
540        return YES;
541    }
542    if(error) *error = err(EPARSE, @"Expected 'true'");
543    return NO;
544}
545
546- (BOOL)scanRestOfFalse:(NSNumber **)o error:(NSError **)error
547{
548    if (!strncmp(c, "alse", 4)) {
549        c += 4;
550        *o = [NSNumber numberWithBool:NO];
551        return YES;
552    }
553    if(error) *error = err(EPARSE, @"Expected 'false'");
554    return NO;
555}
556
557- (BOOL)scanRestOfNull:(NSNull **)o error:(NSError **)error
558{
559    if (!strncmp(c, "ull", 3)) {
560        c += 3;
561        *o = [NSNull null];
562        return YES;
563    }
564    if(error) *error = err(EPARSE, @"Expected 'null'");
565    return NO;
566}
567
568- (BOOL)scanRestOfArray:(NSMutableArray **)o error:(NSError **)error
569{
570    if (maxDepth && ++depth > maxDepth) {
571        if(error) *error = err(EDEPTH, @"Nested too deep");
572        return NO;
573    }
574    
575    *o = [NSMutableArray arrayWithCapacity:8];
576    
577    for (; *c ;) {
578        id v;
579        
580        skipWhitespace(c);
581        if (*c == ']' && c++) {
582            depth--;
583            return YES;
584        }
585        
586        if (![self scanValue:&v error:error]) {
587			if(error){
588				DeveloperLog(@"[DEBUG] Error in parser: %@",*error);
589				*error = errWithUnderlier(EPARSE, error, @"Expected value while parsing array");
590			}
591            return NO;
592        }
593        
594        [*o addObject:v];
595        
596        skipWhitespace(c);
597        if (*c == ',' && c++) {
598            skipWhitespace(c);
599            if (*c == ']') {
600                if(error) *error = err(ETRAILCOMMA, @"Trailing comma disallowed in array");
601                return NO;
602            }
603        }        
604    }
605    
606    if(error) *error = err(EEOF, @"End of input while parsing array");
607    return NO;
608}
609
610- (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o error:(NSError **)error
611{
612    if (maxDepth && ++depth > maxDepth) {
613        if(error) *error = err(EDEPTH, @"Nested too deep");
614        return NO;
615    }
616    
617    *o = [NSMutableDictionary dictionaryWithCapacity:7];
618    
619    for (; *c ;) {
620        id k, v;
621        
622        skipWhitespace(c);
623        if (*c == '}' && c++) {
624            depth--;
625            return YES;
626        }    
627        
628		if(*c=='<'){
629			c++;
630			if(![self scanRestOfEncodedString:&k error:error]){
631				return NO;
632			}
633		} else if (!(*c == '\"' && c++ && [self scanRestOfString:&k error:error])) {
634            if(error) *error = errWithUnderlier(EPARSE, error, @"Object key string expected");
635            return NO;
636        }
637        
638        skipWhitespace(c);
639        if (*c != ':') {
640            if(error) *error = err(EPARSE, @"Expected ':' separating key and value");
641            return NO;
642        }
643        
644        c++;
645        if (![self scanValue:&v error:error]) {
646            NSString *string = [NSString stringWithFormat:@"Object value expected for key: %@", k];
647            if(error) *error = errWithUnderlier(EPARSE, error, string);
648            return NO;
649        }
650        
651        [*o setObject:v forKey:k];
652		
653        skipWhitespace(c);
654        if (*c == ',' && c++) {
655            skipWhitespace(c);
656            if (*c == '}') {
657                if(error) *error = err(ETRAILCOMMA, @"Trailing comma disallowed in object");
658                return NO;
659            }
660        }        
661    }
662    
663    if(error) *error = err(EEOF, @"End of input while parsing object");
664    return NO;
665}
666
667- (BOOL)scanRestOfEncodedString:(NSMutableString **)o error:(NSError **)error;
668{
669	*o = [NSMutableString stringWithCapacity:16];
670	
671#define BUFFY_SIZE 16
672	char buffy[BUFFY_SIZE];
673	int len=0;
674	char thisChar;
675	
676    do {
677		thisChar = *c++;
678		
679		BOOL isEnd = thisChar=='>';
680		
681		if((isEnd && (len>0)) || (len>=BUFFY_SIZE))
682		{
683			NSString * nextSegment = [[NSString alloc] initWithBytesNoCopy:buffy
684																	length:len encoding:NSUTF8StringEncoding freeWhenDone:NO];
685			if(nextSegment != nil){
686				[*o appendString:nextSegment];
687				[nextSegment release];
688			} else {
689				if(error) *error = err(EEOF, @"[ERROR] Invalid UTF-8 while parsing encoded string");
690				return NO;
691			}
692			len = 0;
693		}
694		
695		if(isEnd){
696			return YES;
697		}
698		
699		// we need to compensate for unicode encoded for higher order characters like700		if (thisChar == '\\')
701		{
702            unichar uc = *++c;
703			if (![self scanUnicodeChar:&uc error:error]) {
704				if(error) *error = errWithUnderlier(EUNICODE, error, @"Broken unicode character");
705				return NO;
706			}
707			if (len>0)
708			{
709				NSString * nextSegment = [[NSString alloc] initWithBytesNoCopy:buffy length:len encoding:NSUTF8StringEncoding freeWhenDone:NO];
710				if (nextSegment!=nil)
711				{
712					[*o appendString:nextSegment];
713				}
714				[nextSegment release];
715			}
716			[*o appendString:[NSString stringWithFormat:@"%C",uc]];
717			len = 0;
718			continue;
719		}
720		
721		if((thisChar >= '0') && (thisChar <= '9')){
722			buffy[len] = (thisChar - '0') << 4;
723		} else if ((thisChar >= 'a') && (thisChar <= 'f')){
724			buffy[len] = (thisChar - 'a' + 10) << 4;
725		} else if ((thisChar >= 'A') && (thisChar <= 'F')){
726			buffy[len] = (thisChar - 'A' + 10) << 4;
727		} else {
728			if(error) *error = err(EEOF, @"[ERROR] Non-hexcode while parsing encoded string");
729			return NO;
730		}
731		
732		thisChar = *c++;
733		
734		if((thisChar >= '0') && (thisChar <= '9')){
735			buffy[len] += (thisChar - '0');
736		} else if ((thisChar >= 'a') && (thisChar <= 'f')){
737			buffy[len] += (thisChar - 'a' + 10);
738		} else if ((thisChar >= 'A') && (thisChar <= 'F')){
739			buffy[len] += (thisChar - 'A' + 10);
740		} else {
741			if(error) *error = err(EEOF, @"[ERROR] Non-hexcode while parsing encoded string");
742			return NO;
743		}
744		len++;
745    } while (*c);
746    
747    if(error) *error = err(EEOF, @"[ERROR] Unexpected EOF while parsing encoded string");
748    return NO;
749}
750
751
752- (BOOL)scanRestOfString:(NSMutableString **)o error:(NSError **)error
753{
754    *o = [NSMutableString stringWithCapacity:16];
755    do {
756        // First see if there's a portion we can grab in one go. 
757        // Doing this caused a massive speedup on the long string.
758        size_t len = strcspn(c, ctrl);
759        if (len) {
760            // check for 
761            id t = [[NSString alloc] initWithBytesNoCopy:(char*)c
762                                                  length:len
763                                                encoding:NSUTF8StringEncoding
764                                            freeWhenDone:NO];
765            if (t) {
766                [*o appendString:t];
767                [t release];
768                c += len;
769            }
770        }
771        
772        if (*c == '"') {
773            c++;
774            return YES;
775            
776        } else if (*c == '\\') {
777            unichar uc = *++c;
778            switch (uc) {
779                case '\\':
780                case '/':
781                case '"':
782                    break;
783                    
784                case 'b':   uc = '\b';  break;
785                case 'n':   uc = '\n';  break;
786                case 'r':   uc = '\r';  break;
787                case 't':   uc = '\t';  break;
788                case 'f':   uc = '\f';  break;                    
789                    
790                case 'u':case 'U':
791                    c++;
792                    if (![self scanUnicodeChar:&uc error:error]) {
793                        if(error) *error = errWithUnderlier(EUNICODE, error, @"Broken unicode character");
794                        return NO;
795                    }
796                    c--; // hack.
797                    break;
798                default:
799                    if(error) *error = err(EESCAPE, [NSString stringWithFormat:@"Illegal escape sequence '0x%x'", uc]);
800                    return NO;
801                    break;
802            }
803            [*o appendFormat:@"%C", uc];
804            c++;
805            
806        } else if (*c < 0x20) {
807            if(error) *error = err(ECTRL, [NSString stringWithFormat:@"Unescaped control character '0x%x'", *c]);
808            return NO;
809            
810        } else {
811            DeveloperLog(@"[ERROR] Should not be able to get here in SBJSON.m");
812        }
813    } while (*c);
814    
815    if(error) *error = err(EEOF, @"[ERROR] Unexpected EOF while parsing string");
816    return NO;
817}
818
819- (BOOL)scanUnicodeChar:(unichar *)x error:(NSError **)error
820{
821    unichar hi, lo;
822    
823    if (![self scanHexQuad:&hi error:error]) {
824        if(error) *error = err(EUNICODE, @"Missing hex quad");
825        return NO;        
826    }
827    
828    if (hi >= 0xd800) {     // high surrogate char?
829        if (hi < 0xdc00) {  // yes - expect a low char
830            
831            if (!(*c == '\\' && ++c && *c == 'u' && ++c && [self scanHexQuad:&lo error:error])) {
832                if(error) *error = errWithUnderlier(EUNICODE, error, @"Missing low character in surrogate pair");
833                return NO;
834            }
835            
836            if (lo < 0xdc00 || lo >= 0xdfff) {
837                if(error) *error = err(EUNICODE, @"Invalid low surrogate char");
838                return NO;
839            }
840            
841            hi = (hi - 0xd800) * 0x400 + (lo - 0xdc00) + 0x10000;
842            
843        } else if (hi < 0xe000) {
844            if(error) *error = err(EUNICODE, @"Invalid high character in surrogate pair");
845            return NO;
846        }
847    }
848    
849    *x = hi;
850    return YES;
851}
852
853- (BOOL)scanHexQuad:(unichar *)x error:(NSError **)error
854{
855    *x = 0;
856    for (int i = 0; i < 4; i++) {
857        unichar uc = *c;
858        c++;
859        int d = (uc >= '0' && uc <= '9')
860        ? uc - '0' : (uc >= 'a' && uc <= 'f')
861        ? (uc - 'a' + 10) : (uc >= 'A' && uc <= 'F')
862        ? (uc - 'A' + 10) : -1;
863        if (d == -1) {
864            if(error) *error = err(EUNICODE, @"Missing hex digit in quad");
865            return NO;
866        }
867        *x *= 16;
868        *x += d;
869    }
870    return YES;
871}
872
873- (BOOL)scanNumber:(NSNumber **)o error:(NSError **)error
874{
875    const char *ns = c;
876    
877    // The logic to test for validity of the number formatting is relicensed
878    // from JSON::XS with permission from its author Marc Lehmann.
879    // (Available at the CPAN: http://search.cpan.org/dist/JSON-XS/ .)
880    
881    if ('-' == *c)
882        c++;
883    
884    if ('0' == *c && c++) {        
885        if (isdigit(*c)) {
886            if(error) *error = err(EPARSENUM, @"Leading 0 disallowed in number");
887            return NO;
888        }
889        
890    } else if (!isdigit(*c) && c != ns) {
891        if(error) *error = err(EPARSENUM, @"No digits after initial minus");
892        return NO;
893        
894    } else {
895        skipDigits(c);
896    }
897    
898    // Fractional part
899    if ('.' == *c && c++) {
900        
901        if (!isdigit(*c)) {
902            if(error) *error = err(EPARSENUM, @"No digits after decimal point");
903            return NO;
904        }        
905        skipDigits(c);
906    }
907    
908    // Exponential part
909    if ('e' == *c || 'E' == *c) {
910        c++;
911        
912        if ('-' == *c || '+' == *c)
913            c++;
914        
915        if (!isdigit(*c)) {
916            if(error) *error = err(EPARSENUM, @"No digits after exponent");
917            return NO;
918        }
919        skipDigits(c);
920    }
921    
922    id str = [[NSString alloc] initWithBytesNoCopy:(char*)ns
923                                            length:c - ns
924                                          encoding:NSUTF8StringEncoding
925                                      freeWhenDone:NO];
926    [str autorelease];
927    if (str && (*o = [NSDecimalNumber decimalNumberWithString:str]))
928        return YES;
929    
930    if(error) *error = err(EPARSENUM, @"Failed creating decimal instance");
931    return NO;
932}
933
934- (BOOL)scanIsAtEnd
935{
936    skipWhitespace(c);
937    return !*c;
938}
939
940
941
942#pragma mark Properties
943
944@synthesize humanReadable;
945@synthesize sortKeys;
946@synthesize maxDepth;
947
948@end