oj 3.11.5 → 3.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1421 -0
  3. data/README.md +19 -5
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +20 -6
  6. data/ext/oj/cache.c +329 -0
  7. data/ext/oj/cache.h +22 -0
  8. data/ext/oj/cache8.c +10 -9
  9. data/ext/oj/circarray.c +8 -6
  10. data/ext/oj/circarray.h +2 -2
  11. data/ext/oj/code.c +19 -33
  12. data/ext/oj/code.h +2 -2
  13. data/ext/oj/compat.c +27 -77
  14. data/ext/oj/custom.c +86 -179
  15. data/ext/oj/debug.c +126 -0
  16. data/ext/oj/dump.c +256 -249
  17. data/ext/oj/dump.h +26 -12
  18. data/ext/oj/dump_compat.c +565 -642
  19. data/ext/oj/dump_leaf.c +17 -63
  20. data/ext/oj/dump_object.c +65 -187
  21. data/ext/oj/dump_strict.c +27 -51
  22. data/ext/oj/encoder.c +43 -0
  23. data/ext/oj/err.c +2 -13
  24. data/ext/oj/err.h +24 -8
  25. data/ext/oj/extconf.rb +21 -6
  26. data/ext/oj/fast.c +149 -149
  27. data/ext/oj/intern.c +313 -0
  28. data/ext/oj/intern.h +22 -0
  29. data/ext/oj/mem.c +318 -0
  30. data/ext/oj/mem.h +53 -0
  31. data/ext/oj/mimic_json.c +121 -106
  32. data/ext/oj/object.c +85 -162
  33. data/ext/oj/odd.c +89 -67
  34. data/ext/oj/odd.h +15 -15
  35. data/ext/oj/oj.c +542 -411
  36. data/ext/oj/oj.h +99 -73
  37. data/ext/oj/parse.c +175 -187
  38. data/ext/oj/parse.h +26 -24
  39. data/ext/oj/parser.c +1600 -0
  40. data/ext/oj/parser.h +101 -0
  41. data/ext/oj/rails.c +112 -159
  42. data/ext/oj/rails.h +1 -1
  43. data/ext/oj/reader.c +11 -14
  44. data/ext/oj/reader.h +4 -2
  45. data/ext/oj/resolve.c +5 -24
  46. data/ext/oj/rxclass.c +7 -6
  47. data/ext/oj/rxclass.h +1 -1
  48. data/ext/oj/saj.c +22 -33
  49. data/ext/oj/saj2.c +584 -0
  50. data/ext/oj/saj2.h +23 -0
  51. data/ext/oj/scp.c +5 -28
  52. data/ext/oj/sparse.c +28 -72
  53. data/ext/oj/stream_writer.c +50 -40
  54. data/ext/oj/strict.c +56 -61
  55. data/ext/oj/string_writer.c +72 -39
  56. data/ext/oj/trace.h +31 -4
  57. data/ext/oj/usual.c +1218 -0
  58. data/ext/oj/usual.h +69 -0
  59. data/ext/oj/util.h +1 -1
  60. data/ext/oj/val_stack.c +14 -3
  61. data/ext/oj/val_stack.h +8 -7
  62. data/ext/oj/validate.c +46 -0
  63. data/ext/oj/wab.c +63 -88
  64. data/lib/oj/active_support_helper.rb +1 -3
  65. data/lib/oj/bag.rb +7 -1
  66. data/lib/oj/easy_hash.rb +4 -5
  67. data/lib/oj/error.rb +1 -2
  68. data/lib/oj/json.rb +162 -150
  69. data/lib/oj/mimic.rb +9 -7
  70. data/lib/oj/saj.rb +20 -6
  71. data/lib/oj/schandler.rb +5 -4
  72. data/lib/oj/state.rb +12 -8
  73. data/lib/oj/version.rb +1 -2
  74. data/lib/oj.rb +2 -0
  75. data/pages/Compatibility.md +1 -1
  76. data/pages/InstallOptions.md +20 -0
  77. data/pages/JsonGem.md +15 -0
  78. data/pages/Modes.md +8 -3
  79. data/pages/Options.md +43 -5
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +14 -2
  82. data/test/_test_active.rb +8 -9
  83. data/test/_test_active_mimic.rb +7 -8
  84. data/test/_test_mimic_rails.rb +17 -20
  85. data/test/activerecord/result_test.rb +5 -6
  86. data/test/activesupport6/encoding_test.rb +63 -28
  87. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  88. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  89. data/test/{activesupport5 → activesupport7}/encoding_test.rb +86 -50
  90. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  91. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  92. data/test/files.rb +15 -15
  93. data/test/foo.rb +16 -45
  94. data/test/helper.rb +11 -8
  95. data/test/isolated/shared.rb +3 -2
  96. data/test/json_gem/json_addition_test.rb +2 -2
  97. data/test/json_gem/json_common_interface_test.rb +8 -6
  98. data/test/json_gem/json_encoding_test.rb +0 -0
  99. data/test/json_gem/json_ext_parser_test.rb +1 -0
  100. data/test/json_gem/json_fixtures_test.rb +3 -2
  101. data/test/json_gem/json_generator_test.rb +56 -38
  102. data/test/json_gem/json_generic_object_test.rb +11 -11
  103. data/test/json_gem/json_parser_test.rb +54 -47
  104. data/test/json_gem/json_string_matching_test.rb +9 -9
  105. data/test/json_gem/test_helper.rb +7 -3
  106. data/test/mem.rb +34 -0
  107. data/test/perf.rb +22 -27
  108. data/test/perf_compat.rb +31 -33
  109. data/test/perf_dump.rb +50 -0
  110. data/test/perf_fast.rb +80 -82
  111. data/test/perf_file.rb +27 -29
  112. data/test/perf_object.rb +65 -69
  113. data/test/perf_once.rb +59 -0
  114. data/test/perf_parser.rb +183 -0
  115. data/test/perf_saj.rb +46 -54
  116. data/test/perf_scp.rb +58 -69
  117. data/test/perf_simple.rb +41 -39
  118. data/test/perf_strict.rb +74 -82
  119. data/test/perf_wab.rb +67 -69
  120. data/test/prec.rb +5 -5
  121. data/test/sample/change.rb +0 -1
  122. data/test/sample/dir.rb +0 -1
  123. data/test/sample/doc.rb +0 -1
  124. data/test/sample/file.rb +0 -1
  125. data/test/sample/group.rb +0 -1
  126. data/test/sample/hasprops.rb +0 -1
  127. data/test/sample/layer.rb +0 -1
  128. data/test/sample/rect.rb +0 -1
  129. data/test/sample/shape.rb +0 -1
  130. data/test/sample/text.rb +0 -1
  131. data/test/sample.rb +16 -16
  132. data/test/sample_json.rb +8 -8
  133. data/test/test_compat.rb +95 -43
  134. data/test/test_custom.rb +73 -51
  135. data/test/test_debian.rb +7 -10
  136. data/test/test_fast.rb +135 -79
  137. data/test/test_file.rb +41 -30
  138. data/test/test_gc.rb +16 -5
  139. data/test/test_generate.rb +5 -5
  140. data/test/test_hash.rb +5 -5
  141. data/test/test_integer_range.rb +9 -9
  142. data/test/test_null.rb +20 -20
  143. data/test/test_object.rb +99 -96
  144. data/test/test_parser.rb +11 -0
  145. data/test/test_parser_debug.rb +27 -0
  146. data/test/test_parser_saj.rb +337 -0
  147. data/test/test_parser_usual.rb +251 -0
  148. data/test/test_rails.rb +2 -2
  149. data/test/test_saj.rb +10 -8
  150. data/test/test_scp.rb +37 -39
  151. data/test/test_strict.rb +40 -32
  152. data/test/test_various.rb +165 -84
  153. data/test/test_wab.rb +48 -44
  154. data/test/test_writer.rb +47 -47
  155. data/test/tests.rb +13 -5
  156. data/test/tests_mimic.rb +12 -3
  157. data/test/tests_mimic_addition.rb +12 -3
  158. metadata +74 -128
  159. data/ext/oj/hash.c +0 -131
  160. data/ext/oj/hash.h +0 -19
  161. data/ext/oj/hash_test.c +0 -491
  162. data/test/activesupport4/decoding_test.rb +0 -108
  163. data/test/activesupport4/encoding_test.rb +0 -531
  164. data/test/activesupport4/test_helper.rb +0 -41
  165. data/test/activesupport5/test_helper.rb +0 -72
  166. data/test/bar.rb +0 -35
  167. data/test/baz.rb +0 -16
  168. data/test/zoo.rb +0 -13
data/ext/oj/fast.c CHANGED
@@ -11,13 +11,15 @@
11
11
  #include <stdlib.h>
12
12
  #include <string.h>
13
13
 
14
+ #include "dump.h"
14
15
  #include "encode.h"
16
+ #include "mem.h"
15
17
  #include "oj.h"
16
18
 
17
19
  // maximum to allocate on the stack, arbitrary limit
18
20
  #define SMALL_JSON 65536
19
21
  #define MAX_STACK 100
20
- //#define BATCH_SIZE (4096 / sizeof(struct _leaf) - 1)
22
+ // #define BATCH_SIZE (4096 / sizeof(struct _leaf) - 1)
21
23
  #define BATCH_SIZE 100
22
24
 
23
25
  // Support for compaction
@@ -31,25 +33,25 @@ typedef struct _batch {
31
33
  struct _batch *next;
32
34
  int next_avail;
33
35
  struct _leaf leaves[BATCH_SIZE];
34
- } * Batch;
36
+ } *Batch;
35
37
 
36
38
  typedef struct _doc {
37
39
  Leaf data;
38
- Leaf * where; // points to current location
40
+ Leaf *where; // points to current location
39
41
  Leaf where_path[MAX_STACK]; // points to head of path
40
- char * json;
42
+ char *json;
41
43
  unsigned long size; // number of leaves/branches in the doc
42
44
  VALUE self;
43
45
  Batch batches;
44
46
  struct _batch batch0;
45
- } * Doc;
47
+ } *Doc;
46
48
 
47
49
  typedef struct _parseInfo {
48
50
  char *str; // buffer being read from
49
51
  char *s; // current position in buffer
50
52
  Doc doc;
51
53
  void *stack_min;
52
- } * ParseInfo;
54
+ } *ParseInfo;
53
55
 
54
56
  static void leaf_init(Leaf leaf, int type);
55
57
  static Leaf leaf_new(Doc doc, int type);
@@ -73,29 +75,13 @@ static char *read_quoted_value(ParseInfo pi);
73
75
  static void skip_comment(ParseInfo pi);
74
76
 
75
77
  static VALUE protect_open_proc(VALUE x);
76
- static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated);
78
+ static VALUE parse_json(VALUE clas, char *json, bool given);
77
79
  static void each_leaf(Doc doc, VALUE self);
78
80
  static int move_step(Doc doc, const char *path, int loc);
79
81
  static Leaf get_doc_leaf(Doc doc, const char *path);
80
82
  static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
81
83
  static void each_value(Doc doc, Leaf leaf);
82
84
 
83
- static void doc_init(Doc doc);
84
- static void doc_free(Doc doc);
85
- static VALUE doc_open(VALUE clas, VALUE str);
86
- static VALUE doc_open_file(VALUE clas, VALUE filename);
87
- static VALUE doc_where(VALUE self);
88
- static VALUE doc_local_key(VALUE self);
89
- static VALUE doc_home(VALUE self);
90
- static VALUE doc_type(int argc, VALUE *argv, VALUE self);
91
- static VALUE doc_fetch(int argc, VALUE *argv, VALUE self);
92
- static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self);
93
- static VALUE doc_move(VALUE self, VALUE str);
94
- static VALUE doc_each_child(int argc, VALUE *argv, VALUE self);
95
- static VALUE doc_each_value(int argc, VALUE *argv, VALUE self);
96
- static VALUE doc_dump(int argc, VALUE *argv, VALUE self);
97
- static VALUE doc_size(VALUE self);
98
-
99
85
  VALUE oj_doc_class = Qundef;
100
86
 
101
87
  // This is only for CentOS 5.4 with Ruby 1.9.3-p0.
@@ -128,10 +114,7 @@ inline static char *ulong_fill(char *s, size_t num) {
128
114
  char *b = buf + sizeof(buf) - 1;
129
115
 
130
116
  *b-- = '\0';
131
- for (; 0 < num; num /= 10, b--) {
132
- *b = (num % 10) + '0';
133
- }
134
- b++;
117
+ b = oj_longlong_to_string((long long)num, false, b);
135
118
  if ('\0' == *b) {
136
119
  b--;
137
120
  *b = '0';
@@ -175,7 +158,7 @@ inline static Leaf leaf_new(Doc doc, int type) {
175
158
  Leaf leaf;
176
159
 
177
160
  if (0 == doc->batches || BATCH_SIZE == doc->batches->next_avail) {
178
- Batch b = ALLOC(struct _batch);
161
+ Batch b = OJ_R_ALLOC(struct _batch);
179
162
 
180
163
  // Initializes all leaves with a NO_VAL value_type
181
164
  memset(b, 0, sizeof(struct _batch));
@@ -216,9 +199,7 @@ static VALUE leaf_value(Doc doc, Leaf leaf) {
216
199
  break;
217
200
  case T_ARRAY: return leaf_array_value(doc, leaf); break;
218
201
  case T_HASH: return leaf_hash_value(doc, leaf); break;
219
- default:
220
- rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype);
221
- break;
202
+ default: rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype); break;
222
203
  }
223
204
  }
224
205
  return leaf->value;
@@ -267,7 +248,7 @@ static void skip_comment(ParseInfo pi) {
267
248
  #endif
268
249
 
269
250
  static void leaf_fixnum_value(Leaf leaf) {
270
- char * s = leaf->str;
251
+ char *s = leaf->str;
271
252
  int64_t n = 0;
272
253
  int neg = 0;
273
254
  int big = 0;
@@ -373,7 +354,7 @@ static Leaf read_next(ParseInfo pi) {
373
354
 
374
355
  static Leaf read_obj(ParseInfo pi) {
375
356
  Leaf h = leaf_new(pi->doc, T_HASH);
376
- char * end;
357
+ char *end;
377
358
  const char *key = 0;
378
359
  Leaf val = 0;
379
360
 
@@ -665,10 +646,11 @@ static void doc_free(Doc doc) {
665
646
  while (0 != (b = doc->batches)) {
666
647
  doc->batches = doc->batches->next;
667
648
  if (&doc->batch0 != b) {
668
- xfree(b);
649
+ OJ_R_FREE(b);
669
650
  }
670
651
  }
671
- // xfree(f);
652
+ OJ_R_FREE(doc->json);
653
+ OJ_R_FREE(doc);
672
654
  }
673
655
  }
674
656
 
@@ -688,27 +670,28 @@ static void free_doc_cb(void *x) {
688
670
  Doc doc = (Doc)x;
689
671
 
690
672
  if (0 != doc) {
691
- xfree(doc->json);
692
673
  doc_free(doc);
693
674
  }
694
675
  }
695
676
 
696
677
  static void mark_leaf(Leaf leaf) {
697
- switch (leaf->value_type) {
698
- case COL_VAL:
699
- if (NULL != leaf->elements) {
700
- Leaf first = leaf->elements->next;
701
- Leaf e = first;
678
+ if (NULL != leaf) {
679
+ switch (leaf->value_type) {
680
+ case COL_VAL:
681
+ if (NULL != leaf->elements) {
682
+ Leaf first = leaf->elements->next;
683
+ Leaf e = first;
702
684
 
703
- do {
704
- mark_leaf(e);
705
- e = e->next;
706
- } while (e != first);
707
- }
708
- break;
709
- case RUBY_VAL: mark(leaf->value); break;
685
+ do {
686
+ mark_leaf(e);
687
+ e = e->next;
688
+ } while (e != first);
689
+ }
690
+ break;
691
+ case RUBY_VAL: mark(leaf->value); break;
710
692
 
711
- default: break;
693
+ default: break;
694
+ }
712
695
  }
713
696
  }
714
697
 
@@ -764,20 +747,15 @@ static const rb_data_type_t oj_doc_type = {
764
747
  0,
765
748
  };
766
749
 
767
- static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
750
+ static VALUE parse_json(VALUE clas, char *json, bool given) {
768
751
  struct _parseInfo pi;
769
752
  volatile VALUE result = Qnil;
770
753
  Doc doc;
771
754
  int ex = 0;
772
755
  volatile VALUE self;
773
756
 
774
- // TBD are both needed? is stack allocation ever needed?
757
+ doc = OJ_R_ALLOC_N(struct _doc, 1);
775
758
 
776
- if (given) {
777
- doc = ALLOCA_N(struct _doc, 1);
778
- } else {
779
- doc = ALLOC(struct _doc);
780
- }
781
759
  // skip UTF-8 BOM if present
782
760
  if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
783
761
  pi.str = json + 3;
@@ -787,9 +765,9 @@ static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
787
765
  pi.s = pi.str;
788
766
  doc_init(doc);
789
767
  pi.doc = doc;
790
- #if IS_WINDOWS
768
+ #if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
791
769
  // assume a 1M stack and give half to ruby
792
- pi.stack_min = (void *)((char *)&pi - (512 * 1024));
770
+ pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
793
771
  #else
794
772
  {
795
773
  struct rlimit lim;
@@ -802,18 +780,19 @@ static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
802
780
  }
803
781
  }
804
782
  #endif
805
- self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
806
- doc->self = self;
807
- doc->json = json;
808
- DATA_PTR(doc->self) = doc;
809
- result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
783
+ doc->json = json;
784
+ self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
785
+ doc->self = self;
786
+ result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
810
787
  if (given || 0 != ex) {
811
788
  DATA_PTR(doc->self) = NULL;
789
+ // TBD is this needed?
790
+ /*
812
791
  doc_free(pi.doc);
813
- if (allocated && 0 != ex) { // will jump so caller will not free
814
- xfree(json);
792
+ if (0 != ex) { // will jump so caller will not free
793
+ OJ_R_FREE(json);
815
794
  }
816
- rb_gc_enable();
795
+ */
817
796
  } else {
818
797
  result = doc->self;
819
798
  }
@@ -841,9 +820,7 @@ static Leaf get_doc_leaf(Doc doc, const char *path) {
841
820
  size_t cnt = doc->where - doc->where_path;
842
821
 
843
822
  if (MAX_STACK <= cnt) {
844
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
845
- "Path too deep. Limit is %d levels.",
846
- MAX_STACK);
823
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
847
824
  }
848
825
  memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
849
826
  lp = stack + cnt;
@@ -884,9 +861,7 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
884
861
  Leaf leaf = *lp;
885
862
 
886
863
  if (MAX_STACK <= lp - stack) {
887
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
888
- "Path too deep. Limit is %d levels.",
889
- MAX_STACK);
864
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
890
865
  }
891
866
  if ('\0' != *path) {
892
867
  if ('.' == *path && '.' == *(path + 1)) {
@@ -899,12 +874,18 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
899
874
  } else {
900
875
  return 0;
901
876
  }
902
- } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
877
+ } else if (NULL == leaf->elements) {
878
+ leaf = NULL;
879
+ } else if (STR_VAL == leaf->value_type || RUBY_VAL == leaf->value_type) {
880
+ // We are trying to get a children of a leaf, which
881
+ // doesn't exist.
882
+ leaf = NULL;
883
+ } else if (COL_VAL == leaf->value_type) {
903
884
  Leaf first = leaf->elements->next;
904
885
  Leaf e = first;
905
886
  int type = leaf->rtype;
906
887
 
907
- leaf = 0;
888
+ leaf = NULL;
908
889
  if (T_ARRAY == type) {
909
890
  int cnt = 0;
910
891
 
@@ -929,6 +910,7 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
929
910
  const char *slash = next_slash(path);
930
911
  int klen;
931
912
 
913
+ leaf = NULL;
932
914
  if (0 == slash) {
933
915
  klen = (int)strlen(key);
934
916
  path += klen;
@@ -959,9 +941,7 @@ static void each_leaf(Doc doc, VALUE self) {
959
941
 
960
942
  doc->where++;
961
943
  if (MAX_STACK <= doc->where - doc->where_path) {
962
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
963
- "Path too deep. Limit is %d levels.",
964
- MAX_STACK);
944
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
965
945
  }
966
946
  do {
967
947
  *doc->where = e;
@@ -977,9 +957,7 @@ static void each_leaf(Doc doc, VALUE self) {
977
957
 
978
958
  static int move_step(Doc doc, const char *path, int loc) {
979
959
  if (MAX_STACK <= doc->where - doc->where_path) {
980
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
981
- "Path too deep. Limit is %d levels.",
982
- MAX_STACK);
960
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
983
961
  }
984
962
  if ('\0' == *path) {
985
963
  loc = 0;
@@ -1104,31 +1082,23 @@ static void each_value(Doc doc, Leaf leaf) {
1104
1082
  * doc.close()
1105
1083
  */
1106
1084
  static VALUE doc_open(VALUE clas, VALUE str) {
1107
- char * json;
1085
+ char *json;
1108
1086
  size_t len;
1109
1087
  volatile VALUE obj;
1110
1088
  int given = rb_block_given_p();
1111
- int allocate;
1112
1089
 
1113
1090
  Check_Type(str, T_STRING);
1114
- len = (int)RSTRING_LEN(str) + 1;
1115
- allocate = (SMALL_JSON < len || !given);
1116
- if (allocate) {
1117
- json = ALLOC_N(char, len);
1118
- } else {
1119
- json = ALLOCA_N(char, len);
1120
- }
1121
- // It should not be necessaary to stop GC but if it is not stopped and a
1122
- // large string is parsed that string is corrupted or freed during
1123
- // parsing. I'm not sure what is going on exactly but disabling GC avoids
1124
- // the issue.
1125
- rb_gc_disable();
1091
+ len = (int)RSTRING_LEN(str) + 1;
1092
+ json = OJ_R_ALLOC_N(char, len);
1093
+
1126
1094
  memcpy(json, StringValuePtr(str), len);
1127
- obj = parse_json(clas, json, given, allocate);
1128
- rb_gc_enable();
1129
- if (given && allocate) {
1130
- xfree(json);
1095
+ obj = parse_json(clas, json, given);
1096
+ // TBD is this needed
1097
+ /*
1098
+ if (given) {
1099
+ OJ_R_FREE(json);
1131
1100
  }
1101
+ */
1132
1102
  return obj;
1133
1103
  }
1134
1104
 
@@ -1152,27 +1122,21 @@ static VALUE doc_open(VALUE clas, VALUE str) {
1152
1122
  * doc.close()
1153
1123
  */
1154
1124
  static VALUE doc_open_file(VALUE clas, VALUE filename) {
1155
- char * path;
1156
- char * json;
1157
- FILE * f;
1125
+ char *path;
1126
+ char *json;
1127
+ FILE *f;
1158
1128
  size_t len;
1159
1129
  volatile VALUE obj;
1160
1130
  int given = rb_block_given_p();
1161
- int allocate;
1162
1131
 
1163
- Check_Type(filename, T_STRING);
1164
1132
  path = StringValuePtr(filename);
1165
1133
  if (0 == (f = fopen(path, "r"))) {
1166
1134
  rb_raise(rb_eIOError, "%s", strerror(errno));
1167
1135
  }
1168
1136
  fseek(f, 0, SEEK_END);
1169
- len = ftell(f);
1170
- allocate = (SMALL_JSON < len || !given);
1171
- if (allocate) {
1172
- json = ALLOC_N(char, len + 1);
1173
- } else {
1174
- json = ALLOCA_N(char, len + 1);
1175
- }
1137
+ len = ftell(f);
1138
+ json = OJ_R_ALLOC_N(char, len + 1);
1139
+
1176
1140
  fseek(f, 0, SEEK_SET);
1177
1141
  if (len != fread(json, 1, len, f)) {
1178
1142
  fclose(f);
@@ -1183,12 +1147,13 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1183
1147
  }
1184
1148
  fclose(f);
1185
1149
  json[len] = '\0';
1186
- rb_gc_disable();
1187
- obj = parse_json(clas, json, given, allocate);
1188
- rb_gc_enable();
1189
- if (given && allocate) {
1190
- xfree(json);
1150
+ obj = parse_json(clas, json, given);
1151
+ // TBD is this needed
1152
+ /*
1153
+ if (given) {
1154
+ OJ_R_FREE(json);
1191
1155
  }
1156
+ */
1192
1157
  return obj;
1193
1158
  }
1194
1159
 
@@ -1217,7 +1182,7 @@ static char *append_key(char *p, const char *key) {
1217
1182
  * @see Oj::Doc.open
1218
1183
  */
1219
1184
 
1220
- /* @overload where?() => String
1185
+ /* @overload where() => String
1221
1186
  *
1222
1187
  * Returns a String that describes the absolute path to the current location
1223
1188
  * in the JSON document.
@@ -1228,11 +1193,11 @@ static VALUE doc_where(VALUE self) {
1228
1193
  if (0 == *doc->where_path || doc->where == doc->where_path) {
1229
1194
  return oj_slash_string;
1230
1195
  } else {
1231
- Leaf * lp;
1196
+ Leaf *lp;
1232
1197
  Leaf leaf;
1233
1198
  size_t size = 3; // leading / and terminating \0
1234
- char * path;
1235
- char * p;
1199
+ char *path;
1200
+ char *p;
1236
1201
 
1237
1202
  for (lp = doc->where_path; lp <= doc->where; lp++) {
1238
1203
  leaf = *lp;
@@ -1259,6 +1224,24 @@ static VALUE doc_where(VALUE self) {
1259
1224
  }
1260
1225
  }
1261
1226
 
1227
+ /* @overload where?() => String
1228
+ * @deprecated
1229
+ * Returns a String that describes the absolute path to the current location
1230
+ * in the JSON document.
1231
+ */
1232
+ static VALUE doc_where_q(VALUE self) {
1233
+ return doc_where(self);
1234
+ }
1235
+
1236
+ /* @overload path() => String
1237
+ *
1238
+ * Returns a String that describes the absolute path to the current location
1239
+ * in the JSON document.
1240
+ */
1241
+ static VALUE doc_path(VALUE self) {
1242
+ return doc_where(self);
1243
+ }
1244
+
1262
1245
  /* @overload local_key() => String, Fixnum, nil
1263
1246
  *
1264
1247
  * Returns the final key to the current location.
@@ -1317,7 +1300,6 @@ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
1317
1300
  VALUE type = Qnil;
1318
1301
 
1319
1302
  if (1 <= argc) {
1320
- Check_Type(*argv, T_STRING);
1321
1303
  path = StringValuePtr(*argv);
1322
1304
  }
1323
1305
  if (0 != (leaf = get_doc_leaf(doc, path))) {
@@ -1326,11 +1308,7 @@ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
1326
1308
  case T_TRUE: type = rb_cTrueClass; break;
1327
1309
  case T_FALSE: type = rb_cFalseClass; break;
1328
1310
  case T_STRING: type = rb_cString; break;
1329
- #ifdef RUBY_INTEGER_UNIFICATION
1330
1311
  case T_FIXNUM: type = rb_cInteger; break;
1331
- #else
1332
- case T_FIXNUM: type = rb_cFixnum; break;
1333
- #endif
1334
1312
  case T_FLOAT: type = rb_cFloat; break;
1335
1313
  case T_ARRAY: type = rb_cArray; break;
1336
1314
  case T_HASH: type = rb_cHash; break;
@@ -1340,14 +1318,14 @@ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
1340
1318
  return type;
1341
1319
  }
1342
1320
 
1343
- /* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array,
1321
+ /* @overload fetch(path=nil,default=nil) => nil, true, false, Fixnum, Float, String, Array,
1344
1322
  * Hash
1345
1323
  *
1346
1324
  * Returns the value at the location identified by the path or the current
1347
1325
  * location if the path is nil or not provided. This method will create and
1348
1326
  * return an Array or Hash if that is the type of Object at the location
1349
1327
  * specified. This is more expensive than navigating to the leaves of the JSON
1350
- * document.
1328
+ * document. If a default is provided that is used if no value if found.
1351
1329
  * @param [String] path path to the location to get the type of if provided
1352
1330
  * @example
1353
1331
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
@@ -1357,11 +1335,10 @@ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
1357
1335
  Doc doc;
1358
1336
  Leaf leaf;
1359
1337
  volatile VALUE val = Qnil;
1360
- const char * path = 0;
1338
+ const char *path = 0;
1361
1339
 
1362
1340
  doc = self_doc(self);
1363
1341
  if (1 <= argc) {
1364
- Check_Type(*argv, T_STRING);
1365
1342
  path = StringValuePtr(*argv);
1366
1343
  if (2 == argc) {
1367
1344
  val = argv[1];
@@ -1373,6 +1350,27 @@ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
1373
1350
  return val;
1374
1351
  }
1375
1352
 
1353
+ /* @overload exists?(path) => true, false
1354
+ *
1355
+ * Returns true if the value at the location identified by the path exists.
1356
+ * @param [String] path path to the location
1357
+ * @example
1358
+ * Oj::Doc.open('[1,2]') { |doc| doc.exists?('/1') } #=> true
1359
+ * Oj::Doc.open('[1,2]') { |doc| doc.exists?('/3') } #=> false
1360
+ */
1361
+ static VALUE doc_exists(VALUE self, VALUE str) {
1362
+ Doc doc;
1363
+ Leaf leaf;
1364
+
1365
+ doc = self_doc(self);
1366
+ if (0 != (leaf = get_doc_leaf(doc, StringValuePtr(str)))) {
1367
+ if (NULL != leaf) {
1368
+ return Qtrue;
1369
+ }
1370
+ }
1371
+ return Qfalse;
1372
+ }
1373
+
1376
1374
  /* @overload each_leaf(path=nil) => nil
1377
1375
  *
1378
1376
  * Yields to the provided block for each leaf node with the identified
@@ -1401,7 +1399,6 @@ static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1401
1399
  memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1402
1400
  }
1403
1401
  if (1 <= argc) {
1404
- Check_Type(*argv, T_STRING);
1405
1402
  path = StringValuePtr(*argv);
1406
1403
  if ('/' == *path) {
1407
1404
  doc->where = doc->where_path;
@@ -1436,7 +1433,6 @@ static VALUE doc_move(VALUE self, VALUE str) {
1436
1433
  const char *path;
1437
1434
  int loc;
1438
1435
 
1439
- Check_Type(str, T_STRING);
1440
1436
  path = StringValuePtr(str);
1441
1437
  if ('/' == *path) {
1442
1438
  doc->where = doc->where_path;
@@ -1455,7 +1451,7 @@ static VALUE doc_move(VALUE self, VALUE str) {
1455
1451
  * to the block on yield is the Doc instance after moving to the child
1456
1452
  * location.
1457
1453
  * @param [String] path if provided it identified the top of the branch to
1458
- * process the chilren of
1454
+ * process the children of
1459
1455
  * @yieldparam [Doc] Doc at the child location
1460
1456
  * @example
1461
1457
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1471,13 +1467,13 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1471
1467
  Doc doc = self_doc(self);
1472
1468
  const char *path = 0;
1473
1469
  size_t wlen;
1470
+ Leaf *where_orig = doc->where;
1474
1471
 
1475
1472
  wlen = doc->where - doc->where_path;
1476
1473
  if (0 < wlen) {
1477
1474
  memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1478
1475
  }
1479
1476
  if (1 <= argc) {
1480
- Check_Type(*argv, T_STRING);
1481
1477
  path = StringValuePtr(*argv);
1482
1478
  if ('/' == *path) {
1483
1479
  doc->where = doc->where_path;
@@ -1487,9 +1483,13 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1487
1483
  if (0 < wlen) {
1488
1484
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1489
1485
  }
1486
+ doc->where = where_orig;
1490
1487
  return Qnil;
1491
1488
  }
1492
1489
  }
1490
+ if (NULL == doc->where || NULL == *doc->where) {
1491
+ return Qnil;
1492
+ }
1493
1493
  if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
1494
1494
  Leaf first = (*doc->where)->elements->next;
1495
1495
  Leaf e = first;
@@ -1504,6 +1504,7 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1504
1504
  if (0 < wlen) {
1505
1505
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1506
1506
  }
1507
+ doc->where = where_orig;
1507
1508
  }
1508
1509
  return Qnil;
1509
1510
  }
@@ -1539,7 +1540,6 @@ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
1539
1540
  Leaf leaf;
1540
1541
 
1541
1542
  if (1 <= argc) {
1542
- Check_Type(*argv, T_STRING);
1543
1543
  path = StringValuePtr(*argv);
1544
1544
  }
1545
1545
  if (0 != (leaf = get_doc_leaf(doc, path))) {
@@ -1571,11 +1571,9 @@ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
1571
1571
 
1572
1572
  if (1 <= argc) {
1573
1573
  if (Qnil != *argv) {
1574
- Check_Type(*argv, T_STRING);
1575
1574
  path = StringValuePtr(*argv);
1576
1575
  }
1577
1576
  if (2 <= argc) {
1578
- Check_Type(argv[1], T_STRING);
1579
1577
  filename = StringValuePtr(argv[1]);
1580
1578
  }
1581
1579
  }
@@ -1583,18 +1581,15 @@ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
1583
1581
  volatile VALUE rjson;
1584
1582
 
1585
1583
  if (0 == filename) {
1586
- char buf[4096];
1587
1584
  struct _out out;
1588
1585
 
1589
- out.buf = buf;
1590
- out.end = buf + sizeof(buf) - 10;
1591
- out.allocated = false;
1592
- out.omit_nil = oj_default_options.dump_opts.omit_nil;
1586
+ oj_out_init(&out);
1587
+
1588
+ out.omit_nil = oj_default_options.dump_opts.omit_nil;
1593
1589
  oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
1594
1590
  rjson = rb_str_new2(out.buf);
1595
- if (out.allocated) {
1596
- xfree(out.buf);
1597
- }
1591
+
1592
+ oj_out_free(&out);
1598
1593
  } else {
1599
1594
  oj_write_leaf_to_file(leaf, filename, &oj_default_options);
1600
1595
  rjson = Qnil;
@@ -1613,7 +1608,9 @@ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
1613
1608
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1614
1609
  */
1615
1610
  static VALUE doc_size(VALUE self) {
1616
- return ULONG2NUM(((Doc)DATA_PTR(self))->size);
1611
+ Doc d;
1612
+ TypedData_Get_Struct(self, struct _doc, &oj_doc_type, d);
1613
+ return ULONG2NUM(d->size);
1617
1614
  }
1618
1615
 
1619
1616
  /* @overload close() => nil
@@ -1629,11 +1626,9 @@ static VALUE doc_close(VALUE self) {
1629
1626
  Doc doc = self_doc(self);
1630
1627
 
1631
1628
  rb_gc_unregister_address(&doc->self);
1632
- DATA_PTR(doc->self) = 0;
1629
+ DATA_PTR(doc->self) = NULL;
1633
1630
  if (0 != doc) {
1634
- xfree(doc->json);
1635
1631
  doc_free(doc);
1636
- xfree(doc);
1637
1632
  }
1638
1633
  return Qnil;
1639
1634
  }
@@ -1690,16 +1685,21 @@ static VALUE doc_not_implemented(VALUE self) {
1690
1685
  * # Now try again using a path to Oj::Doc.fetch() directly and not using a
1691
1686
  * block. doc = Oj::Doc.open(json) doc.fetch('/2/three') #=> 3 doc.close()
1692
1687
  */
1693
- void oj_init_doc() {
1688
+ void oj_init_doc(void) {
1694
1689
  oj_doc_class = rb_define_class_under(Oj, "Doc", rb_cObject);
1690
+ rb_gc_register_address(&oj_doc_class);
1691
+ rb_undef_alloc_func(oj_doc_class);
1695
1692
  rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
1696
1693
  rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
1697
1694
  rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
1698
- rb_define_method(oj_doc_class, "where?", doc_where, 0);
1695
+ rb_define_method(oj_doc_class, "where?", doc_where_q, 0);
1696
+ rb_define_method(oj_doc_class, "where", doc_where, 0);
1697
+ rb_define_method(oj_doc_class, "path", doc_path, 0);
1699
1698
  rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
1700
1699
  rb_define_method(oj_doc_class, "home", doc_home, 0);
1701
1700
  rb_define_method(oj_doc_class, "type", doc_type, -1);
1702
1701
  rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
1702
+ rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
1703
1703
  rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
1704
1704
  rb_define_method(oj_doc_class, "move", doc_move, 1);
1705
1705
  rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);