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.
- data/LICENSE +1 -1
- data/README.md +163 -104
- data/ext/oj/cache8.c +130 -0
- data/ext/oj/cache8.h +46 -0
- data/ext/oj/dump.c +270 -106
- data/ext/oj/load.c +266 -77
- data/ext/oj/oj.c +7 -0
- data/ext/oj/oj.h +3 -0
- data/lib/oj/version.rb +1 -1
- data/test/perf_obj.rb +3 -3
- data/test/perf_strict.rb +105 -35
- data/test/tests.rb +108 -7
- metadata +4 -2
data/ext/oj/load.c
CHANGED
@@ -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
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
186
|
+
#ifndef NO_RSTRUCT
|
187
|
+
inline static VALUE
|
188
|
+
structname2obj(const char *name) {
|
189
|
+
VALUE ost;
|
177
190
|
|
178
|
-
|
179
|
-
|
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
|
-
|
194
|
+
return rb_struct_alloc_noinit(ost);
|
186
195
|
#else
|
187
|
-
|
196
|
+
return rb_struct_new(ost);
|
188
197
|
#endif
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
-
|
194
|
-
|
195
|
-
|
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 '
|
310
|
-
|
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 (
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
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
|
-
|
348
|
-
|
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 =
|
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
|
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
|
-
|
388
|
-
|
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
|
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
|
-
|
440
|
-
|
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
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
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
|
-
}
|
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
|
+
}
|