oj 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of oj might be problematic. Click here for more details.

@@ -40,9 +40,17 @@ enum {
40
40
  TIME_HINT = 0x0100,
41
41
  };
42
42
 
43
+ typedef struct _CircArray {
44
+ VALUE obj_array[1024];
45
+ VALUE *objs;
46
+ unsigned long size; // allocated size or initial array size
47
+ unsigned long cnt;
48
+ } *CircArray;
49
+
43
50
  typedef struct _ParseInfo {
44
51
  char *str; /* buffer being read from */
45
52
  char *s; /* current position in buffer */
53
+ CircArray circ_array;
46
54
  #ifdef HAVE_RUBY_ENCODING_H
47
55
  rb_encoding *encoding;
48
56
  #else
@@ -51,10 +59,15 @@ typedef struct _ParseInfo {
51
59
  Options options;
52
60
  } *ParseInfo;
53
61
 
62
+ static CircArray circ_array_new(void);
63
+ static void circ_array_free(CircArray ca);
64
+ static void circ_array_set(CircArray ca, VALUE obj, unsigned long id);
65
+ static VALUE circ_array_get(CircArray ca, unsigned long id);
66
+
54
67
  static VALUE classname2class(const char *name, ParseInfo pi);
55
68
  static VALUE read_next(ParseInfo pi, int hint);
56
69
  static VALUE read_obj(ParseInfo pi);
57
- static VALUE read_array(ParseInfo pi);
70
+ static VALUE read_array(ParseInfo pi, int hint);
58
71
  static VALUE read_str(ParseInfo pi, int hint);
59
72
  static VALUE read_num(ParseInfo pi);
60
73
  static VALUE read_time(ParseInfo pi);
@@ -170,29 +183,94 @@ classname2class(const char *name, ParseInfo pi) {
170
183
  return clas;
171
184
  }
172
185
 
173
- VALUE
174
- oj_parse(char *json, Options options) {
175
- VALUE obj;
176
- struct _ParseInfo pi;
186
+ #ifndef NO_RSTRUCT
187
+ inline static VALUE
188
+ structname2obj(const char *name) {
189
+ VALUE ost;
177
190
 
178
- if (0 == json) {
179
- raise_error("Invalid arg, xml string can not be null", json, 0);
180
- }
181
- /* initialize parse info */
182
- pi.str = json;
183
- pi.s = json;
191
+ ost = rb_const_get(oj_struct_class, rb_intern(name));
192
+ // use encoding as the indicator for Ruby 1.8.7 or 1.9.x
184
193
  #ifdef HAVE_RUBY_ENCODING_H
185
- pi.encoding = ('\0' == *options->encoding) ? 0 : rb_enc_find(options->encoding);
194
+ return rb_struct_alloc_noinit(ost);
186
195
  #else
187
- pi.encoding = 0;
196
+ return rb_struct_new(ost);
188
197
  #endif
189
- pi.options = options;
190
- if (Qundef == (obj = read_next(&pi, 0))) {
191
- raise_error("no object read", pi.str, pi.s);
198
+ }
199
+ #endif
200
+
201
+ inline static unsigned long
202
+ read_ulong(const char *s, ParseInfo pi) {
203
+ unsigned long n = 0;
204
+
205
+ for (; '\0' != *s; s++) {
206
+ if ('0' <= *s && *s <= '9') {
207
+ n = n * 10 + (*s - '0');
208
+ } else {
209
+ raise_error("Not a valid ID number", pi->str, pi->s);
210
+ }
192
211
  }
193
- next_non_white(&pi); // skip white space
194
- if ('\0' != *pi.s) {
195
- raise_error("invalid format, extra characters", pi.str, pi.s);
212
+ return n;
213
+ }
214
+
215
+ static CircArray
216
+ circ_array_new() {
217
+ CircArray ca;
218
+
219
+ if (0 == (ca = (CircArray)malloc(sizeof(struct _CircArray)))) {
220
+ rb_raise(rb_eNoMemError, "not enough memory\n");
221
+ }
222
+ ca->objs = ca->obj_array;
223
+ ca->size = sizeof(ca->obj_array) / sizeof(VALUE);
224
+ ca->cnt = 0;
225
+
226
+ return ca;
227
+ }
228
+
229
+ static void
230
+ circ_array_free(CircArray ca) {
231
+ if (ca->objs != ca->obj_array) {
232
+ free(ca->objs);
233
+ }
234
+ free(ca);
235
+ }
236
+
237
+ static void
238
+ circ_array_set(CircArray ca, VALUE obj, unsigned long id) {
239
+ if (0 < id && 0 != ca) {
240
+ unsigned long i;
241
+
242
+ if (ca->size < id) {
243
+ unsigned long cnt = id + 512;
244
+
245
+ if (ca->objs == ca->obj_array) {
246
+ if (0 == (ca->objs = (VALUE*)malloc(sizeof(VALUE) * cnt))) {
247
+ rb_raise(rb_eNoMemError, "not enough memory\n");
248
+ }
249
+ memcpy(ca->objs, ca->obj_array, sizeof(VALUE) * ca->cnt);
250
+ } else {
251
+ if (0 == (ca->objs = (VALUE*)realloc(ca->objs, sizeof(VALUE) * cnt))) {
252
+ rb_raise(rb_eNoMemError, "not enough memory\n");
253
+ }
254
+ }
255
+ ca->size = cnt;
256
+ }
257
+ id--;
258
+ for (i = ca->cnt; i < id; i++) {
259
+ ca->objs[i] = Qnil;
260
+ }
261
+ ca->objs[id] = obj;
262
+ if (ca->cnt <= id) {
263
+ ca->cnt = id + 1;
264
+ }
265
+ }
266
+ }
267
+
268
+ static VALUE
269
+ circ_array_get(CircArray ca, unsigned long id) {
270
+ VALUE obj = Qnil;
271
+
272
+ if (id <= ca->cnt && 0 != ca) {
273
+ obj = ca->objs[id - 1];
196
274
  }
197
275
  return obj;
198
276
  }
@@ -207,7 +285,7 @@ read_next(ParseInfo pi, int hint) {
207
285
  obj = read_obj(pi);
208
286
  break;
209
287
  case '[':
210
- obj = read_array(pi);
288
+ obj = read_array(pi, hint);
211
289
  break;
212
290
  case '"':
213
291
  obj = read_str(pi, hint);
@@ -256,6 +334,7 @@ read_obj(ParseInfo pi) {
256
334
  VALUE val = Qundef;
257
335
  const char *ks;
258
336
  int obj_type = T_NONE;
337
+ const char *json_class_name = 0;
259
338
 
260
339
  pi->s++;
261
340
  next_non_white(pi);
@@ -306,8 +385,24 @@ read_obj(ParseInfo pi) {
306
385
  obj_type = T_OBJECT;
307
386
  key = Qundef;
308
387
  break;
309
- case 'i': // Id for circular reference
310
- // TBD
388
+ case 'u': // Struct
389
+ obj = read_next(pi, T_STRUCT);
390
+ obj_type = T_STRUCT;
391
+ key = Qundef;
392
+ break;
393
+ /*
394
+ case 'r': // Id for circular reference
395
+ val = read_next(pi, T_FIXNUM);
396
+ if (T_FIXNUM == rb_type(val)) {
397
+ obj_type = T_FIXNUM;
398
+ obj = circ_array_get(pi->circ_array, NUM2ULONG(val));
399
+ if (Qundef == obj || Qnil == obj) {
400
+ raise_error("Failed to find referenced object", pi->str, pi->s);
401
+ }
402
+ key = Qundef;
403
+ }
404
+ break;
405
+ */
311
406
  default:
312
407
  // handle later
313
408
  break;
@@ -318,41 +413,55 @@ read_obj(ParseInfo pi) {
318
413
  if (Qundef == val && Qundef == (val = read_next(pi, 0))) {
319
414
  raise_error("unexpected character", pi->str, pi->s);
320
415
  }
321
- if (ObjectMode == pi->options->mode &&
322
- 0 != ks && '^' == *ks && '#' == ks[1] &&
323
- (T_NONE == obj_type || T_HASH == obj_type) &&
324
- rb_type(val) == T_ARRAY && 2 == RARRAY_LEN(val)) { // Hash entry
325
- VALUE *np = RARRAY_PTR(val);
326
-
327
- key = *np;
328
- val = *(np + 1);
329
- }
330
416
  if (Qundef == obj) {
331
417
  obj = rb_hash_new();
332
418
  obj_type = T_HASH;
333
419
  }
334
- if (T_OBJECT == obj_type) {
335
- VALUE *slot;
336
- ID var_id;
337
-
338
- if (Qundef == (var_id = oj_cache_get(oj_attr_cache, ks, &slot))) {
339
- char attr[1024];
340
-
341
- if ('~' == *ks) {
342
- strncpy(attr, ks + 1, sizeof(attr) - 1);
343
- } else {
344
- *attr = '@';
345
- strncpy(attr + 1, ks, sizeof(attr) - 2);
420
+ if (ObjectMode == pi->options->mode && 0 != ks && '^' == *ks) {
421
+ int val_type = rb_type(val);
422
+
423
+ if ('i' == ks[1] && '\0' == ks[2] && T_FIXNUM == val_type) {
424
+ circ_array_set(pi->circ_array, obj, NUM2ULONG(val));
425
+ key = Qundef;
426
+ } else if ('#' == ks[1] &&
427
+ (T_NONE == obj_type || T_HASH == obj_type) &&
428
+ T_ARRAY == val_type && 2 == RARRAY_LEN(val)) { // Hash entry
429
+ VALUE *np = RARRAY_PTR(val);
430
+
431
+ key = *np;
432
+ val = *(np + 1);
433
+ }
434
+ }
435
+ if (Qundef != key) {
436
+ if (T_OBJECT == obj_type) {
437
+ VALUE *slot;
438
+ ID var_id;
439
+
440
+ if (Qundef == (var_id = oj_cache_get(oj_attr_cache, ks, &slot))) {
441
+ char attr[1024];
442
+
443
+ if ('~' == *ks) {
444
+ strncpy(attr, ks + 1, sizeof(attr) - 1);
445
+ } else {
446
+ *attr = '@';
447
+ strncpy(attr + 1, ks, sizeof(attr) - 2);
448
+ }
449
+ attr[sizeof(attr) - 1] = '\0';
450
+ var_id = rb_intern(attr);
451
+ *slot = var_id;
452
+ }
453
+ rb_ivar_set(obj, var_id, val);
454
+ } else if (T_HASH == obj_type) {
455
+ rb_hash_aset(obj, key, val);
456
+ if ((CompatMode == pi->options->mode || ObjectMode == pi->options->mode) &&
457
+ 0 == json_class_name &&
458
+ 0 != ks && 'j' == *ks && 0 == strcmp("json_class", ks) &&
459
+ T_STRING == rb_type(val)) {
460
+ json_class_name = StringValuePtr(val);
346
461
  }
347
- attr[sizeof(attr) - 1] = '\0';
348
- var_id = rb_intern(attr);
349
- *slot = var_id;
462
+ } else {
463
+ raise_error("invalid Object format, too many Hash entries.", pi->str, pi->s);
350
464
  }
351
- rb_ivar_set(obj, var_id, val);
352
- } else if (T_HASH == obj_type) {
353
- rb_hash_aset(obj, key, val);
354
- } else {
355
- raise_error("invalid Object format, too many Hash entries.", pi->str, pi->s);
356
465
  }
357
466
  }
358
467
  next_non_white(pi);
@@ -366,26 +475,69 @@ read_obj(ParseInfo pi) {
366
475
  raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
367
476
  }
368
477
  }
478
+ if (0 != json_class_name) {
479
+ VALUE clas = classname2class(json_class_name, pi);
480
+ VALUE args[1];
481
+
482
+ *args = obj;
483
+ obj = rb_funcall2(clas, oj_json_create_id, 1, args);
484
+ }
369
485
  return obj;
370
486
  }
371
487
 
372
488
  static VALUE
373
- read_array(ParseInfo pi) {
374
- VALUE a = rb_ary_new();
489
+ read_array(ParseInfo pi, int hint) {
490
+ VALUE a = Qundef;
375
491
  VALUE e;
492
+ int type = T_NONE;
493
+ int cnt = 0;
494
+ long slen = 0;
495
+ int a_str;
376
496
 
377
497
  pi->s++;
378
498
  next_non_white(pi);
379
499
  if (']' == *pi->s) {
380
500
  pi->s++;
381
- return a;
501
+ return rb_ary_new();
382
502
  }
383
503
  while (1) {
504
+ next_non_white(pi);
505
+ a_str = ('"' == *pi->s);
384
506
  if (Qundef == (e = read_next(pi, 0))) {
385
507
  raise_error("unexpected character", pi->str, pi->s);
386
508
  }
387
- rb_ary_push(a, e);
388
- next_non_white(pi); // skip white space
509
+ #ifndef NO_RSTRUCT
510
+ if (Qundef == a && T_STRUCT == hint && T_STRING == rb_type(e)) {
511
+ a = structname2obj(StringValuePtr(e));
512
+ type = T_STRUCT;
513
+ slen = RSTRUCT_LEN(a);
514
+ e = Qundef;
515
+ }
516
+ #endif
517
+ if (Qundef == a) {
518
+ a = rb_ary_new();
519
+ type = T_ARRAY;
520
+ }
521
+ if (a_str && T_FIXNUM == rb_type(e)) {
522
+ circ_array_set(pi->circ_array, a, NUM2ULONG(e));
523
+ e = Qundef;
524
+ }
525
+ if (Qundef != e) {
526
+ if (T_STRUCT == type) {
527
+ #ifdef NO_RSTRUCT
528
+ raise_error("Ruby structs not supported with this version of Ruby", pi->str, pi->s);
529
+ #else
530
+ if (slen <= cnt) {
531
+ raise_error("Too many elements for Struct", pi->str, pi->s);
532
+ }
533
+ RSTRUCT_PTR(a)[cnt] = e;
534
+ #endif
535
+ } else {
536
+ a = rb_ary_push(a, e);
537
+ }
538
+ cnt++;
539
+ }
540
+ next_non_white(pi);
389
541
  if (',' == *pi->s) {
390
542
  pi->s++;
391
543
  } else if (']' == *pi->s) {
@@ -400,9 +552,12 @@ read_array(ParseInfo pi) {
400
552
 
401
553
  static VALUE
402
554
  read_str(ParseInfo pi, int hint) {
403
- char *text = read_quoted_value(pi);
555
+ char *text;
404
556
  VALUE obj;
557
+ int escaped;
405
558
 
559
+ escaped = ('\\' == pi->s[1]);
560
+ text = read_quoted_value(pi);
406
561
  if (ObjectMode != pi->options->mode) {
407
562
  hint = T_STRING;
408
563
  }
@@ -436,28 +591,29 @@ read_str(ParseInfo pi, int hint) {
436
591
  break;
437
592
  case 0:
438
593
  default:
439
- if (':' == *text) {
440
- if (':' == text[1]) { // escaped :, it s string
441
- obj = rb_str_new2(text + 1);
442
- #ifdef HAVE_RUBY_ENCODING_H
443
- if (0 != pi->encoding) {
444
- rb_enc_associate(obj, pi->encoding);
445
- }
446
- #endif
447
- } else { // Symbol
594
+ obj = Qundef;
595
+ if (':' == *text && !escaped) { // Symbol
448
596
  #ifdef HAVE_RUBY_ENCODING_H
449
- if (0 != pi->encoding) {
450
- obj = rb_str_new2(text + 1);
451
- rb_enc_associate(obj, pi->encoding);
452
- obj = rb_funcall(obj, oj_to_sym_id, 0);
453
- } else {
454
- obj = ID2SYM(rb_intern(text + 1));
455
- }
456
- #else
597
+ if (0 != pi->encoding) {
598
+ obj = rb_str_new2(text + 1);
599
+ rb_enc_associate(obj, pi->encoding);
600
+ obj = rb_funcall(obj, oj_to_sym_id, 0);
601
+ } else {
457
602
  obj = ID2SYM(rb_intern(text + 1));
603
+ }
604
+ #else
605
+ obj = ID2SYM(rb_intern(text + 1));
458
606
  #endif
607
+ } else if (ObjectMode == pi->options->mode && '^' == *text && '\0' != text[2]) {
608
+ char c1 = text[1];
609
+
610
+ if ('r' == c1) {
611
+ obj = circ_array_get(pi->circ_array, read_ulong(text + 2, pi));
612
+ } else if ('i' == c1) {
613
+ obj = ULONG2NUM(read_ulong(text + 2, pi));
459
614
  }
460
- } else {
615
+ }
616
+ if (Qundef == obj) {
461
617
  obj = rb_str_new2(text);
462
618
  #ifdef HAVE_RUBY_ENCODING_H
463
619
  if (0 != pi->encoding) {
@@ -644,7 +800,6 @@ read_quoted_value(ParseInfo pi) {
644
800
  h++; // skip quote character
645
801
  t++;
646
802
  value = h;
647
- // TBD can whole string be read in and then eval-ed by ruby of there is a special character
648
803
  for (; '"' != *h; h++, t++) {
649
804
  if ('\0' == *h) {
650
805
  pi->s = h;
@@ -661,7 +816,6 @@ read_quoted_value(ParseInfo pi) {
661
816
  case '/': *t = '/'; break;
662
817
  case '\\': *t = '\\'; break;
663
818
  case 'u':
664
- // TBD if first character is 00 then skip it
665
819
  h++;
666
820
  *t = read_hex(pi, h);
667
821
  h += 2;
@@ -685,3 +839,38 @@ read_quoted_value(ParseInfo pi) {
685
839
 
686
840
  return value;
687
841
  }
842
+
843
+ VALUE
844
+ oj_parse(char *json, Options options) {
845
+ VALUE obj;
846
+ struct _ParseInfo pi;
847
+
848
+ if (0 == json) {
849
+ raise_error("Invalid arg, xml string can not be null", json, 0);
850
+ }
851
+ /* initialize parse info */
852
+ pi.str = json;
853
+ pi.s = json;
854
+ pi.circ_array = 0;
855
+ if (Yes == options->circular) {
856
+ pi.circ_array = circ_array_new();
857
+ }
858
+ #ifdef HAVE_RUBY_ENCODING_H
859
+ pi.encoding = ('\0' == *options->encoding) ? 0 : rb_enc_find(options->encoding);
860
+ #else
861
+ pi.encoding = 0;
862
+ #endif
863
+ pi.options = options;
864
+ obj = read_next(&pi, 0);
865
+ if (Yes == options->circular) {
866
+ circ_array_free(pi.circ_array);
867
+ }
868
+ if (Qundef == obj) {
869
+ raise_error("no object read", pi.str, pi.s);
870
+ }
871
+ next_non_white(&pi);
872
+ if ('\0' != *pi.s) {
873
+ raise_error("invalid format, extra characters", pi.str, pi.s);
874
+ }
875
+ return obj;
876
+ }