oj 1.1.1 → 1.2.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/README.md +4 -10
- data/ext/oj/dump.c +100 -29
- data/ext/oj/fast.c +1 -6
- data/ext/oj/load.c +87 -69
- data/ext/oj/oj.c +11 -50
- data/ext/oj/oj.h +5 -8
- data/lib/oj/version.rb +1 -1
- data/test/boo.rb +26 -0
- data/test/{perf2.rb → foo.rb} +12 -30
- data/test/tests.rb +15 -8
- data/test/where.rb +54 -0
- metadata +6 -7
- data/test/perf1.rb +0 -64
- data/test/perf_obj_old.rb +0 -213
data/README.md
CHANGED
@@ -20,15 +20,15 @@ A fast JSON parser and Object marshaller as a Ruby gem.
|
|
20
20
|
|
21
21
|
*Fast XML parser and marshaller on RubyGems*: https://rubygems.org/gems/ox
|
22
22
|
|
23
|
-
*Fast XML parser and marshaller on GitHub*: https://
|
23
|
+
*Fast XML parser and marshaller on GitHub*: https://github.com/ohler55/ox
|
24
24
|
|
25
25
|
## <a name="release">Release Notes</a>
|
26
26
|
|
27
|
-
### Release 1.
|
27
|
+
### Release 1.2.0
|
28
28
|
|
29
|
-
-
|
29
|
+
- Removed the encoding option and fixed a misunderstanding of the string encoding. Unicode code points are now used instead of byte codes. This is not compatible with previous releases but is compliant with RFC4627.
|
30
30
|
|
31
|
-
-
|
31
|
+
- Time encoding in :object mode is faster and higher nanosecond precision.
|
32
32
|
|
33
33
|
## <a name="description">Description</a>
|
34
34
|
|
@@ -59,12 +59,6 @@ methods exist then the Oj internal Object variable encoding is used.
|
|
59
59
|
|
60
60
|
Oj is compatible with Ruby 1.8.7, 1.9.2, 1.9.3, JRuby, and RBX.
|
61
61
|
|
62
|
-
## <a name="plans">Planned Releases</a>
|
63
|
-
|
64
|
-
- Release 1.0.1: Optimize the Oj::Doc dump() method to be native.
|
65
|
-
|
66
|
-
- Release 1.1: A JSON stream parser. Pushed out for the Oj::Doc parser.
|
67
|
-
|
68
62
|
## <a name="compare">Comparisons</a>
|
69
63
|
|
70
64
|
### Fast Oj::Doc parser comparisons
|
data/ext/oj/dump.c
CHANGED
@@ -30,6 +30,7 @@
|
|
30
30
|
|
31
31
|
#include <stdlib.h>
|
32
32
|
#include <errno.h>
|
33
|
+
#include <sys/time.h>
|
33
34
|
#include <time.h>
|
34
35
|
#include <stdio.h>
|
35
36
|
#include <string.h>
|
@@ -38,6 +39,17 @@
|
|
38
39
|
#include "oj.h"
|
39
40
|
#include "cache8.h"
|
40
41
|
|
42
|
+
#ifdef RUBY_API_VERSION_MAJOR
|
43
|
+
#define HAS_TIMESPEC
|
44
|
+
#endif
|
45
|
+
|
46
|
+
#ifndef HAVE_RUBY_ENCODING_H
|
47
|
+
#define rb_eEncodingError rb_eException
|
48
|
+
#endif
|
49
|
+
#ifdef RUBINIUS
|
50
|
+
#define rb_eEncodingError rb_eException
|
51
|
+
#endif
|
52
|
+
|
41
53
|
typedef unsigned long ulong;
|
42
54
|
|
43
55
|
typedef struct _Out {
|
@@ -89,7 +101,7 @@ static int dump_attr_cb(ID key, VALUE value, Out out);
|
|
89
101
|
static void dump_obj_attrs(VALUE obj, int with_class, slot_t id, int depth, Out out);
|
90
102
|
|
91
103
|
static void grow(Out out, size_t len);
|
92
|
-
static size_t
|
104
|
+
static size_t hibit_friendly_size(const u_char *str, size_t len);
|
93
105
|
static size_t ascii_friendly_size(const u_char *str, size_t len);
|
94
106
|
|
95
107
|
static void dump_leaf_to_json(Leaf leaf, Options copts, Out out);
|
@@ -103,7 +115,7 @@ static void dump_leaf_hash(Leaf leaf, int depth, Out out);
|
|
103
115
|
|
104
116
|
static const char hex_chars[17] = "0123456789abcdef";
|
105
117
|
|
106
|
-
static char
|
118
|
+
static char hibit_friendly_chars[256] = "\
|
107
119
|
66666666222622666666666666666666\
|
108
120
|
11211111111111121111111111111111\
|
109
121
|
11111111111111111111111111112111\
|
@@ -113,22 +125,24 @@ static char json_friendly_chars[256] = "\
|
|
113
125
|
11111111111111111111111111111111\
|
114
126
|
11111111111111111111111111111111";
|
115
127
|
|
128
|
+
// High bit set characters are always encoded as unicode. Worse case is 3
|
129
|
+
// bytes per character in the output. That makes this conservative.
|
116
130
|
static char ascii_friendly_chars[256] = "\
|
117
131
|
66666666222622666666666666666666\
|
118
132
|
11211111111111121111111111111111\
|
119
133
|
11111111111111111111111111112111\
|
120
134
|
11111111111111111111111111111116\
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
135
|
+
33333333333333333333333333333333\
|
136
|
+
33333333333333333333333333333333\
|
137
|
+
33333333333333333333333333333333\
|
138
|
+
33333333333333333333333333333333";
|
125
139
|
|
126
140
|
inline static size_t
|
127
|
-
|
141
|
+
hibit_friendly_size(const u_char *str, size_t len) {
|
128
142
|
size_t size = 0;
|
129
143
|
|
130
144
|
for (; 0 < len; str++, len--) {
|
131
|
-
size +=
|
145
|
+
size += hibit_friendly_chars[*str];
|
132
146
|
}
|
133
147
|
return size - len * (size_t)'0';
|
134
148
|
}
|
@@ -215,6 +229,58 @@ dump_hex(u_char c, Out out) {
|
|
215
229
|
*out->cur++ = hex_chars[d];
|
216
230
|
}
|
217
231
|
|
232
|
+
const char*
|
233
|
+
dump_unicode(const char *str, const char *end, Out out) {
|
234
|
+
uint32_t code = 0;
|
235
|
+
uint8_t b = *(uint8_t*)str;
|
236
|
+
int i, cnt;
|
237
|
+
|
238
|
+
if (0xC0 == (0xE0 & b)) {
|
239
|
+
cnt = 1;
|
240
|
+
code = b & 0x0000001F;
|
241
|
+
} else if (0xE0 == (0xF0 & b)) {
|
242
|
+
cnt = 2;
|
243
|
+
code = b & 0x0000000F;
|
244
|
+
} else if (0xF0 == (0xF8 & b)) {
|
245
|
+
cnt = 3;
|
246
|
+
code = b & 0x00000007;
|
247
|
+
} else if (0xF8 == (0xFC & b)) {
|
248
|
+
cnt = 4;
|
249
|
+
code = b & 0x00000003;
|
250
|
+
} else if (0xFC == (0xFE & b)) {
|
251
|
+
cnt = 5;
|
252
|
+
code = b & 0x00000001;
|
253
|
+
} else {
|
254
|
+
rb_raise(rb_eEncodingError, "Invalid Unicode\n");
|
255
|
+
}
|
256
|
+
str++;
|
257
|
+
for (; 0 < cnt; cnt--, str++) {
|
258
|
+
b = *(uint8_t*)str;
|
259
|
+
if (end <= str || 0x80 != (0xC0 & b)) {
|
260
|
+
rb_raise(rb_eEncodingError, "Invalid Unicode\n");
|
261
|
+
}
|
262
|
+
code = (code << 6) | (b & 0x0000003F);
|
263
|
+
}
|
264
|
+
if (0x0000FFFF < code) {
|
265
|
+
uint32_t c1;
|
266
|
+
|
267
|
+
code -= 0x00010000;
|
268
|
+
c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
|
269
|
+
code = (code & 0x000003FF) + 0x0000DC00;
|
270
|
+
*out->cur++ = '\\';
|
271
|
+
*out->cur++ = 'u';
|
272
|
+
for (i = 3; 0 <= i; i--) {
|
273
|
+
*out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
|
274
|
+
}
|
275
|
+
}
|
276
|
+
*out->cur++ = '\\';
|
277
|
+
*out->cur++ = 'u';
|
278
|
+
for (i = 3; 0 <= i; i--) {
|
279
|
+
*out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
|
280
|
+
}
|
281
|
+
return str - 1;
|
282
|
+
}
|
283
|
+
|
218
284
|
// returns 0 if not using circular references, -1 if not further writing is
|
219
285
|
// needed (duplicate), and a positive value if the object was added to the cache.
|
220
286
|
static long
|
@@ -383,8 +449,8 @@ dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
|
|
383
449
|
cmap = ascii_friendly_chars;
|
384
450
|
size = ascii_friendly_size((u_char*)str, cnt);
|
385
451
|
} else {
|
386
|
-
cmap =
|
387
|
-
size =
|
452
|
+
cmap = hibit_friendly_chars;
|
453
|
+
size = hibit_friendly_size((u_char*)str, cnt);
|
388
454
|
}
|
389
455
|
if (out->end - out->cur <= (long)size + 10) { // extra 10 for escaped first char, quotes, and sym
|
390
456
|
grow(out, size + 10);
|
@@ -410,10 +476,12 @@ dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
|
|
410
476
|
}
|
411
477
|
*out->cur++ = '"';
|
412
478
|
} else {
|
479
|
+
const char *end = str + cnt;
|
480
|
+
|
413
481
|
if (is_sym) {
|
414
482
|
*out->cur++ = ':';
|
415
483
|
}
|
416
|
-
for (;
|
484
|
+
for (; str < end; str++) {
|
417
485
|
switch (cmap[(u_char)*str]) {
|
418
486
|
case '1':
|
419
487
|
*out->cur++ = *str;
|
@@ -429,18 +497,15 @@ dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
|
|
429
497
|
default: *out->cur++ = *str; break;
|
430
498
|
}
|
431
499
|
break;
|
432
|
-
case '
|
500
|
+
case '3': // Unicode
|
501
|
+
str = dump_unicode(str, end, out);
|
502
|
+
break;
|
503
|
+
case '6': // control characters
|
433
504
|
*out->cur++ = '\\';
|
434
505
|
*out->cur++ = 'u';
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
dump_hex((u_char)*str, out);
|
439
|
-
} else { // continuation?
|
440
|
-
*out->cur++ = '0';
|
441
|
-
*out->cur++ = '0';
|
442
|
-
dump_hex((u_char)*str, out);
|
443
|
-
}
|
506
|
+
*out->cur++ = '0';
|
507
|
+
*out->cur++ = '0';
|
508
|
+
dump_hex((u_char)*str, out);
|
444
509
|
break;
|
445
510
|
default:
|
446
511
|
break; // ignore, should never happen if the table is correct
|
@@ -847,16 +912,22 @@ dump_hash(VALUE obj, int depth, int mode, Out out) {
|
|
847
912
|
|
848
913
|
static void
|
849
914
|
dump_time(VALUE obj, Out out) {
|
850
|
-
char
|
851
|
-
char
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
915
|
+
char buf[64];
|
916
|
+
char *b = buf + sizeof(buf) - 1;
|
917
|
+
long size;
|
918
|
+
char *dot = b - 10;
|
919
|
+
#ifdef HAS_TIMESPEC
|
920
|
+
struct timespec ts = rb_time_timespec(obj);
|
921
|
+
time_t sec = ts.tv_sec;
|
922
|
+
long nsec = ts.tv_nsec;
|
923
|
+
#else
|
924
|
+
time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
925
|
+
long nsec = NUM2LONG(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
926
|
+
#endif
|
856
927
|
|
857
928
|
*b-- = '\0';
|
858
|
-
for (; dot < b; b--,
|
859
|
-
*b = '0' + (
|
929
|
+
for (; dot < b; b--, nsec /= 10) {
|
930
|
+
*b = '0' + (nsec % 10);
|
860
931
|
}
|
861
932
|
*b-- = '.';
|
862
933
|
for (; 0 < sec; b--, sec /= 10) {
|
data/ext/oj/fast.c
CHANGED
@@ -774,11 +774,6 @@ doc_init(Doc doc) {
|
|
774
774
|
*doc->where = 0;
|
775
775
|
doc->data = 0;
|
776
776
|
doc->self = Qundef;
|
777
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
778
|
-
doc->encoding = oj_default_options.encoding;
|
779
|
-
#else
|
780
|
-
doc->encoding = 0;
|
781
|
-
#endif
|
782
777
|
doc->size = 0;
|
783
778
|
doc->json = 0;
|
784
779
|
doc->batches = &doc->batch0;
|
@@ -1156,7 +1151,7 @@ doc_open_file(VALUE clas, VALUE filename) {
|
|
1156
1151
|
fseek(f, 0, SEEK_SET);
|
1157
1152
|
if (len != fread(json, 1, len, f)) {
|
1158
1153
|
fclose(f);
|
1159
|
-
rb_raise(rb_eLoadError, "Failed to read %
|
1154
|
+
rb_raise(rb_eLoadError, "Failed to read %lu bytes from %s.\n", (unsigned long)len, path);
|
1160
1155
|
}
|
1161
1156
|
fclose(f);
|
1162
1157
|
json[len] = '\0';
|
data/ext/oj/load.c
CHANGED
@@ -50,11 +50,6 @@ typedef struct _ParseInfo {
|
|
50
50
|
char *str; /* buffer being read from */
|
51
51
|
char *s; /* current position in buffer */
|
52
52
|
CircArray circ_array;
|
53
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
54
|
-
rb_encoding *encoding;
|
55
|
-
#else
|
56
|
-
void *encoding;
|
57
|
-
#endif
|
58
53
|
Options options;
|
59
54
|
} *ParseInfo;
|
60
55
|
|
@@ -602,20 +597,14 @@ read_str(ParseInfo pi, int hint) {
|
|
602
597
|
case T_STRING:
|
603
598
|
obj = rb_str_new2(text);
|
604
599
|
#ifdef HAVE_RUBY_ENCODING_H
|
605
|
-
|
606
|
-
rb_enc_associate(obj, pi->encoding);
|
607
|
-
}
|
600
|
+
rb_enc_associate(obj, oj_utf8_encoding);
|
608
601
|
#endif
|
609
602
|
break;
|
610
603
|
case T_SYMBOL:
|
611
604
|
#ifdef HAVE_RUBY_ENCODING_H
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
obj = rb_funcall(obj, oj_to_sym_id, 0);
|
616
|
-
} else {
|
617
|
-
obj = ID2SYM(rb_intern(text));
|
618
|
-
}
|
605
|
+
obj = rb_str_new2(text);
|
606
|
+
rb_enc_associate(obj, oj_utf8_encoding);
|
607
|
+
obj = rb_funcall(obj, oj_to_sym_id, 0);
|
619
608
|
#else
|
620
609
|
obj = ID2SYM(rb_intern(text));
|
621
610
|
#endif
|
@@ -625,13 +614,9 @@ read_str(ParseInfo pi, int hint) {
|
|
625
614
|
obj = Qundef;
|
626
615
|
if (':' == *text && !escaped) { // Symbol
|
627
616
|
#ifdef HAVE_RUBY_ENCODING_H
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
obj = rb_funcall(obj, oj_to_sym_id, 0);
|
632
|
-
} else {
|
633
|
-
obj = ID2SYM(rb_intern(text + 1));
|
634
|
-
}
|
617
|
+
obj = rb_str_new2(text + 1);
|
618
|
+
rb_enc_associate(obj, oj_utf8_encoding);
|
619
|
+
obj = rb_funcall(obj, oj_to_sym_id, 0);
|
635
620
|
#else
|
636
621
|
obj = ID2SYM(rb_intern(text + 1));
|
637
622
|
#endif
|
@@ -647,9 +632,7 @@ read_str(ParseInfo pi, int hint) {
|
|
647
632
|
if (Qundef == obj) {
|
648
633
|
obj = rb_str_new2(text);
|
649
634
|
#ifdef HAVE_RUBY_ENCODING_H
|
650
|
-
|
651
|
-
rb_enc_associate(obj, pi->encoding);
|
652
|
-
}
|
635
|
+
rb_enc_associate(obj, oj_utf8_encoding);
|
653
636
|
#endif
|
654
637
|
}
|
655
638
|
break;
|
@@ -749,23 +732,24 @@ read_num(ParseInfo pi) {
|
|
749
732
|
|
750
733
|
static VALUE
|
751
734
|
read_time(ParseInfo pi) {
|
752
|
-
|
753
|
-
long
|
754
|
-
long v2 = 0;
|
735
|
+
time_t v = 0;
|
736
|
+
long v2 = 0;
|
755
737
|
|
756
738
|
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
757
739
|
v = v * 10 + (*pi->s - '0');
|
758
740
|
}
|
759
741
|
if ('.' == *pi->s) {
|
742
|
+
int cnt;
|
743
|
+
|
760
744
|
pi->s++;
|
761
|
-
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s
|
745
|
+
for (cnt = 9; 0 < cnt && '0' <= *pi->s && *pi->s <= '9'; pi->s++, cnt--) {
|
762
746
|
v2 = v2 * 10 + (*pi->s - '0');
|
763
747
|
}
|
748
|
+
for (; 0 < cnt; cnt--) {
|
749
|
+
v2 *= 10;
|
750
|
+
}
|
764
751
|
}
|
765
|
-
|
766
|
-
args[1] = LONG2NUM(v2);
|
767
|
-
|
768
|
-
return rb_funcall2(oj_time_class, oj_at_id, 2, args);
|
752
|
+
return rb_time_nano_new(v, v2);
|
769
753
|
}
|
770
754
|
|
771
755
|
static VALUE
|
@@ -801,33 +785,61 @@ read_nil(ParseInfo pi) {
|
|
801
785
|
return Qnil;
|
802
786
|
}
|
803
787
|
|
804
|
-
static
|
788
|
+
static uint32_t
|
805
789
|
read_hex(ParseInfo pi, char *h) {
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
b =
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
}
|
823
|
-
|
824
|
-
|
825
|
-
|
790
|
+
uint32_t b = 0;
|
791
|
+
int i;
|
792
|
+
|
793
|
+
// TBD this can be made faster with a table
|
794
|
+
for (i = 0; i < 4; i++, h++) {
|
795
|
+
b = b << 4;
|
796
|
+
if ('0' <= *h && *h <= '9') {
|
797
|
+
b += *h - '0';
|
798
|
+
} else if ('A' <= *h && *h <= 'F') {
|
799
|
+
b += *h - 'A' + 10;
|
800
|
+
} else if ('a' <= *h && *h <= 'f') {
|
801
|
+
b += *h - 'a' + 10;
|
802
|
+
} else {
|
803
|
+
pi->s = h;
|
804
|
+
raise_error("invalid hex character", pi->str, pi->s);
|
805
|
+
}
|
806
|
+
}
|
807
|
+
return b;
|
808
|
+
}
|
809
|
+
|
810
|
+
static char*
|
811
|
+
unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
|
812
|
+
if (0x0000007F >= code) {
|
813
|
+
*t = (char)code;
|
814
|
+
} else if (0x000007FF >= code) {
|
815
|
+
*t++ = 0xC0 | (code >> 6);
|
816
|
+
*t = 0x80 | (0x3F & code);
|
817
|
+
} else if (0x0000FFFF >= code) {
|
818
|
+
*t++ = 0xE0 | (code >> 12);
|
819
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
820
|
+
*t = 0x80 | (0x3F & code);
|
821
|
+
} else if (0x001FFFFF >= code) {
|
822
|
+
*t++ = 0xF0 | (code >> 18);
|
823
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
824
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
825
|
+
*t = 0x80 | (0x3F & code);
|
826
|
+
} else if (0x03FFFFFF >= code) {
|
827
|
+
*t++ = 0xF8 | (code >> 24);
|
828
|
+
*t++ = 0x80 | ((code >> 18) & 0x3F);
|
829
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
830
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
831
|
+
*t = 0x80 | (0x3F & code);
|
832
|
+
} else if (0x7FFFFFFF >= code) {
|
833
|
+
*t++ = 0xFC | (code >> 30);
|
834
|
+
*t++ = 0x80 | ((code >> 24) & 0x3F);
|
835
|
+
*t++ = 0x80 | ((code >> 18) & 0x3F);
|
836
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
837
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
838
|
+
*t = 0x80 | (0x3F & code);
|
826
839
|
} else {
|
827
|
-
pi->s
|
828
|
-
raise_error("invalid hex character", pi->str, pi->s);
|
840
|
+
raise_error("invalid Unicode", pi->str, pi->s);
|
829
841
|
}
|
830
|
-
return
|
842
|
+
return t;
|
831
843
|
}
|
832
844
|
|
833
845
|
/* Assume the value starts immediately and goes until the quote character is
|
@@ -838,6 +850,7 @@ read_quoted_value(ParseInfo pi) {
|
|
838
850
|
char *value = 0;
|
839
851
|
char *h = pi->s; // head
|
840
852
|
char *t = h; // tail
|
853
|
+
uint32_t code;
|
841
854
|
|
842
855
|
h++; // skip quote character
|
843
856
|
t++;
|
@@ -859,13 +872,24 @@ read_quoted_value(ParseInfo pi) {
|
|
859
872
|
case '\\': *t = '\\'; break;
|
860
873
|
case 'u':
|
861
874
|
h++;
|
862
|
-
|
863
|
-
h +=
|
864
|
-
if (
|
865
|
-
|
875
|
+
code = read_hex(pi, h);
|
876
|
+
h += 3;
|
877
|
+
if (0x0000D800 <= code && code <= 0x0000DFFF) {
|
878
|
+
uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
|
879
|
+
uint32_t c2;
|
880
|
+
|
881
|
+
h++;
|
882
|
+
if ('\\' != *h || 'u' != *(h + 1)) {
|
883
|
+
pi->s = h;
|
884
|
+
raise_error("invalid escaped character", pi->str, pi->s);
|
885
|
+
}
|
886
|
+
h += 2;
|
887
|
+
c2 = read_hex(pi, h);
|
888
|
+
h += 3;
|
889
|
+
c2 = (c2 - 0x0000DC00) & 0x000003FF;
|
890
|
+
code = ((c1 << 10) | c2) + 0x00010000;
|
866
891
|
}
|
867
|
-
|
868
|
-
h++;
|
892
|
+
t = unicode_to_chars(pi, t, code);
|
869
893
|
break;
|
870
894
|
default:
|
871
895
|
pi->s = h;
|
@@ -897,12 +921,6 @@ oj_parse(char *json, Options options) {
|
|
897
921
|
if (Yes == options->circular) {
|
898
922
|
pi.circ_array = circ_array_new();
|
899
923
|
}
|
900
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
901
|
-
pi.encoding = options->encoding;
|
902
|
-
//pi.encoding = ('\0' == *options->encoding) ? 0 : rb_enc_find(options->encoding);
|
903
|
-
#else
|
904
|
-
pi.encoding = 0;
|
905
|
-
#endif
|
906
924
|
pi.options = options;
|
907
925
|
obj = read_next(&pi, 0);
|
908
926
|
if (Yes == options->circular) {
|
data/ext/oj/oj.c
CHANGED
@@ -52,7 +52,6 @@ void Init_oj();
|
|
52
52
|
VALUE Oj = Qnil;
|
53
53
|
|
54
54
|
ID oj_as_json_id;
|
55
|
-
ID oj_at_id;
|
56
55
|
ID oj_fileno_id;
|
57
56
|
ID oj_instance_variables_id;
|
58
57
|
ID oj_json_create_id;
|
@@ -61,10 +60,9 @@ ID oj_string_id;
|
|
61
60
|
ID oj_to_hash_id;
|
62
61
|
ID oj_to_json_id;
|
63
62
|
ID oj_to_sym_id;
|
63
|
+
ID oj_write_id;
|
64
64
|
ID oj_tv_nsec_id;
|
65
65
|
ID oj_tv_sec_id;
|
66
|
-
ID oj_tv_usec_id;
|
67
|
-
ID oj_write_id;
|
68
66
|
|
69
67
|
VALUE oj_bag_class;
|
70
68
|
VALUE oj_date_class;
|
@@ -78,7 +76,6 @@ static VALUE ascii_only_sym;
|
|
78
76
|
static VALUE auto_define_sym;
|
79
77
|
static VALUE circular_sym;
|
80
78
|
static VALUE compat_sym;
|
81
|
-
static VALUE encoding_sym;
|
82
79
|
static VALUE indent_sym;
|
83
80
|
static VALUE mode_sym;
|
84
81
|
static VALUE null_sym;
|
@@ -99,8 +96,11 @@ static VALUE keep = Qnil;
|
|
99
96
|
Cache oj_class_cache = 0;
|
100
97
|
Cache oj_attr_cache = 0;
|
101
98
|
|
99
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
100
|
+
rb_encoding *oj_utf8_encoding = 0;
|
101
|
+
#endif
|
102
|
+
|
102
103
|
struct _Options oj_default_options = {
|
103
|
-
0, // encoding
|
104
104
|
0, // indent
|
105
105
|
No, // circular
|
106
106
|
Yes, // auto_define
|
@@ -116,7 +116,6 @@ static VALUE define_mimic_json(VALUE self);
|
|
116
116
|
*
|
117
117
|
* Returns the default load and dump options as a Hash. The options are
|
118
118
|
* - indent: [Fixnum] number of spaces to indent each element in an JSON document
|
119
|
-
* - encoding: [String|Encoding] character encoding for the JSON coument
|
120
119
|
* - circular: [true|false|nil] support circular references while dumping
|
121
120
|
* - auto_define: [true|false|nil] automatically define classes if they do not exist
|
122
121
|
* - symbol_keys: [true|false|nil] use symbols instead of strings for hash keys
|
@@ -127,9 +126,6 @@ static VALUE
|
|
127
126
|
get_def_opts(VALUE self) {
|
128
127
|
VALUE opts = rb_hash_new();
|
129
128
|
|
130
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
131
|
-
rb_hash_aset(opts, encoding_sym, (0 == oj_default_options.encoding) ? Qnil : rb_enc_from_encoding(oj_default_options.encoding));
|
132
|
-
#endif
|
133
129
|
rb_hash_aset(opts, indent_sym, INT2FIX(oj_default_options.indent));
|
134
130
|
rb_hash_aset(opts, circular_sym, (Yes == oj_default_options.circular) ? Qtrue : ((No == oj_default_options.circular) ? Qfalse : Qnil));
|
135
131
|
rb_hash_aset(opts, auto_define_sym, (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
|
@@ -150,7 +146,6 @@ get_def_opts(VALUE self) {
|
|
150
146
|
* Sets the default options for load and dump.
|
151
147
|
* @param [Hash] opts options to change
|
152
148
|
* @param [Fixnum] :indent number of spaces to indent each element in an JSON document
|
153
|
-
* @param [String] :encoding character encoding for the JSON file
|
154
149
|
* @param [true|false|nil] :circular support circular references while dumping
|
155
150
|
* @param [true|false|nil] :auto_define automatically define classes if they do not exist
|
156
151
|
* @param [true|false|nil] :symbol_keys convert hash keys to symbols
|
@@ -177,21 +172,6 @@ set_def_opts(VALUE self, VALUE opts) {
|
|
177
172
|
VALUE v;
|
178
173
|
|
179
174
|
Check_Type(opts, T_HASH);
|
180
|
-
|
181
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
182
|
-
if (Qtrue == rb_funcall(opts, rb_intern("has_key?"), 1, encoding_sym)) {
|
183
|
-
v = rb_hash_lookup(opts, encoding_sym);
|
184
|
-
if (Qnil == v) {
|
185
|
-
oj_default_options.encoding = 0;
|
186
|
-
} else if (T_STRING == rb_type(v)) {
|
187
|
-
oj_default_options.encoding = rb_enc_find(StringValuePtr(v));
|
188
|
-
} else if (rb_cEncoding == rb_obj_class(v)) {
|
189
|
-
oj_default_options.encoding = rb_to_encoding(v);
|
190
|
-
} else {
|
191
|
-
rb_raise(rb_eArgError, ":encoding must be nil, a String, or an Encoding.\n");
|
192
|
-
}
|
193
|
-
}
|
194
|
-
#endif
|
195
175
|
v = rb_hash_aref(opts, indent_sym);
|
196
176
|
if (Qnil != v) {
|
197
177
|
Check_Type(v, T_FIXNUM);
|
@@ -250,17 +230,6 @@ parse_options(VALUE ropts, Options copts) {
|
|
250
230
|
}
|
251
231
|
copts->indent = NUM2INT(v);
|
252
232
|
}
|
253
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
254
|
-
if (Qnil != (v = rb_hash_lookup(ropts, encoding_sym))) {
|
255
|
-
if (T_STRING == rb_type(v)) {
|
256
|
-
oj_default_options.encoding = rb_enc_find(StringValuePtr(v));
|
257
|
-
} else if (rb_cEncoding == rb_obj_class(v)) {
|
258
|
-
oj_default_options.encoding = rb_to_encoding(v);
|
259
|
-
} else {
|
260
|
-
rb_raise(rb_eArgError, ":encoding must be nil, a String, or an Encoding.\n");
|
261
|
-
}
|
262
|
-
}
|
263
|
-
#endif
|
264
233
|
if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
|
265
234
|
if (object_sym == v) {
|
266
235
|
copts->mode = ObjectMode;
|
@@ -430,9 +399,7 @@ dump(int argc, VALUE *argv, VALUE self) {
|
|
430
399
|
}
|
431
400
|
rstr = rb_str_new2(json);
|
432
401
|
#ifdef HAVE_RUBY_ENCODING_H
|
433
|
-
|
434
|
-
rb_enc_associate(rstr, copts.encoding);
|
435
|
-
}
|
402
|
+
rb_enc_associate(rstr, oj_utf8_encoding);
|
436
403
|
#endif
|
437
404
|
xfree(json);
|
438
405
|
|
@@ -475,9 +442,7 @@ mimic_dump(int argc, VALUE *argv, VALUE self) {
|
|
475
442
|
}
|
476
443
|
rstr = rb_str_new2(json);
|
477
444
|
#ifdef ENCODING_INLINE_MAX
|
478
|
-
|
479
|
-
rb_enc_associate(rstr, copts.encoding);
|
480
|
-
}
|
445
|
+
rb_enc_associate(rstr, oj_utf8_encoding);
|
481
446
|
#endif
|
482
447
|
if (2 <= argc && Qnil != argv[1]) {
|
483
448
|
VALUE io = argv[1];
|
@@ -615,9 +580,7 @@ mimic_generate_core(int argc, VALUE *argv, Options copts) {
|
|
615
580
|
}
|
616
581
|
rstr = rb_str_new2(json);
|
617
582
|
#ifdef ENCODING_INLINE_MAX
|
618
|
-
|
619
|
-
rb_enc_associate(rstr, copts->encoding);
|
620
|
-
}
|
583
|
+
rb_enc_associate(rstr, oj_utf8_encoding);
|
621
584
|
#endif
|
622
585
|
xfree(json);
|
623
586
|
|
@@ -732,6 +695,7 @@ define_mimic_json(VALUE self) {
|
|
732
695
|
symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_ary_push(keep, symbolize_names_sym);
|
733
696
|
|
734
697
|
oj_default_options.mode = CompatMode;
|
698
|
+
oj_default_options.ascii_only = Yes;
|
735
699
|
}
|
736
700
|
return mimic;
|
737
701
|
}
|
@@ -754,7 +718,6 @@ void Init_oj() {
|
|
754
718
|
rb_define_module_function(Oj, "to_file", to_file, -1);
|
755
719
|
|
756
720
|
oj_as_json_id = rb_intern("as_json");
|
757
|
-
oj_at_id = rb_intern("at");
|
758
721
|
oj_fileno_id = rb_intern("fileno");
|
759
722
|
oj_instance_variables_id = rb_intern("instance_variables");
|
760
723
|
oj_json_create_id = rb_intern("json_create");
|
@@ -763,10 +726,9 @@ void Init_oj() {
|
|
763
726
|
oj_to_hash_id = rb_intern("to_hash");
|
764
727
|
oj_to_json_id = rb_intern("to_json");
|
765
728
|
oj_to_sym_id = rb_intern("to_sym");
|
729
|
+
oj_write_id = rb_intern("write");
|
766
730
|
oj_tv_nsec_id = rb_intern("tv_nsec");
|
767
731
|
oj_tv_sec_id = rb_intern("tv_sec");
|
768
|
-
oj_tv_usec_id = rb_intern("tv_usec");
|
769
|
-
oj_write_id = rb_intern("write");
|
770
732
|
|
771
733
|
oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag"));
|
772
734
|
oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
|
@@ -778,7 +740,6 @@ void Init_oj() {
|
|
778
740
|
auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_ary_push(keep, auto_define_sym);
|
779
741
|
circular_sym = ID2SYM(rb_intern("circular")); rb_ary_push(keep, circular_sym);
|
780
742
|
compat_sym = ID2SYM(rb_intern("compat")); rb_ary_push(keep, compat_sym);
|
781
|
-
encoding_sym = ID2SYM(rb_intern("encoding")); rb_ary_push(keep, encoding_sym);
|
782
743
|
indent_sym = ID2SYM(rb_intern("indent")); rb_ary_push(keep, indent_sym);
|
783
744
|
mode_sym = ID2SYM(rb_intern("mode")); rb_ary_push(keep, mode_sym);
|
784
745
|
symbol_keys_sym = ID2SYM(rb_intern("symbol_keys")); rb_ary_push(keep, symbol_keys_sym);
|
@@ -790,7 +751,7 @@ void Init_oj() {
|
|
790
751
|
|
791
752
|
oj_default_options.mode = ObjectMode;
|
792
753
|
#ifdef HAVE_RUBY_ENCODING_H
|
793
|
-
|
754
|
+
oj_utf8_encoding = rb_enc_find("UTF-8");
|
794
755
|
#endif
|
795
756
|
|
796
757
|
oj_cache_new(&oj_class_cache);
|
data/ext/oj/oj.h
CHANGED
@@ -61,8 +61,10 @@ extern "C" {
|
|
61
61
|
#undef T_RATIONAL
|
62
62
|
#undef T_COMPLEX
|
63
63
|
#define NO_RSTRUCT 1
|
64
|
+
#ifndef IVAR_HELPERS
|
64
65
|
#define IVAR_HELPERS 0
|
65
66
|
#endif
|
67
|
+
#endif
|
66
68
|
|
67
69
|
#if IVAR_HELPERS
|
68
70
|
#include "ruby/st.h"
|
@@ -99,12 +101,6 @@ typedef struct _DumpOpts {
|
|
99
101
|
} *DumpOpts;
|
100
102
|
|
101
103
|
typedef struct _Options {
|
102
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
103
|
-
rb_encoding *encoding;
|
104
|
-
#else
|
105
|
-
void *encoding;
|
106
|
-
#endif
|
107
|
-
//char encoding[64]; // encoding, stored in the option to avoid GC invalidation in default values
|
108
104
|
int indent; // indention for dump, default 2
|
109
105
|
char circular; // YesNo
|
110
106
|
char auto_define; // YesNo
|
@@ -148,6 +144,9 @@ extern void oj_init_doc(void);
|
|
148
144
|
|
149
145
|
extern VALUE Oj;
|
150
146
|
extern struct _Options oj_default_options;
|
147
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
148
|
+
extern rb_encoding *oj_utf8_encoding;
|
149
|
+
#endif
|
151
150
|
|
152
151
|
extern VALUE oj_bag_class;
|
153
152
|
extern VALUE oj_date_class;
|
@@ -159,7 +158,6 @@ extern VALUE oj_time_class;
|
|
159
158
|
extern VALUE oj_slash_string;
|
160
159
|
|
161
160
|
extern ID oj_as_json_id;
|
162
|
-
extern ID oj_at_id;
|
163
161
|
extern ID oj_instance_variables_id;
|
164
162
|
extern ID oj_json_create_id;
|
165
163
|
extern ID oj_string_id;
|
@@ -168,7 +166,6 @@ extern ID oj_to_json_id;
|
|
168
166
|
extern ID oj_to_sym_id;
|
169
167
|
extern ID oj_tv_nsec_id;
|
170
168
|
extern ID oj_tv_sec_id;
|
171
|
-
extern ID oj_tv_usec_id;
|
172
169
|
|
173
170
|
extern Cache oj_class_cache;
|
174
171
|
extern Cache oj_attr_cache;
|
data/lib/oj/version.rb
CHANGED
data/test/boo.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW1
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
5
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
6
|
+
|
7
|
+
require 'yajl'
|
8
|
+
require 'oj'
|
9
|
+
|
10
|
+
iter = 100
|
11
|
+
s = File.read("boo.json")
|
12
|
+
start = Time.now
|
13
|
+
iter.times do
|
14
|
+
Oj.load(s)
|
15
|
+
end
|
16
|
+
oj_dt = Time.now - start
|
17
|
+
puts "%d Oj.load()s in %0.3f seconds or %0.1f loads/second" % [iter, oj_dt, iter/oj_dt]
|
18
|
+
|
19
|
+
start = Time.now
|
20
|
+
iter.times do
|
21
|
+
Yajl::Parser.parse(s)
|
22
|
+
end
|
23
|
+
yajl_dt = Time.now - start
|
24
|
+
puts "%d Yajl::Parser.parse()s in %0.3f seconds or %0.1f parsed/second" % [iter, yajl_dt, iter/yajl_dt]
|
25
|
+
|
26
|
+
puts "Oj is %0.1f times faster than YAJL" % [yajl_dt / oj_dt]
|
data/test/{perf2.rb → foo.rb}
RENAMED
@@ -15,19 +15,6 @@ opts = OptionParser.new
|
|
15
15
|
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
16
16
|
files = opts.parse(ARGV)
|
17
17
|
|
18
|
-
class Foo
|
19
|
-
def initialize()
|
20
|
-
@x = true
|
21
|
-
@y = 58
|
22
|
-
end
|
23
|
-
def to_json()
|
24
|
-
%{{"x":#{@x},"y":#{@y}}}
|
25
|
-
end
|
26
|
-
def to_hash()
|
27
|
-
{ 'x' => @x, 'y' => @y }
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
18
|
iter = 100000
|
32
19
|
s = %{
|
33
20
|
{ "class": "Foo::Bar",
|
@@ -36,41 +23,36 @@ s = %{
|
|
36
23
|
}
|
37
24
|
}
|
38
25
|
|
39
|
-
obj = Oj.load(s)
|
40
|
-
obj["foo"] = Foo.new()
|
41
|
-
|
42
|
-
Oj.default_options = { :indent => 0, :effort => :internal }
|
43
|
-
|
44
|
-
puts
|
45
|
-
|
46
26
|
start = Time.now
|
47
27
|
iter.times do
|
48
28
|
Oj.load(s)
|
49
29
|
end
|
50
|
-
|
51
|
-
puts "%d Oj.load()s in %0.3f seconds or %0.1f loads/msec" % [iter,
|
30
|
+
oj_dt = Time.now - start
|
31
|
+
puts "%d Oj.load()s in %0.3f seconds or %0.1f loads/msec" % [iter, oj_dt, iter/oj_dt/1000.0]
|
52
32
|
|
53
33
|
start = Time.now
|
54
34
|
iter.times do
|
55
35
|
Yajl::Parser.parse(s)
|
56
36
|
end
|
57
|
-
|
58
|
-
puts "%d Yajl::Parser.parse()s in %0.3f seconds or %0.1f parses/msec" % [iter,
|
37
|
+
yajl_dt = Time.now - start
|
38
|
+
puts "%d Yajl::Parser.parse()s in %0.3f seconds or %0.1f parses/msec" % [iter, yajl_dt, iter/yajl_dt/1000.0]
|
59
39
|
|
60
|
-
puts
|
40
|
+
puts "Oj is %0.1f times faster than YAJL at parsing." % [yajl_dt / oj_dt]
|
61
41
|
|
42
|
+
|
43
|
+
obj = Oj.load(s)
|
62
44
|
start = Time.now
|
63
45
|
iter.times do
|
64
46
|
Oj.dump(obj)
|
65
47
|
end
|
66
|
-
|
67
|
-
puts "%d Oj.dump()s in %0.3f seconds or %0.1f dumps/msec" % [iter,
|
48
|
+
oj_dt = Time.now - start
|
49
|
+
puts "%d Oj.dump()s in %0.3f seconds or %0.1f dumps/msec" % [iter, oj_dt, iter/oj_dt/1000.0]
|
68
50
|
|
69
51
|
start = Time.now
|
70
52
|
iter.times do
|
71
53
|
Yajl::Encoder.encode(obj)
|
72
54
|
end
|
73
|
-
|
74
|
-
puts "%d Yajl::Encoder.encode()s in %0.3f seconds or %0.1f encodes/msec" % [iter,
|
55
|
+
yajl_dt = Time.now - start
|
56
|
+
puts "%d Yajl::Encoder.encode()s in %0.3f seconds or %0.1f encodes/msec" % [iter, yajl_dt, iter/yajl_dt/1000.0]
|
75
57
|
|
76
|
-
puts
|
58
|
+
puts "Oj is %0.1f times faster than YAJL at dumping." % [yajl_dt / oj_dt]
|
data/test/tests.rb
CHANGED
@@ -67,8 +67,7 @@ class Juice < ::Test::Unit::TestCase
|
|
67
67
|
|
68
68
|
def test0_get_options
|
69
69
|
opts = Oj.default_options()
|
70
|
-
assert_equal({ :
|
71
|
-
:indent=>0,
|
70
|
+
assert_equal({ :indent=>0,
|
72
71
|
:circular=>false,
|
73
72
|
:auto_define=>true,
|
74
73
|
:symbol_keys=>false,
|
@@ -78,7 +77,6 @@ class Juice < ::Test::Unit::TestCase
|
|
78
77
|
|
79
78
|
def test0_set_options
|
80
79
|
orig = {
|
81
|
-
:encoding=>nil,
|
82
80
|
:indent=>0,
|
83
81
|
:circular=>false,
|
84
82
|
:auto_define=>true,
|
@@ -86,7 +84,6 @@ class Juice < ::Test::Unit::TestCase
|
|
86
84
|
:ascii_only=>false,
|
87
85
|
:mode=>:object}
|
88
86
|
o2 = {
|
89
|
-
:encoding=>"UTF-8",
|
90
87
|
:indent=>4,
|
91
88
|
:circular=>true,
|
92
89
|
:auto_define=>false,
|
@@ -96,7 +93,6 @@ class Juice < ::Test::Unit::TestCase
|
|
96
93
|
o3 = { :indent => 4 }
|
97
94
|
Oj.default_options = o2
|
98
95
|
opts = Oj.default_options()
|
99
|
-
o2[:encoding] = Encoding.find('UTF-8')
|
100
96
|
assert_equal(opts, o2);
|
101
97
|
Oj.default_options = o3 # see if it throws an exception
|
102
98
|
Oj.default_options = orig # return to original
|
@@ -147,11 +143,22 @@ class Juice < ::Test::Unit::TestCase
|
|
147
143
|
end
|
148
144
|
|
149
145
|
def test_encode
|
150
|
-
Oj.default_options
|
146
|
+
opts = Oj.default_options
|
147
|
+
Oj.default_options = { :ascii_only => false }
|
151
148
|
dump_and_load("ぴーたー", false)
|
152
149
|
Oj.default_options = { :ascii_only => true }
|
150
|
+
json = Oj.dump("ぴーたー")
|
151
|
+
assert_equal(%{"\\u3074\\u30fc\\u305f\\u30fc"}, json)
|
153
152
|
dump_and_load("ぴーたー", false)
|
154
|
-
Oj.default_options =
|
153
|
+
Oj.default_options = opts
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_unicode
|
157
|
+
# hits the 3 normal ranges and one extended surrogate pair
|
158
|
+
json = %{"\\u019f\\u05e9\\u3074\\ud834\\udd1e"}
|
159
|
+
obj = Oj.load(json)
|
160
|
+
json2 = Oj.dump(obj, :ascii_only => true)
|
161
|
+
assert_equal(json, json2)
|
155
162
|
end
|
156
163
|
|
157
164
|
def test_array
|
@@ -202,7 +209,7 @@ class Juice < ::Test::Unit::TestCase
|
|
202
209
|
def test_time_compat
|
203
210
|
t = Time.local(2012, 1, 5, 23, 58, 7)
|
204
211
|
json = Oj.dump(t, :mode => :compat)
|
205
|
-
assert_equal(%{1325775487.
|
212
|
+
assert_equal(%{1325775487.000000000}, json)
|
206
213
|
end
|
207
214
|
def test_time_object
|
208
215
|
t = Time.now()
|
data/test/where.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW1
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
$: << '.'
|
5
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
6
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
7
|
+
|
8
|
+
require 'optparse'
|
9
|
+
require 'perf'
|
10
|
+
require 'oj'
|
11
|
+
|
12
|
+
$verbose = false
|
13
|
+
$indent = 0
|
14
|
+
$iter = 1000000
|
15
|
+
|
16
|
+
opts = OptionParser.new
|
17
|
+
opts.on("-v", "verbose") { $verbose = true }
|
18
|
+
opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
|
19
|
+
opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
|
20
|
+
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
21
|
+
files = opts.parse(ARGV)
|
22
|
+
|
23
|
+
$obj = {
|
24
|
+
'a' => 'Alpha', # string
|
25
|
+
'b' => true, # boolean
|
26
|
+
'c' => 12345, # number
|
27
|
+
'd' => [ true, [false, [12345, nil], 3.967, ['something', false], nil]], # mix it up array
|
28
|
+
'e' => { 'one' => 1, 'two' => 2 }, # hash
|
29
|
+
'f' => nil, # nil
|
30
|
+
'g' => 12345678901234567890123456789, # big number
|
31
|
+
'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
|
32
|
+
'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
|
33
|
+
}
|
34
|
+
|
35
|
+
Oj.default_options = { :indent => $indent, :mode => :strict }
|
36
|
+
|
37
|
+
$json = Oj.dump($obj)
|
38
|
+
|
39
|
+
if $verbose
|
40
|
+
puts "json:\n#{$json}\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
puts '-' * 80
|
44
|
+
puts "Parse Performance"
|
45
|
+
Oj::Fast.open($json) do |fast|
|
46
|
+
fast.move('/d/2/4/2')
|
47
|
+
puts fast.where2?
|
48
|
+
puts fast.where?
|
49
|
+
perf = Perf.new()
|
50
|
+
perf.add('Oj:fast', 'where') { fast.where? }
|
51
|
+
perf.add('Oj:fast2', 'where2') { fast.where2? }
|
52
|
+
perf.run($iter)
|
53
|
+
end
|
54
|
+
puts
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-30 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'The fastest JSON parser and object serializer. '
|
15
15
|
email: peter@ohler.com
|
@@ -32,13 +32,12 @@ files:
|
|
32
32
|
- ext/oj/fast.c
|
33
33
|
- ext/oj/load.c
|
34
34
|
- ext/oj/oj.c
|
35
|
+
- test/boo.rb
|
35
36
|
- test/files.rb
|
37
|
+
- test/foo.rb
|
36
38
|
- test/perf.rb
|
37
|
-
- test/perf1.rb
|
38
|
-
- test/perf2.rb
|
39
39
|
- test/perf_fast.rb
|
40
40
|
- test/perf_obj.rb
|
41
|
-
- test/perf_obj_old.rb
|
42
41
|
- test/perf_simple.rb
|
43
42
|
- test/perf_strict.rb
|
44
43
|
- test/sample/change.rb
|
@@ -58,6 +57,7 @@ files:
|
|
58
57
|
- test/test_fast.rb
|
59
58
|
- test/test_mimic.rb
|
60
59
|
- test/tests.rb
|
60
|
+
- test/where.rb
|
61
61
|
- LICENSE
|
62
62
|
- README.md
|
63
63
|
homepage: https://github.com/ohler55/oj
|
@@ -83,9 +83,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
83
|
version: '0'
|
84
84
|
requirements: []
|
85
85
|
rubyforge_project: oj
|
86
|
-
rubygems_version: 1.8.
|
86
|
+
rubygems_version: 1.8.15
|
87
87
|
signing_key:
|
88
88
|
specification_version: 3
|
89
89
|
summary: A fast JSON parser and serializer.
|
90
90
|
test_files: []
|
91
|
-
has_rdoc: true
|
data/test/perf1.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby -wW1
|
2
|
-
# encoding: UTF-8
|
3
|
-
|
4
|
-
$: << File.join(File.dirname(__FILE__), "../lib")
|
5
|
-
$: << File.join(File.dirname(__FILE__), "../ext")
|
6
|
-
|
7
|
-
#require 'test/unit'
|
8
|
-
require 'optparse'
|
9
|
-
require 'oj'
|
10
|
-
require 'ox'
|
11
|
-
|
12
|
-
$indent = 2
|
13
|
-
|
14
|
-
opts = OptionParser.new
|
15
|
-
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
16
|
-
files = opts.parse(ARGV)
|
17
|
-
|
18
|
-
iter = 100000
|
19
|
-
s = %{
|
20
|
-
{ "class": "Foo::Bar",
|
21
|
-
"attr1": [ true, [false, [12345, null], 3.967, ["something", false], null]],
|
22
|
-
"attr2": { "one": 1 }
|
23
|
-
}
|
24
|
-
}
|
25
|
-
#s = File.read('sample.json')
|
26
|
-
|
27
|
-
Oj.default_options = { :indent => 0 }
|
28
|
-
|
29
|
-
obj = Oj.load(s)
|
30
|
-
xml = Ox.dump(obj, :indent => 0)
|
31
|
-
|
32
|
-
puts xml
|
33
|
-
|
34
|
-
start = Time.now
|
35
|
-
iter.times do
|
36
|
-
Oj.load(s)
|
37
|
-
end
|
38
|
-
dt = Time.now - start
|
39
|
-
puts "%d Oj.load()s in %0.3f seconds or %0.1f loads/msec" % [iter, dt, iter/dt/1000.0]
|
40
|
-
|
41
|
-
start = Time.now
|
42
|
-
iter.times do
|
43
|
-
Ox.load(xml)
|
44
|
-
end
|
45
|
-
dt = Time.now - start
|
46
|
-
puts "%d Ox.load()s in %0.3f seconds or %0.1f loads/msec" % [iter, dt, iter/dt/1000.0]
|
47
|
-
|
48
|
-
puts
|
49
|
-
|
50
|
-
start = Time.now
|
51
|
-
iter.times do
|
52
|
-
Oj.dump(obj)
|
53
|
-
end
|
54
|
-
dt = Time.now - start
|
55
|
-
puts "%d Oj.dump()s in %0.3f seconds or %0.1f dumps/msec" % [iter, dt, iter/dt/1000.0]
|
56
|
-
|
57
|
-
start = Time.now
|
58
|
-
iter.times do
|
59
|
-
Ox.dump(obj)
|
60
|
-
end
|
61
|
-
dt = Time.now - start
|
62
|
-
puts "%d Ox.dump()s in %0.3f seconds or %0.1f dumps/msec" % [iter, dt, iter/dt/1000.0]
|
63
|
-
|
64
|
-
puts
|
data/test/perf_obj_old.rb
DELETED
@@ -1,213 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby -wW1
|
2
|
-
|
3
|
-
$: << '.'
|
4
|
-
$: << '..'
|
5
|
-
$: << '../lib'
|
6
|
-
$: << '../ext'
|
7
|
-
|
8
|
-
if __FILE__ == $0
|
9
|
-
if (i = ARGV.index('-I'))
|
10
|
-
x,path = ARGV.slice!(i, 2)
|
11
|
-
$: << path
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
require 'optparse'
|
16
|
-
require 'ox'
|
17
|
-
require 'oj'
|
18
|
-
require 'perf'
|
19
|
-
require 'sample'
|
20
|
-
require 'files'
|
21
|
-
|
22
|
-
$verbose = 0
|
23
|
-
$circular = false
|
24
|
-
$indent = 0
|
25
|
-
|
26
|
-
do_sample = false
|
27
|
-
do_files = false
|
28
|
-
|
29
|
-
do_load = false
|
30
|
-
do_dump = false
|
31
|
-
do_read = false
|
32
|
-
do_write = false
|
33
|
-
$iter = 1000
|
34
|
-
|
35
|
-
opts = OptionParser.new
|
36
|
-
opts.on("-v", "increase verbosity") { $verbose += 1 }
|
37
|
-
|
38
|
-
opts.on("-c", "circular options") { $circular = true }
|
39
|
-
|
40
|
-
opts.on("-s", "load and dump as sample Ruby object") { do_sample = true }
|
41
|
-
opts.on("-f", "load and dump as files Ruby object") { do_files = true }
|
42
|
-
|
43
|
-
opts.on("-l", "load") { do_load = true }
|
44
|
-
opts.on("-d", "dump") { do_dump = true }
|
45
|
-
opts.on("-r", "read") { do_read = true }
|
46
|
-
opts.on("-w", "write") { do_write = true }
|
47
|
-
opts.on("-a", "load, dump, read and write") { do_load = true; do_dump = true; do_read = true; do_write = true }
|
48
|
-
|
49
|
-
opts.on("-i", "--iterations [Int]", Integer, "iterations") { |i| $iter = i }
|
50
|
-
|
51
|
-
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
52
|
-
files = opts.parse(ARGV)
|
53
|
-
|
54
|
-
if files.empty?
|
55
|
-
data = []
|
56
|
-
obj = do_sample ? sample_doc(2) : files('..')
|
57
|
-
mars = Marshal.dump(obj)
|
58
|
-
xml = Ox.dump(obj, :indent => $indent, circular: $circular)
|
59
|
-
json = Oj.dump(obj, :indent => $indent, circular: $circular)
|
60
|
-
File.open('sample.xml', 'w') { |f| f.write(xml) }
|
61
|
-
File.open('sample.json', 'w') { |f| f.write(json) }
|
62
|
-
File.open('sample.marshal', 'w') { |f| f.write(mars) }
|
63
|
-
data << { :file => 'sample.xml', :obj => obj, :xml => xml, :marshal => mars, :json => json }
|
64
|
-
else
|
65
|
-
puts "loading and parsing #{files}\n\n"
|
66
|
-
# TBD change to allow xml and json
|
67
|
-
data = files.map do |f|
|
68
|
-
xml = File.read(f)
|
69
|
-
obj = Ox.load(xml);
|
70
|
-
mars = Marshal.dump(obj)
|
71
|
-
json = Oj.dump(obj, :indent => $indent, circular: $circular)
|
72
|
-
{ :file => f, :obj => obj, :xml => xml, :marshal => mars, :json => json }
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
$ox_load_time = 0
|
77
|
-
$mars_load_time = 0
|
78
|
-
$ox_dump_time = 0
|
79
|
-
$oj_dump_time = 0
|
80
|
-
$mars_dump_time = 0
|
81
|
-
|
82
|
-
def perf_load(d)
|
83
|
-
filename = d[:file]
|
84
|
-
marshal_filename = 'sample.marshal'
|
85
|
-
xml = d[:xml]
|
86
|
-
mars = d[:marshal]
|
87
|
-
json = d[:json]
|
88
|
-
|
89
|
-
if 0 < $verbose
|
90
|
-
obj = Ox.load(xml, :mode => :object, :trace => $verbose)
|
91
|
-
return
|
92
|
-
end
|
93
|
-
start = Time.now
|
94
|
-
(1..$iter).each do
|
95
|
-
obj = Ox.load(xml, :mode => :object)
|
96
|
-
end
|
97
|
-
$ox_load_time = Time.now - start
|
98
|
-
puts "Parsing #{$iter} times with Ox took #{$ox_load_time} seconds."
|
99
|
-
|
100
|
-
start = Time.now
|
101
|
-
(1..$iter).each do
|
102
|
-
obj = Oj.load(json, :mode => :object)
|
103
|
-
end
|
104
|
-
$oj_load_time = Time.now - start
|
105
|
-
puts "Parsing #{$iter} times with Oj took #{$oj_load_time} seconds."
|
106
|
-
|
107
|
-
start = Time.now
|
108
|
-
(1..$iter).each do
|
109
|
-
obj = Marshal.load(mars)
|
110
|
-
end
|
111
|
-
$mars_load_time = Time.now - start
|
112
|
-
puts "Marshalling #{$iter} times took #{$mars_load_time} seconds."
|
113
|
-
puts ">>> Ox is %0.1f faster than Marshal loading.\n\n" % [$mars_load_time/$ox_load_time]
|
114
|
-
end
|
115
|
-
|
116
|
-
def perf_dump(d)
|
117
|
-
obj = d[:obj]
|
118
|
-
|
119
|
-
start = Time.now
|
120
|
-
(1..$iter).each do
|
121
|
-
xml = Ox.dump(obj, :indent => $indent, :circular => $circular)
|
122
|
-
#puts "*** ox:\n#{xml}"
|
123
|
-
end
|
124
|
-
$ox_dump_time = Time.now - start
|
125
|
-
puts "Ox dumping #{$iter} times with ox took #{$ox_dump_time} seconds."
|
126
|
-
|
127
|
-
Oj.default_options = {:indent => $indent}
|
128
|
-
start = Time.now
|
129
|
-
(1..$iter).each do
|
130
|
-
json = Oj.dump(obj)
|
131
|
-
end
|
132
|
-
$oj_dump_time = Time.now - start
|
133
|
-
puts "Oj dumping #{$iter} times with oj took #{$oj_dump_time} seconds."
|
134
|
-
|
135
|
-
obj = d[:obj]
|
136
|
-
start = Time.now
|
137
|
-
(1..$iter).each do
|
138
|
-
m = Marshal.dump(obj)
|
139
|
-
end
|
140
|
-
$mars_dump_time = Time.now - start
|
141
|
-
puts "Marshal dumping #{$iter} times took #{$mars_dump_time} seconds."
|
142
|
-
puts ">>> Ox is %0.1f faster than Marshal dumping.\n\n" % [$mars_dump_time/$ox_dump_time]
|
143
|
-
end
|
144
|
-
|
145
|
-
def perf_read(d)
|
146
|
-
ox_read_time = 0
|
147
|
-
mars_read_time = 0
|
148
|
-
|
149
|
-
filename = d[:file]
|
150
|
-
marshal_filename = 'sample.marshal'
|
151
|
-
xml = d[:xml]
|
152
|
-
mars = d[:marshal]
|
153
|
-
|
154
|
-
# now load from the file
|
155
|
-
start = Time.now
|
156
|
-
(1..$iter).each do
|
157
|
-
obj = Ox.load_file(filename, :mode => :object)
|
158
|
-
end
|
159
|
-
ox_read_time = Time.now - start
|
160
|
-
puts "Loading and parsing #{$iter} times with ox took #{ox_read_time} seconds."
|
161
|
-
|
162
|
-
start = Time.now
|
163
|
-
(1..$iter).each do
|
164
|
-
m = File.read(marshal_filename)
|
165
|
-
obj = Marshal.load(m)
|
166
|
-
end
|
167
|
-
mars_read_time = Time.now - start
|
168
|
-
puts "Reading and marshalling #{$iter} times took #{mars_read_time} seconds."
|
169
|
-
puts ">>> Ox is %0.1f faster than Marshal loading and parsing.\n\n" % [mars_read_time/ox_read_time]
|
170
|
-
|
171
|
-
end
|
172
|
-
|
173
|
-
def perf_write(d)
|
174
|
-
ox_write_time = 0
|
175
|
-
mars_write_time = 0
|
176
|
-
|
177
|
-
ox_filename = 'out.xml'
|
178
|
-
marshal_filename = 'out.marshal'
|
179
|
-
obj = d[:obj]
|
180
|
-
|
181
|
-
start = Time.now
|
182
|
-
(1..$iter).each do
|
183
|
-
xml = Ox.to_file(ox_filename, obj, :indent => $indent)
|
184
|
-
end
|
185
|
-
ox_write_time = Time.now - start
|
186
|
-
puts "Ox dumping #{$iter} times with ox took #{ox_write_time} seconds."
|
187
|
-
|
188
|
-
start = Time.now
|
189
|
-
(1..$iter).each do
|
190
|
-
m = Marshal.dump(obj, circular: $circular)
|
191
|
-
File.open(marshal_filename, "w") { |f| f.write(m) }
|
192
|
-
end
|
193
|
-
mars_write_time = Time.now - start
|
194
|
-
puts "Marshal dumping and writing #{$iter} times took #{mars_write_time} seconds."
|
195
|
-
puts ">>> Ox is %0.1f faster than Marshal dumping.\n\n" % [mars_write_time/ox_write_time]
|
196
|
-
|
197
|
-
end
|
198
|
-
|
199
|
-
#if do_sample or do_files
|
200
|
-
data.each do |d|
|
201
|
-
puts "Using file #{d[:file]}."
|
202
|
-
|
203
|
-
perf_load(d) if do_load
|
204
|
-
perf_dump(d) if do_dump
|
205
|
-
if do_load and do_dump
|
206
|
-
puts ">>> Ox is %0.1f faster than Marshal dumping and loading.\n\n" % [($mars_load_time + $mars_dump_time)/($ox_load_time + $ox_dump_time)] unless 0 == $mars_load_time
|
207
|
-
end
|
208
|
-
|
209
|
-
perf_read(d) if do_read
|
210
|
-
perf_write(d) if do_write
|
211
|
-
|
212
|
-
end
|
213
|
-
#end
|