oj 3.11.5 → 3.16.5

Sign up to get free protection for your applications and to get access to all the features.
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);