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