oj 2.3.0 → 2.4.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0ac1090fe61f4056ff0fb5c838297e76f356fe8c
4
- data.tar.gz: e75c5ddbce19b8846c1cac9f5dca6994194aa9e7
3
+ metadata.gz: 873b6fade9db8e2c3e82c5a6329c4ceb09215d6d
4
+ data.tar.gz: 4f8074a9a5ac20d567c58d002323949fc08828ce
5
5
  SHA512:
6
- metadata.gz: f509d7b7dfbd8b848a0ce4b388e962853e3db6b7e2c050639a559a837964f767e1e279b53ef3cd29c7a6ceb569ac10522f9f3726f892f7a0c13e586688a28540
7
- data.tar.gz: f7bdb0ae179ac0e796625445447f1f0dbce85ff3b2d3af1181e16fea2128077112056c9e104b15a06e4d13b48ff630bec7fe675e316ea16500b4869edbf69287
6
+ metadata.gz: f82b5aedde38474ca0ddd6826f11ed4fa02bf17fbcf6a56ba14dca772c9e3000119d15d81c092f4dae9000a7dec6ca2c1a7c971a2b854a54d68a50586ab8f2d4
7
+ data.tar.gz: 8935ab29251ca82de0c9b770df24a0015bc6e46dc0f1cdfba6868ddc23b685306b8c4c4a1d182559ada38f654a8f9dbd199d4b68f9670a18d65fc7c45370eecf
data/README.md CHANGED
@@ -21,6 +21,14 @@ Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announceme
21
21
  [![Build Status](https://secure.travis-ci.org/ohler55/oj.png?branch=master)](http://travis-ci.org/ohler55/oj)
22
22
 
23
23
 
24
+ ### Current Release 2.4.0
25
+
26
+ - Merged in a PR to again allow strings with embedded nulls.
27
+
28
+ - Implemented StreamWriter to compliment the StringWriter.
29
+
30
+ - Fixed bug in the class cache hash function that showed up with the sparc compiler.
31
+
24
32
  ### Current Release 2.3.0
25
33
 
26
34
  - JRuby is no longer supported.
@@ -75,7 +75,7 @@ end_hash(struct _ParseInfo *pi) {
75
75
  if (Qundef != clas) { // else an error
76
76
  parent->val = rb_funcall(clas, oj_json_create_id, 1, parent->val);
77
77
  }
78
- if (parent->classname < pi->json || pi->cur < parent->classname) {
78
+ if (parent->classname < pi->json || pi->end < parent->classname) {
79
79
  xfree((char*)parent->classname);
80
80
  parent->classname = 0;
81
81
  }
@@ -96,11 +96,11 @@ oj_compat_parse(int argc, VALUE *argv, VALUE self) {
96
96
  pi.options = oj_default_options;
97
97
  oj_set_compat_callbacks(&pi);
98
98
 
99
- return oj_pi_parse(argc, argv, &pi, 0);
99
+ return oj_pi_parse(argc, argv, &pi, 0, 0);
100
100
  }
101
101
 
102
102
  VALUE
103
- oj_compat_parse_cstr(int argc, VALUE *argv, char *json) {
103
+ oj_compat_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
104
104
  struct _ParseInfo pi;
105
105
 
106
106
  pi.options = oj_default_options;
@@ -108,5 +108,5 @@ oj_compat_parse_cstr(int argc, VALUE *argv, char *json) {
108
108
  pi.end_hash = end_hash;
109
109
  pi.hash_set_cstr = hash_set_cstr;
110
110
 
111
- return oj_pi_parse(argc, argv, &pi, json);
111
+ return oj_pi_parse(argc, argv, &pi, json, len);
112
112
  }
@@ -426,7 +426,7 @@ dump_bignum(VALUE obj, Out out) {
426
426
  if (out->end - out->cur <= (long)cnt) {
427
427
  grow(out, cnt);
428
428
  }
429
- memcpy(out->cur, rb_string_value_cstr((VALUE*)&rs), cnt);
429
+ memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
430
430
  out->cur += cnt;
431
431
  *out->cur = '\0';
432
432
  }
@@ -554,12 +554,12 @@ dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
554
554
 
555
555
  static void
556
556
  dump_str_comp(VALUE obj, Out out) {
557
- dump_cstr(rb_string_value_cstr((VALUE*)&obj), RSTRING_LEN(obj), 0, 0, out);
557
+ dump_cstr(rb_string_value_ptr((VALUE*)&obj), RSTRING_LEN(obj), 0, 0, out);
558
558
  }
559
559
 
560
560
  static void
561
561
  dump_str_obj(VALUE obj, Out out) {
562
- const char *s = rb_string_value_cstr((VALUE*)&obj);
562
+ const char *s = rb_string_value_ptr((VALUE*)&obj);
563
563
  size_t len = RSTRING_LEN(obj);
564
564
  char s1 = s[1];
565
565
 
@@ -1023,7 +1023,7 @@ static void
1023
1023
  dump_ruby_time(VALUE obj, Out out) {
1024
1024
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1025
1025
 
1026
- dump_cstr(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1026
+ dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1027
1027
  }
1028
1028
 
1029
1029
  static void
@@ -1121,7 +1121,7 @@ dump_data_strict(VALUE obj, Out out) {
1121
1121
  if (oj_bigdecimal_class == clas) {
1122
1122
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1123
1123
 
1124
- dump_raw(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1124
+ dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1125
1125
  } else {
1126
1126
  raise_strict(obj);
1127
1127
  }
@@ -1134,7 +1134,7 @@ dump_data_null(VALUE obj, Out out) {
1134
1134
  if (oj_bigdecimal_class == clas) {
1135
1135
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1136
1136
 
1137
- dump_raw(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1137
+ dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1138
1138
  } else {
1139
1139
  dump_nil(out);
1140
1140
  }
@@ -1161,7 +1161,7 @@ dump_data_comp(VALUE obj, int depth, Out out) {
1161
1161
  last_obj = obj;
1162
1162
  rs = rb_funcall(obj, oj_to_json_id, 0);
1163
1163
  last_obj = Qundef;
1164
- s = rb_string_value_cstr((VALUE*)&rs);
1164
+ s = rb_string_value_ptr((VALUE*)&rs);
1165
1165
  len = (int)RSTRING_LEN(rs);
1166
1166
 
1167
1167
  if (out->end - out->cur <= len + 1) {
@@ -1184,14 +1184,14 @@ dump_data_comp(VALUE obj, int depth, Out out) {
1184
1184
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1185
1185
 
1186
1186
  if (Yes == out->opts->bigdec_as_num) {
1187
- dump_raw(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1187
+ dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1188
1188
  } else {
1189
- dump_cstr(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1189
+ dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1190
1190
  }
1191
1191
  } else {
1192
1192
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1193
1193
 
1194
- dump_cstr(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1194
+ dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1195
1195
  }
1196
1196
  }
1197
1197
  }
@@ -1221,9 +1221,9 @@ dump_data_obj(VALUE obj, int depth, Out out) {
1221
1221
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1222
1222
 
1223
1223
  if (Yes == out->opts->bigdec_as_num) {
1224
- dump_raw(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1224
+ dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1225
1225
  } else {
1226
- dump_cstr(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1226
+ dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1227
1227
  }
1228
1228
  } else {
1229
1229
  dump_nil(out);
@@ -1244,9 +1244,7 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
1244
1244
  }
1245
1245
  dump_hash(h, depth, out->opts->mode, out);
1246
1246
  } else if (rb_respond_to(obj, oj_as_json_id)) {
1247
- volatile VALUE js = rb_funcall(obj, oj_as_json_id, 0);
1248
-
1249
- dump_val(js, depth, out);
1247
+ dump_val(rb_funcall(obj, oj_as_json_id, 0), depth, out);
1250
1248
  } else if (rb_respond_to(obj, oj_to_json_id) && (!oj_rails_hack || last_obj != obj)) {
1251
1249
  volatile VALUE rs;
1252
1250
  const char *s;
@@ -1255,7 +1253,7 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
1255
1253
  last_obj = obj;
1256
1254
  rs = rb_funcall(obj, oj_to_json_id, 0);
1257
1255
  last_obj = Qundef;
1258
- s = rb_string_value_cstr((VALUE*)&rs);
1256
+ s = rb_string_value_ptr((VALUE*)&rs);
1259
1257
  len = (int)RSTRING_LEN(rs);
1260
1258
 
1261
1259
  if (out->end - out->cur <= len + 1) {
@@ -1271,14 +1269,14 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
1271
1269
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1272
1270
 
1273
1271
  if (Yes == out->opts->bigdec_as_num) {
1274
- dump_raw(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1272
+ dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1275
1273
  } else {
1276
- dump_cstr(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1274
+ dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1277
1275
  }
1278
1276
  } else if (oj_datetime_class == clas || oj_date_class == clas) {
1279
1277
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1280
1278
 
1281
- dump_cstr(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1279
+ dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1282
1280
  } else {
1283
1281
  Odd odd = oj_get_odd(clas);
1284
1282
 
@@ -1304,7 +1302,7 @@ dump_obj_obj(VALUE obj, int depth, Out out) {
1304
1302
  if (oj_bigdecimal_class == clas) {
1305
1303
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1306
1304
 
1307
- dump_raw(rb_string_value_cstr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1305
+ dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1308
1306
  } else {
1309
1307
  dump_obj_attrs(obj, clas, id, depth, out);
1310
1308
  }
@@ -1496,7 +1494,7 @@ dump_struct_comp(VALUE obj, int depth, Out out) {
1496
1494
  const char *s;
1497
1495
  int len;
1498
1496
 
1499
- s = rb_string_value_cstr((VALUE*)&rs);
1497
+ s = rb_string_value_ptr((VALUE*)&rs);
1500
1498
  len = (int)RSTRING_LEN(rs);
1501
1499
  if (out->end - out->cur <= len) {
1502
1500
  grow(out, len);
@@ -2035,8 +2033,6 @@ key_check(StrWriter sw, const char *key) {
2035
2033
 
2036
2034
  if (0 == key && (ObjectNew == type || ObjectType == type)) {
2037
2035
  rb_raise(rb_eStandardError, "Can not push onto an Object without a key.");
2038
- } else if (0 != key && (ArrayNew == type || ArrayType == type)) {
2039
- rb_raise(rb_eStandardError, "No key is needed to push into an array.");
2040
2036
  }
2041
2037
  }
2042
2038
 
@@ -2155,4 +2151,3 @@ oj_str_writer_pop_all(StrWriter sw) {
2155
2151
  oj_str_writer_pop(sw);
2156
2152
  }
2157
2153
  }
2158
-
@@ -56,27 +56,30 @@ struct _Hash intern_hash;
56
56
 
57
57
  static uint32_t
58
58
  hash_calc(const uint8_t *key, size_t len) {
59
- uint32_t k;
60
- uint32_t *kp = (uint32_t*)key;
61
- uint32_t *end = kp + (len / 4);
62
- uint32_t h = (uint32_t)len;
63
-
64
- for (; kp < end; kp++) {
65
- k = M * *kp;
59
+ const uint8_t *end = key + len;
60
+ const uint8_t *endless = key + (len / 4 * 4);
61
+ uint32_t h = (uint32_t)len;
62
+ uint32_t k;
63
+
64
+ while (key < endless) {
65
+ k = (uint32_t)*key++;
66
+ k |= (uint32_t)*key++ << 8;
67
+ k |= (uint32_t)*key++ << 16;
68
+ k |= (uint32_t)*key++ << 24;
69
+
70
+ k *= M;
66
71
  k ^= k >> 24;
67
72
  h *= M;
68
73
  h ^= k * M;
69
74
  }
70
- len = len - (len / 4 * 4);
71
- if (1 < len) {
72
- h ^= *(uint16_t*)kp << 8;
73
- len -= 2;
74
- key = (uint8_t*)(((uint16_t*)kp) + 1);
75
- } else {
76
- key = (uint8_t*)kp;
75
+ if (1 < end - key) {
76
+ uint16_t k16 = (uint16_t)*key++;
77
+
78
+ k16 |= (uint16_t)*key++ << 8;
79
+ h ^= k16 << 8;
77
80
  }
78
- if (0 < len) {
79
- h ^= key[0];
81
+ if (key < end) {
82
+ h ^= *key;
80
83
  }
81
84
  h *= M;
82
85
  h ^= h >> 13;
@@ -115,7 +118,7 @@ hash_get(Hash hash, const char *key, size_t len, VALUE **slotp, VALUE def_value)
115
118
 
116
119
  b->next = 0;
117
120
  bucket->next = b;
118
-
121
+ bucket = b;
119
122
  }
120
123
  bucket->key = oj_strndup(key, len);
121
124
  bucket->len = len;
@@ -224,7 +224,6 @@ static struct _StrLen data[] = {
224
224
  { "StandardError", 13 },
225
225
  { "Interrupt", 9 },
226
226
  { "SignalException", 15 },
227
- { "#<Class:0x007fb0510c8790>", 25 },
228
227
  { "SystemExit", 10 },
229
228
  { "Exception", 9 },
230
229
  { "Symbol", 6 },
@@ -460,6 +459,7 @@ perf() {
460
459
  VALUE *slot = 0;
461
460
  uint64_t dt, start;
462
461
  int i, iter = 1000000;
462
+ int dataCnt = sizeof(data) / sizeof(*data);
463
463
 
464
464
  oj_hash_init();
465
465
  start = micro_time();
@@ -476,9 +476,9 @@ perf() {
476
476
  }
477
477
  dt = micro_time() - start;
478
478
  #if IS_WINDOWS
479
- printf("%d iterations took %ld msecs\n", iter, (long)(dt / 1000));
479
+ printf("%d iterations took %ld msecs, %ld gets/msec\n", iter, (long)(dt / 1000), (long)(iter * dataCnt / (dt / 1000)));
480
480
  #else
481
- printf("%d iterations took %"PRIu64" msecs\n", iter, dt / 1000);
481
+ printf("%d iterations took %"PRIu64" msecs, %ld gets/msec\n", iter, dt / 1000, (long)(iter * dataCnt / (dt / 1000)));
482
482
  #endif
483
483
  }
484
484
 
@@ -490,23 +490,23 @@ oj_hash_test() {
490
490
 
491
491
  oj_hash_init();
492
492
  for (d = data; 0 != d->str; d++) {
493
- /*printf("*** hash_get on %s\n", *d);*/
493
+ char *s = oj_strndup(d->str, d->len);
494
494
  v = oj_class_hash_get(d->str, d->len, &slot);
495
- if (Qundef == v) {
495
+ if (Qnil == v) {
496
496
  if (0 == slot) {
497
- /*printf("*** failed to get a slot for %s\n", *d); */
497
+ printf("*** failed to get a slot for %s\n", s);
498
498
  } else {
499
- /*printf("*** added '%s' to hash\n", *d); */
500
499
  v = ID2SYM(rb_intern(d->str));
501
500
  *slot = v;
502
501
  }
503
502
  } else {
504
503
  VALUE rs = rb_funcall2(v, rb_intern("to_s"), 0, 0);
505
504
 
506
- printf("*** get on '%s' returned '%s' (%s)\n", d->str, StringValuePtr(rs), rb_class2name(rb_obj_class(v)));
505
+ printf("*** get on '%s' returned '%s' (%s)\n", s, StringValuePtr(rs), rb_class2name(rb_obj_class(v)));
507
506
  }
508
507
  /*oj_hash_print(c);*/
509
508
  }
509
+ printf("*** ---------- hash table ------------\n");
510
510
  oj_hash_print();
511
511
  perf();
512
512
  }
@@ -511,11 +511,11 @@ oj_object_parse(int argc, VALUE *argv, VALUE self) {
511
511
  pi.add_cstr = add_cstr;
512
512
  pi.array_append_cstr = array_append_cstr;
513
513
 
514
- return oj_pi_parse(argc, argv, &pi, 0);
514
+ return oj_pi_parse(argc, argv, &pi, 0, 0);
515
515
  }
516
516
 
517
517
  VALUE
518
- oj_object_parse_cstr(int argc, VALUE *argv, char *json) {
518
+ oj_object_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
519
519
  struct _ParseInfo pi;
520
520
 
521
521
  pi.options = oj_default_options;
@@ -528,5 +528,5 @@ oj_object_parse_cstr(int argc, VALUE *argv, char *json) {
528
528
  pi.add_cstr = add_cstr;
529
529
  pi.array_append_cstr = array_append_cstr;
530
530
 
531
- return oj_pi_parse(argc, argv, &pi, json);
531
+ return oj_pi_parse(argc, argv, &pi, json, len);
532
532
  }
@@ -693,15 +693,15 @@ load_file(int argc, VALUE *argv, VALUE self) {
693
693
  // The json string is freed in the parser when it is finished with it.
694
694
  switch (mode) {
695
695
  case StrictMode:
696
- return oj_strict_parse_cstr(argc, argv, json);
696
+ return oj_strict_parse_cstr(argc, argv, json, len);
697
697
  case NullMode:
698
698
  case CompatMode:
699
- return oj_compat_parse_cstr(argc, argv, json);
699
+ return oj_compat_parse_cstr(argc, argv, json, len);
700
700
  case ObjectMode:
701
701
  default:
702
702
  break;
703
703
  }
704
- return oj_object_parse_cstr(argc, argv, json);
704
+ return oj_object_parse_cstr(argc, argv, json, len);
705
705
  }
706
706
 
707
707
  /* call-seq: safe_load(doc)
@@ -725,7 +725,7 @@ safe_load(VALUE self, VALUE doc) {
725
725
  oj_set_strict_callbacks(&pi);
726
726
  *args = doc;
727
727
 
728
- return oj_pi_parse(1, args, &pi, 0);
728
+ return oj_pi_parse(1, args, &pi, 0, 0);
729
729
  }
730
730
 
731
731
  /* call-seq: saj_parse(handler, io)
@@ -850,15 +850,8 @@ str_writer_free(void *ptr) {
850
850
  * construction is complete will return the document in it's current state.
851
851
  */
852
852
 
853
- /* call-seq: new(options)
854
- *
855
- * Creates a new StringWriter.
856
- * @param [Hash] options formating options
857
- */
858
- static VALUE
859
- str_writer_new(int argc, VALUE *argv, VALUE self) {
860
- StrWriter sw = ALLOC(struct _StrWriter);
861
-
853
+ static void
854
+ str_writer_init(StrWriter sw) {
862
855
  sw->opts = oj_default_options;
863
856
  sw->depth = 0;
864
857
  sw->types = ALLOC_N(char, 256);
@@ -871,12 +864,23 @@ str_writer_new(int argc, VALUE *argv, VALUE self) {
871
864
  *sw->out.cur = '\0';
872
865
  sw->out.circ_cnt = 0;
873
866
  sw->out.hash_cnt = 0;
874
- if (1 == argc) {
875
- oj_parse_options(argv[0], &sw->opts);
876
- }
877
867
  sw->out.opts = &sw->opts;
878
868
  sw->out.indent = sw->opts.indent;
869
+ }
879
870
 
871
+ /* call-seq: new(options)
872
+ *
873
+ * Creates a new StringWriter.
874
+ * @param [Hash] options formating options
875
+ */
876
+ static VALUE
877
+ str_writer_new(int argc, VALUE *argv, VALUE self) {
878
+ StrWriter sw = ALLOC(struct _StrWriter);
879
+
880
+ str_writer_init(sw);
881
+ if (1 == argc) {
882
+ oj_parse_options(argv[0], &sw->opts);
883
+ }
880
884
  return Data_Wrap_Struct(oj_string_writer_class, 0, str_writer_free, sw);
881
885
  }
882
886
 
@@ -893,8 +897,12 @@ str_writer_push_object(int argc, VALUE *argv, VALUE self) {
893
897
  oj_str_writer_push_object((StrWriter)DATA_PTR(self), 0);
894
898
  break;
895
899
  case 1:
896
- rb_check_type(argv[0], T_STRING);
897
- oj_str_writer_push_object((StrWriter)DATA_PTR(self), StringValuePtr(argv[0]));
900
+ if (Qnil == argv[0]) {
901
+ oj_str_writer_push_object((StrWriter)DATA_PTR(self), 0);
902
+ } else {
903
+ rb_check_type(argv[0], T_STRING);
904
+ oj_str_writer_push_object((StrWriter)DATA_PTR(self), StringValuePtr(argv[0]));
905
+ }
898
906
  break;
899
907
  default:
900
908
  rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'.");
@@ -916,8 +924,12 @@ str_writer_push_array(int argc, VALUE *argv, VALUE self) {
916
924
  oj_str_writer_push_array((StrWriter)DATA_PTR(self), 0);
917
925
  break;
918
926
  case 1:
919
- rb_check_type(argv[0], T_STRING);
920
- oj_str_writer_push_array((StrWriter)DATA_PTR(self), StringValuePtr(argv[0]));
927
+ if (Qnil == argv[0]) {
928
+ oj_str_writer_push_array((StrWriter)DATA_PTR(self), 0);
929
+ } else {
930
+ rb_check_type(argv[0], T_STRING);
931
+ oj_str_writer_push_array((StrWriter)DATA_PTR(self), StringValuePtr(argv[0]));
932
+ }
921
933
  break;
922
934
  default:
923
935
  rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'.");
@@ -939,8 +951,12 @@ str_writer_push_value(int argc, VALUE *argv, VALUE self) {
939
951
  oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
940
952
  break;
941
953
  case 2:
942
- rb_check_type(argv[1], T_STRING);
943
- oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, StringValuePtr(argv[1]));
954
+ if (Qnil == argv[1]) {
955
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
956
+ } else {
957
+ rb_check_type(argv[1], T_STRING);
958
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, StringValuePtr(argv[1]));
959
+ }
944
960
  break;
945
961
  default:
946
962
  rb_raise(rb_eArgError, "Wrong number of argument to 'push_value'.");
@@ -1000,6 +1016,217 @@ str_writer_to_s(VALUE self) {
1000
1016
  return oj_encode(rstr);
1001
1017
  }
1002
1018
 
1019
+ // StreamWriter
1020
+
1021
+ static void
1022
+ stream_writer_free(void *ptr) {
1023
+ StreamWriter sw;
1024
+
1025
+ if (0 == ptr) {
1026
+ return;
1027
+ }
1028
+ sw = (StreamWriter)ptr;
1029
+ xfree(sw->sw.out.buf);
1030
+ xfree(sw->sw.types);
1031
+ xfree(ptr);
1032
+ }
1033
+
1034
+ static void
1035
+ stream_writer_write(StreamWriter sw) {
1036
+ ssize_t size = sw->sw.out.cur - sw->sw.out.buf;
1037
+
1038
+ switch (sw->type) {
1039
+ case STRING_IO:
1040
+ rb_funcall(sw->stream, oj_write_id, 1, rb_str_new(sw->sw.out.buf, size));
1041
+ break;
1042
+ case STREAM_IO:
1043
+ rb_funcall(sw->stream, oj_write_id, 1, rb_str_new(sw->sw.out.buf, size));
1044
+ break;
1045
+ case FILE_IO:
1046
+ if (size != write(sw->fd, sw->sw.out.buf, size)) {
1047
+ rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", errno, strerror(errno));
1048
+ }
1049
+ break;
1050
+ default:
1051
+ rb_raise(rb_eArgError, "expected an IO Object.");
1052
+ }
1053
+ }
1054
+
1055
+ static void
1056
+ stream_writer_reset_buf(StreamWriter sw) {
1057
+ sw->sw.out.cur = sw->sw.out.buf;
1058
+ *sw->sw.out.cur = '\0';
1059
+ }
1060
+
1061
+ /* call-seq: new(options)
1062
+ *
1063
+ * Creates a new StreamWriter.
1064
+ * @param [Hash] options formating options
1065
+ */
1066
+ /* call-seq: new(options)
1067
+ *
1068
+ * Creates a new StreamWriter.
1069
+ * @param [Hash] options formating options
1070
+ */
1071
+ static VALUE
1072
+ stream_writer_new(int argc, VALUE *argv, VALUE self) {
1073
+ StreamWriterType type = STREAM_IO;
1074
+ int fd = 0;
1075
+ VALUE stream = argv[0];
1076
+ VALUE clas = rb_obj_class(stream);
1077
+ VALUE s;
1078
+ StreamWriter sw;
1079
+
1080
+ if (oj_stringio_class == clas) {
1081
+ type = STRING_IO;
1082
+ #ifndef JRUBY_RUBY
1083
+ #if !IS_WINDOWS
1084
+ } else if (rb_respond_to(stream, oj_fileno_id) && Qnil != (s = rb_funcall(stream, oj_fileno_id, 0))) {
1085
+ type = FILE_IO;
1086
+ fd = FIX2INT(s);
1087
+ #endif
1088
+ #endif
1089
+ } else if (rb_respond_to(stream, oj_write_id)) {
1090
+ type = STREAM_IO;
1091
+ } else {
1092
+ rb_raise(rb_eArgError, "expected an IO Object.");
1093
+ }
1094
+ sw = ALLOC(struct _StreamWriter);
1095
+ str_writer_init(&sw->sw);
1096
+ if (1 == argc) {
1097
+ oj_parse_options(argv[0], &sw->sw.opts);
1098
+ }
1099
+ sw->stream = stream;
1100
+ sw->type = type;
1101
+ sw->fd = fd;
1102
+
1103
+ return Data_Wrap_Struct(oj_stream_writer_class, 0, stream_writer_free, sw);
1104
+ }
1105
+
1106
+ /* call-seq: push_object(key=nil)
1107
+ *
1108
+ * Pushes an object onto the JSON document. Future pushes will be to this object
1109
+ * until a pop() is called.
1110
+ * @param [String] key the key if adding to an object in the JSON document
1111
+ */
1112
+ static VALUE
1113
+ stream_writer_push_object(int argc, VALUE *argv, VALUE self) {
1114
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
1115
+
1116
+ stream_writer_reset_buf(sw);
1117
+ switch (argc) {
1118
+ case 0:
1119
+ oj_str_writer_push_object(&sw->sw, 0);
1120
+ break;
1121
+ case 1:
1122
+ if (Qnil == argv[0]) {
1123
+ oj_str_writer_push_object(&sw->sw, 0);
1124
+ } else {
1125
+ rb_check_type(argv[0], T_STRING);
1126
+ oj_str_writer_push_object(&sw->sw, StringValuePtr(argv[0]));
1127
+ }
1128
+ break;
1129
+ default:
1130
+ rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'.");
1131
+ break;
1132
+ }
1133
+ stream_writer_write(sw);
1134
+ return Qnil;
1135
+ }
1136
+
1137
+ /* call-seq: push_array(key=nil)
1138
+ *
1139
+ * Pushes an array onto the JSON document. Future pushes will be to this object
1140
+ * until a pop() is called.
1141
+ * @param [String] key the key if adding to an object in the JSON document
1142
+ */
1143
+ static VALUE
1144
+ stream_writer_push_array(int argc, VALUE *argv, VALUE self) {
1145
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
1146
+
1147
+ stream_writer_reset_buf(sw);
1148
+ switch (argc) {
1149
+ case 0:
1150
+ oj_str_writer_push_array(&sw->sw, 0);
1151
+ break;
1152
+ case 1:
1153
+ if (Qnil == argv[0]) {
1154
+ oj_str_writer_push_array(&sw->sw, 0);
1155
+ } else {
1156
+ rb_check_type(argv[0], T_STRING);
1157
+ oj_str_writer_push_array(&sw->sw, StringValuePtr(argv[0]));
1158
+ }
1159
+ break;
1160
+ default:
1161
+ rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'.");
1162
+ break;
1163
+ }
1164
+ stream_writer_write(sw);
1165
+ return Qnil;
1166
+ }
1167
+
1168
+ /* call-seq: push_value(value, key=nil)
1169
+ *
1170
+ * Pushes a value onto the JSON document.
1171
+ * @param [Object] value value to add to the JSON document
1172
+ * @param [String] key the key if adding to an object in the JSON document
1173
+ */
1174
+ static VALUE
1175
+ stream_writer_push_value(int argc, VALUE *argv, VALUE self) {
1176
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
1177
+
1178
+ stream_writer_reset_buf(sw);
1179
+ switch (argc) {
1180
+ case 1:
1181
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
1182
+ break;
1183
+ case 2:
1184
+ if (Qnil == argv[0]) {
1185
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
1186
+ } else {
1187
+ rb_check_type(argv[1], T_STRING);
1188
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, StringValuePtr(argv[1]));
1189
+ }
1190
+ break;
1191
+ default:
1192
+ rb_raise(rb_eArgError, "Wrong number of argument to 'push_value'.");
1193
+ break;
1194
+ }
1195
+ stream_writer_write(sw);
1196
+ return Qnil;
1197
+ }
1198
+
1199
+ /* call-seq: pop()
1200
+ *
1201
+ * Pops up a level in the JSON document closing the array or object that is
1202
+ * currently open.
1203
+ */
1204
+ static VALUE
1205
+ stream_writer_pop(VALUE self) {
1206
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
1207
+
1208
+ stream_writer_reset_buf(sw);
1209
+ oj_str_writer_pop(&sw->sw);
1210
+ stream_writer_write(sw);
1211
+ return Qnil;
1212
+ }
1213
+
1214
+ /* call-seq: pop_all()
1215
+ *
1216
+ * Pops all level in the JSON document closing all the array or object that is
1217
+ * currently open.
1218
+ */
1219
+ static VALUE
1220
+ stream_writer_pop_all(VALUE self) {
1221
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
1222
+
1223
+ stream_writer_reset_buf(sw);
1224
+ oj_str_writer_pop_all(&sw->sw);
1225
+ stream_writer_write(sw);
1226
+
1227
+ return Qnil;
1228
+ }
1229
+
1003
1230
  // Mimic JSON section
1004
1231
 
1005
1232
  static VALUE
@@ -1227,7 +1454,7 @@ mimic_parse(int argc, VALUE *argv, VALUE self) {
1227
1454
  }
1228
1455
  *args = *argv;
1229
1456
 
1230
- return oj_pi_parse(1, args, &pi, 0);
1457
+ return oj_pi_parse(1, args, &pi, 0, 0);
1231
1458
  }
1232
1459
 
1233
1460
  static VALUE
@@ -1351,7 +1578,6 @@ define_mimic_json(int argc, VALUE *argv, VALUE self) {
1351
1578
  return mimic;
1352
1579
  }
1353
1580
 
1354
- /*
1355
1581
  extern void oj_hash_test();
1356
1582
 
1357
1583
  static VALUE
@@ -1359,7 +1585,6 @@ hash_test(VALUE self) {
1359
1585
  oj_hash_test();
1360
1586
  return Qnil;
1361
1587
  }
1362
- */
1363
1588
 
1364
1589
  #if !HAS_ENCODING_SUPPORT
1365
1590
  static VALUE
@@ -1401,7 +1626,13 @@ void Init_oj() {
1401
1626
  rb_define_method(oj_string_writer_class, "reset", str_writer_reset, 0);
1402
1627
  rb_define_method(oj_string_writer_class, "to_s", str_writer_to_s, 0);
1403
1628
 
1404
- //oj_stream_writer_class = rb_define_class_under(Oj, "StreamWriter", rb_cObject);
1629
+ oj_stream_writer_class = rb_define_class_under(Oj, "StreamWriter", rb_cObject);
1630
+ rb_define_module_function(oj_stream_writer_class, "new", stream_writer_new, -1);
1631
+ rb_define_method(oj_stream_writer_class, "push_object", stream_writer_push_object, -1);
1632
+ rb_define_method(oj_stream_writer_class, "push_array", stream_writer_push_array, -1);
1633
+ rb_define_method(oj_stream_writer_class, "push_value", stream_writer_push_value, -1);
1634
+ rb_define_method(oj_stream_writer_class, "pop", stream_writer_pop, 0);
1635
+ rb_define_method(oj_stream_writer_class, "pop_all", stream_writer_pop_all, 0);
1405
1636
 
1406
1637
  rb_require("time");
1407
1638
  rb_require("date");
@@ -1419,7 +1650,7 @@ void Init_oj() {
1419
1650
  oj_utf8_encoding = Qnil;
1420
1651
  #endif
1421
1652
 
1422
- //rb_define_module_function(Oj, "hash_test", hash_test, 0);
1653
+ rb_define_module_function(Oj, "hash_test", hash_test, 0);
1423
1654
 
1424
1655
  rb_define_module_function(Oj, "default_options", get_def_opts, 0);
1425
1656
  rb_define_module_function(Oj, "default_options=", set_def_opts, 1);
@@ -105,6 +105,12 @@ typedef enum {
105
105
  ObjectType = 'o',
106
106
  } DumpType;
107
107
 
108
+ typedef enum {
109
+ STRING_IO = 'c',
110
+ STREAM_IO = 's',
111
+ FILE_IO = 'f',
112
+ } StreamWriterType;
113
+
108
114
  typedef struct _DumpOpts {
109
115
  const char *indent;
110
116
  const char *before_sep;
@@ -157,6 +163,13 @@ typedef struct _StrWriter {
157
163
  char *types_end;
158
164
  } *StrWriter;
159
165
 
166
+ typedef struct _StreamWriter {
167
+ struct _StrWriter sw;
168
+ StreamWriterType type;
169
+ VALUE stream;
170
+ int fd;
171
+ } *StreamWriter;
172
+
160
173
  enum {
161
174
  STR_VAL = 0x00,
162
175
  COL_VAL = 0x01,
@@ -186,9 +199,9 @@ extern VALUE oj_strict_parse(int argc, VALUE *argv, VALUE self);
186
199
  extern VALUE oj_compat_parse(int argc, VALUE *argv, VALUE self);
187
200
  extern VALUE oj_object_parse(int argc, VALUE *argv, VALUE self);
188
201
 
189
- extern VALUE oj_strict_parse_cstr(int argc, VALUE *argv, char *json);
190
- extern VALUE oj_compat_parse_cstr(int argc, VALUE *argv, char *json);
191
- extern VALUE oj_object_parse_cstr(int argc, VALUE *argv, char *json);
202
+ extern VALUE oj_strict_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
203
+ extern VALUE oj_compat_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
204
+ extern VALUE oj_object_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
192
205
 
193
206
  extern void oj_parse_options(VALUE ropts, Options copts);
194
207
 
@@ -719,7 +719,7 @@ protect_parse(VALUE pip) {
719
719
  }
720
720
 
721
721
  VALUE
722
- oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json) {
722
+ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len) {
723
723
  char *buf = 0;
724
724
  volatile VALUE input;
725
725
  volatile VALUE wrapped_stack;
@@ -737,9 +737,11 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json) {
737
737
  pi->cbc = (void*)0;
738
738
  if (0 != json) {
739
739
  pi->json = json;
740
+ pi->end = json + len;
740
741
  free_json = 1;
741
742
  } else if (rb_type(input) == T_STRING) {
742
743
  pi->json = rb_string_value_cstr((VALUE*)&input);
744
+ pi->end = pi->json + RSTRING_LEN(input);
743
745
  } else {
744
746
  VALUE clas = rb_obj_class(input);
745
747
  volatile VALUE s;
@@ -747,6 +749,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json) {
747
749
  if (oj_stringio_class == clas) {
748
750
  s = rb_funcall2(input, oj_string_id, 0, 0);
749
751
  pi->json = rb_string_value_cstr((VALUE*)&s);
752
+ pi->end = pi->json + RSTRING_LEN(s);
750
753
  #if !IS_WINDOWS
751
754
  } else if (rb_respond_to(input, oj_fileno_id) && Qnil != (s = rb_funcall(input, oj_fileno_id, 0))) {
752
755
  int fd = FIX2INT(s);
@@ -756,6 +759,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json) {
756
759
  lseek(fd, 0, SEEK_SET);
757
760
  buf = ALLOC_N(char, len + 1);
758
761
  pi->json = buf;
762
+ pi->end = buf + len;
759
763
  if (0 >= (cnt = read(fd, (char*)pi->json, len)) || cnt != (ssize_t)len) {
760
764
  if (0 != buf) {
761
765
  xfree(buf);
@@ -771,6 +775,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json) {
771
775
  } else if (rb_respond_to(input, oj_read_id)) {
772
776
  s = rb_funcall2(input, oj_read_id, 0, 0);
773
777
  pi->json = rb_string_value_cstr((VALUE*)&s);
778
+ pi->end = pi->json + RSTRING_LEN(s);
774
779
  } else {
775
780
  rb_raise(rb_eArgError, "strict_parse() expected a String or IO Object.");
776
781
  }
@@ -56,6 +56,7 @@ typedef struct _NumInfo {
56
56
  typedef struct _ParseInfo {
57
57
  const char *json;
58
58
  const char *cur;
59
+ const char *end;
59
60
  struct _Err err;
60
61
  struct _Options options;
61
62
  void *cbc;
@@ -81,7 +82,7 @@ typedef struct _ParseInfo {
81
82
 
82
83
  extern void oj_parse2(ParseInfo pi);
83
84
  extern void oj_set_error_at(ParseInfo pi, VALUE err_clas, const char* file, int line, const char *format, ...);
84
- extern VALUE oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json);
85
+ extern VALUE oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len);
85
86
  extern VALUE oj_num_as_value(NumInfo ni);
86
87
 
87
88
  extern void oj_set_strict_callbacks(ParseInfo pi);
@@ -145,15 +145,15 @@ oj_strict_parse(int argc, VALUE *argv, VALUE self) {
145
145
  pi.options = oj_default_options;
146
146
  oj_set_strict_callbacks(&pi);
147
147
 
148
- return oj_pi_parse(argc, argv, &pi, 0);
148
+ return oj_pi_parse(argc, argv, &pi, 0, 0);
149
149
  }
150
150
 
151
151
  VALUE
152
- oj_strict_parse_cstr(int argc, VALUE *argv, char *json) {
152
+ oj_strict_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
153
153
  struct _ParseInfo pi;
154
154
 
155
155
  pi.options = oj_default_options;
156
156
  oj_set_strict_callbacks(&pi);
157
157
 
158
- return oj_pi_parse(argc, argv, &pi, json);
158
+ return oj_pi_parse(argc, argv, &pi, json, len);
159
159
  }
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '2.3.0'
4
+ VERSION = '2.4.0'
5
5
  end
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
5
+ # required. That can be set in the RUBYOPT environment variable.
6
+ # export RUBYOPT=-w
7
+
8
+ $VERBOSE = true
9
+
10
+ $: << File.join(File.dirname(__FILE__), "../lib")
11
+ $: << File.join(File.dirname(__FILE__), "../ext")
12
+
13
+ require 'test/unit'
14
+ require 'stringio'
15
+ require 'date'
16
+ require 'bigdecimal'
17
+ require 'oj'
18
+
19
+ $ruby = RUBY_DESCRIPTION.split(' ')[0]
20
+ $ruby = 'ree' if 'ruby' == $ruby && RUBY_DESCRIPTION.include?('Ruby Enterprise Edition')
21
+
22
+ def hash_eql(h1, h2)
23
+ return false if h1.size != h2.size
24
+ h1.keys.each do |k|
25
+ return false unless h1[k] == h2[k]
26
+ end
27
+ true
28
+ end
29
+
30
+ class Jam
31
+ attr_accessor :x, :y
32
+
33
+ def initialize(x, y)
34
+ @x = x
35
+ @y = y
36
+ end
37
+
38
+ def eql?(o)
39
+ self.class == o.class && @x == o.x && @y == o.y
40
+ end
41
+ alias == eql?
42
+
43
+ end# Jam
44
+
45
+ # contributed by sauliusg to fix as_json
46
+ class Orange < Jam
47
+ def initialize(x, y)
48
+ super
49
+ end
50
+
51
+ def as_json()
52
+ puts "Orange.as_json called"
53
+ { :json_class => self.class,
54
+ :x => @x,
55
+ :y => @y }
56
+ end
57
+
58
+ def self.json_create(h)
59
+ puts "Orange.json_create"
60
+ self.new(h['x'], h['y'])
61
+ end
62
+ end
63
+
64
+ class DebJuice < ::Test::Unit::TestCase
65
+
66
+ def test_class_hash
67
+ Oj.hash_test()
68
+ end
69
+
70
+ def test_as_json_object_compat_hash_cached
71
+ Oj.default_options = { :mode => :compat, :class_cache => true }
72
+ obj = Orange.new(true, 58)
73
+ puts "dumping compat with cache"
74
+ json = Oj.dump(obj, :indent => 2)
75
+ assert(!json.nil?)
76
+ dump_and_load(obj, true)
77
+ end
78
+
79
+ def dump_and_load(obj, trace=false)
80
+ puts "dumping"
81
+ json = Oj.dump(obj, :indent => 2)
82
+ puts json if trace
83
+ puts "loading"
84
+ loaded = Oj.load(json);
85
+ puts "done"
86
+ assert_equal(obj, loaded)
87
+ loaded
88
+ end
89
+
90
+ end
@@ -23,7 +23,6 @@ class OjWriter < ::Test::Unit::TestCase
23
23
  w.push_array()
24
24
  w.pop()
25
25
  assert_equal('[]', w.to_s)
26
-
27
26
  end
28
27
 
29
28
  def test_string_writer_nested_array
@@ -100,18 +99,6 @@ class OjWriter < ::Test::Unit::TestCase
100
99
  assert(false, "*** expected an exception")
101
100
  end
102
101
 
103
- def test_string_writer_array_with_key
104
- w = Oj::StringWriter.new(:indent => 0)
105
- w.push_array()
106
- begin
107
- w.push_value(59, 'x')
108
- rescue Exception
109
- assert(true)
110
- return
111
- end
112
- assert(false, "*** expected an exception")
113
- end
114
-
115
102
  def test_string_writer_deep
116
103
  cnt = 1000
117
104
  w = Oj::StringWriter.new(:indent => 0)
@@ -145,4 +132,55 @@ class OjWriter < ::Test::Unit::TestCase
145
132
  assert_equal('', w.to_s)
146
133
  end
147
134
 
135
+ # Stream Writer
136
+
137
+ def test_stream_writer_empty_array
138
+ output = StringIO.open("", "w+")
139
+ w = Oj::StreamWriter.new(output, :indent => 0)
140
+ w.push_array()
141
+ w.pop()
142
+ assert_equal('[]', output.string())
143
+ end
144
+
145
+ def test_stream_writer_mixed_stringio
146
+ output = StringIO.open("", "w+")
147
+ w = Oj::StreamWriter.new(output, :indent => 0)
148
+ w.push_object()
149
+ w.push_object("a1")
150
+ w.pop()
151
+ w.push_object("a2")
152
+ w.push_array("b")
153
+ w.push_value(7)
154
+ w.push_value(true)
155
+ w.push_value("string")
156
+ w.pop()
157
+ w.pop()
158
+ w.push_object("a3")
159
+ w.pop()
160
+ w.pop()
161
+ assert_equal('{"a1":{},"a2":{"b":[7,true,"string"]},"a3":{}}', output.string())
162
+ end
163
+
164
+ def test_stream_writer_mixed_file
165
+ filename = 'open_file_writer_test.json'
166
+ File.open(filename, "w") do |f|
167
+ w = Oj::StreamWriter.new(f, :indent => 0)
168
+ w.push_object()
169
+ w.push_object("a1")
170
+ w.pop()
171
+ w.push_object("a2")
172
+ w.push_array("b")
173
+ w.push_value(7)
174
+ w.push_value(true)
175
+ w.push_value("string")
176
+ w.pop()
177
+ w.pop()
178
+ w.push_object("a3")
179
+ w.pop()
180
+ w.pop()
181
+ end
182
+ content = File.read(filename)
183
+ assert_equal('{"a1":{},"a2":{"b":[7,true,"string"]},"a3":{}}', content)
184
+ end
185
+
148
186
  end # OjWriter
@@ -215,6 +215,7 @@ class Juice < ::Test::Unit::TestCase
215
215
  dump_and_load('abc', false)
216
216
  dump_and_load("abc\ndef", false)
217
217
  dump_and_load("a\u0041", false)
218
+ assert_equal("a\u0000a", dump_and_load("a\u0000a", false))
218
219
  end
219
220
 
220
221
  def test_string_object
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-01 00:00:00.000000000 Z
11
+ date: 2013-12-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'The fastest JSON parser and object serializer. '
14
14
  email: peter@ohler.com
@@ -56,6 +56,7 @@ files:
56
56
  - ext/oj/val_stack.c
57
57
  - test/a.rb
58
58
  - test/bug.rb
59
+ - test/debian_test.rb
59
60
  - test/e.rb
60
61
  - test/files.rb
61
62
  - test/foo.rb