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 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://rubygems.org/gems/ox
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.1.1
27
+ ### Release 1.2.0
28
28
 
29
- - The encoding option can now be an Encoding Object or a String.
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
- - Fixed Rubinius errors.
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
@@ -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 json_friendly_size(const u_char *str, size_t len);
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 json_friendly_chars[256] = "\
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
- 66666666666666666666666666666666\
122
- 66666666666666666666666666666666\
123
- 66666666666666666666666666666666\
124
- 66666666666666666666666666666666";
135
+ 33333333333333333333333333333333\
136
+ 33333333333333333333333333333333\
137
+ 33333333333333333333333333333333\
138
+ 33333333333333333333333333333333";
125
139
 
126
140
  inline static size_t
127
- json_friendly_size(const u_char *str, size_t len) {
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 += json_friendly_chars[*str];
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 = json_friendly_chars;
387
- size = json_friendly_size((u_char*)str, cnt);
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 (; 0 < cnt; cnt--, str++) {
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 '6':
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
- if ((u_char)*str <= 0x7F) {
436
- *out->cur++ = '0';
437
- *out->cur++ = '0';
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 buf[64];
851
- char *b = buf + sizeof(buf) - 1;
852
- time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
853
- long usec = NUM2LONG(rb_funcall2(obj, oj_tv_usec_id, 0, 0));
854
- char *dot = b - 7;
855
- long size;
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--, usec /= 10) {
859
- *b = '0' + (usec % 10);
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) {
@@ -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 %ld bytes from %s.\n", len, path);
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';
@@ -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
- if (0 != pi->encoding) {
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
- if (0 != pi->encoding) {
613
- obj = rb_str_new2(text);
614
- rb_enc_associate(obj, pi->encoding);
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
- if (0 != pi->encoding) {
629
- obj = rb_str_new2(text + 1);
630
- rb_enc_associate(obj, pi->encoding);
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
- if (0 != pi->encoding) {
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
- VALUE args[2];
753
- long v = 0;
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
- args[0] = LONG2NUM(v);
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 char
788
+ static uint32_t
805
789
  read_hex(ParseInfo pi, char *h) {
806
- uint8_t b = 0;
807
-
808
- if ('0' <= *h && *h <= '9') {
809
- b = *h - '0';
810
- } else if ('A' <= *h && *h <= 'F') {
811
- b = *h - 'A' + 10;
812
- } else if ('a' <= *h && *h <= 'f') {
813
- b = *h - 'a' + 10;
814
- } else {
815
- pi->s = h;
816
- raise_error("invalid hex character", pi->str, pi->s);
817
- }
818
- h++;
819
- b = b << 4;
820
- if ('0' <= *h && *h <= '9') {
821
- b += *h - '0';
822
- } else if ('A' <= *h && *h <= 'F') {
823
- b += *h - 'A' + 10;
824
- } else if ('a' <= *h && *h <= 'f') {
825
- b += *h - 'a' + 10;
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 = h;
828
- raise_error("invalid hex character", pi->str, pi->s);
840
+ raise_error("invalid Unicode", pi->str, pi->s);
829
841
  }
830
- return (char)b;
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
- *t = read_hex(pi, h);
863
- h += 2;
864
- if ('\0' != *t) {
865
- t++;
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
- *t = read_hex(pi, h);
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) {
@@ -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
- if (0 != copts.encoding) {
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
- if (0 != copts.encoding) {
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
- if (0 != copts->encoding) {
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
- oj_default_options.encoding = rb_enc_find("UTF-8");
754
+ oj_utf8_encoding = rb_enc_find("UTF-8");
794
755
  #endif
795
756
 
796
757
  oj_cache_new(&oj_class_cache);
@@ -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;
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '1.1.1'
4
+ VERSION = '1.2.0'
5
5
  end
@@ -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]
@@ -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
- dt = Time.now - start
51
- puts "%d Oj.load()s in %0.3f seconds or %0.1f loads/msec" % [iter, dt, iter/dt/1000.0]
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
- dt = Time.now - start
58
- puts "%d Yajl::Parser.parse()s in %0.3f seconds or %0.1f parses/msec" % [iter, dt, iter/dt/1000.0]
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
- dt = Time.now - start
67
- puts "%d Oj.dump()s in %0.3f seconds or %0.1f dumps/msec" % [iter, dt, iter/dt/1000.0]
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
- dt = Time.now - start
74
- puts "%d Yajl::Encoder.encode()s in %0.3f seconds or %0.1f encodes/msec" % [iter, dt, iter/dt/1000.0]
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]
@@ -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({ :encoding=>Encoding.find('UTF-8'),
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 = { :encoding => 'UTF-8' }
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 = { :encoding => nil, :ascii_only => false }
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.000000}, json)
212
+ assert_equal(%{1325775487.000000000}, json)
206
213
  end
207
214
  def test_time_object
208
215
  t = Time.now()
@@ -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.1.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-27 00:00:00.000000000 Z
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.21
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
@@ -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
@@ -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