oj 3.11.0 → 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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1421 -0
  3. data/README.md +20 -5
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +48 -38
  6. data/ext/oj/cache.c +329 -0
  7. data/ext/oj/cache.h +22 -0
  8. data/ext/oj/cache8.c +60 -62
  9. data/ext/oj/cache8.h +8 -7
  10. data/ext/oj/circarray.c +35 -35
  11. data/ext/oj/circarray.h +11 -9
  12. data/ext/oj/code.c +156 -174
  13. data/ext/oj/code.h +19 -18
  14. data/ext/oj/compat.c +140 -197
  15. data/ext/oj/custom.c +737 -879
  16. data/ext/oj/debug.c +126 -0
  17. data/ext/oj/dump.c +830 -835
  18. data/ext/oj/dump.h +65 -53
  19. data/ext/oj/dump_compat.c +566 -642
  20. data/ext/oj/dump_leaf.c +95 -182
  21. data/ext/oj/dump_object.c +518 -659
  22. data/ext/oj/dump_strict.c +301 -334
  23. data/ext/oj/encode.h +3 -4
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +27 -24
  26. data/ext/oj/err.h +38 -13
  27. data/ext/oj/extconf.rb +23 -7
  28. data/ext/oj/fast.c +1043 -1073
  29. data/ext/oj/intern.c +313 -0
  30. data/ext/oj/intern.h +22 -0
  31. data/ext/oj/mem.c +318 -0
  32. data/ext/oj/mem.h +53 -0
  33. data/ext/oj/mimic_json.c +449 -423
  34. data/ext/oj/object.c +530 -576
  35. data/ext/oj/odd.c +155 -138
  36. data/ext/oj/odd.h +24 -22
  37. data/ext/oj/oj.c +1331 -993
  38. data/ext/oj/oj.h +306 -292
  39. data/ext/oj/parse.c +934 -938
  40. data/ext/oj/parse.h +73 -70
  41. data/ext/oj/parser.c +1600 -0
  42. data/ext/oj/parser.h +101 -0
  43. data/ext/oj/rails.c +795 -845
  44. data/ext/oj/rails.h +7 -7
  45. data/ext/oj/reader.c +132 -140
  46. data/ext/oj/reader.h +67 -78
  47. data/ext/oj/resolve.c +40 -59
  48. data/ext/oj/resolve.h +3 -2
  49. data/ext/oj/rxclass.c +67 -67
  50. data/ext/oj/rxclass.h +11 -9
  51. data/ext/oj/saj.c +441 -480
  52. data/ext/oj/saj2.c +584 -0
  53. data/ext/oj/saj2.h +23 -0
  54. data/ext/oj/scp.c +78 -111
  55. data/ext/oj/sparse.c +726 -730
  56. data/ext/oj/stream_writer.c +146 -165
  57. data/ext/oj/strict.c +103 -123
  58. data/ext/oj/string_writer.c +241 -253
  59. data/ext/oj/trace.c +29 -33
  60. data/ext/oj/trace.h +41 -11
  61. data/ext/oj/usual.c +1218 -0
  62. data/ext/oj/usual.h +69 -0
  63. data/ext/oj/util.c +103 -103
  64. data/ext/oj/util.h +3 -2
  65. data/ext/oj/val_stack.c +60 -49
  66. data/ext/oj/val_stack.h +79 -85
  67. data/ext/oj/validate.c +46 -0
  68. data/ext/oj/wab.c +307 -350
  69. data/lib/oj/active_support_helper.rb +1 -3
  70. data/lib/oj/bag.rb +8 -1
  71. data/lib/oj/easy_hash.rb +9 -9
  72. data/lib/oj/error.rb +1 -2
  73. data/lib/oj/json.rb +162 -150
  74. data/lib/oj/mimic.rb +9 -19
  75. data/lib/oj/saj.rb +20 -6
  76. data/lib/oj/schandler.rb +5 -4
  77. data/lib/oj/state.rb +12 -8
  78. data/lib/oj/version.rb +1 -2
  79. data/lib/oj.rb +2 -0
  80. data/pages/Compatibility.md +1 -1
  81. data/pages/InstallOptions.md +20 -0
  82. data/pages/JsonGem.md +15 -0
  83. data/pages/Modes.md +8 -3
  84. data/pages/Options.md +43 -5
  85. data/pages/Parser.md +309 -0
  86. data/pages/Rails.md +14 -2
  87. data/test/_test_active.rb +8 -9
  88. data/test/_test_active_mimic.rb +7 -8
  89. data/test/_test_mimic_rails.rb +17 -20
  90. data/test/activerecord/result_test.rb +12 -8
  91. data/test/activesupport6/encoding_test.rb +63 -28
  92. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  93. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  94. data/test/{activesupport5 → activesupport7}/encoding_test.rb +86 -50
  95. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  96. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  97. data/test/files.rb +15 -15
  98. data/test/foo.rb +17 -43
  99. data/test/helper.rb +16 -3
  100. data/test/isolated/shared.rb +3 -2
  101. data/test/json_gem/json_addition_test.rb +2 -2
  102. data/test/json_gem/json_common_interface_test.rb +8 -6
  103. data/test/json_gem/json_encoding_test.rb +0 -0
  104. data/test/json_gem/json_ext_parser_test.rb +1 -0
  105. data/test/json_gem/json_fixtures_test.rb +3 -2
  106. data/test/json_gem/json_generator_test.rb +71 -41
  107. data/test/json_gem/json_generic_object_test.rb +11 -11
  108. data/test/json_gem/json_parser_test.rb +54 -47
  109. data/test/json_gem/json_string_matching_test.rb +9 -9
  110. data/test/json_gem/test_helper.rb +12 -0
  111. data/test/mem.rb +34 -0
  112. data/test/perf.rb +22 -27
  113. data/test/perf_compat.rb +31 -33
  114. data/test/perf_dump.rb +50 -0
  115. data/test/perf_fast.rb +80 -82
  116. data/test/perf_file.rb +27 -29
  117. data/test/perf_object.rb +65 -69
  118. data/test/perf_once.rb +59 -0
  119. data/test/perf_parser.rb +183 -0
  120. data/test/perf_saj.rb +46 -54
  121. data/test/perf_scp.rb +58 -69
  122. data/test/perf_simple.rb +41 -39
  123. data/test/perf_strict.rb +74 -82
  124. data/test/perf_wab.rb +67 -69
  125. data/test/prec.rb +5 -5
  126. data/test/sample/change.rb +0 -1
  127. data/test/sample/dir.rb +0 -1
  128. data/test/sample/doc.rb +0 -1
  129. data/test/sample/file.rb +0 -1
  130. data/test/sample/group.rb +0 -1
  131. data/test/sample/hasprops.rb +0 -1
  132. data/test/sample/layer.rb +0 -1
  133. data/test/sample/rect.rb +0 -1
  134. data/test/sample/shape.rb +0 -1
  135. data/test/sample/text.rb +0 -1
  136. data/test/sample.rb +16 -16
  137. data/test/sample_json.rb +8 -8
  138. data/test/test_compat.rb +97 -45
  139. data/test/test_custom.rb +73 -51
  140. data/test/test_debian.rb +7 -10
  141. data/test/test_fast.rb +135 -79
  142. data/test/test_file.rb +41 -30
  143. data/test/test_gc.rb +16 -5
  144. data/test/test_generate.rb +21 -0
  145. data/test/test_hash.rb +15 -5
  146. data/test/test_integer_range.rb +9 -9
  147. data/test/test_null.rb +20 -20
  148. data/test/test_object.rb +99 -96
  149. data/test/test_parser.rb +11 -0
  150. data/test/test_parser_debug.rb +27 -0
  151. data/test/test_parser_saj.rb +337 -0
  152. data/test/test_parser_usual.rb +251 -0
  153. data/test/test_rails.rb +2 -2
  154. data/test/test_saj.rb +10 -8
  155. data/test/test_scp.rb +38 -40
  156. data/test/test_strict.rb +40 -32
  157. data/test/test_various.rb +165 -84
  158. data/test/test_wab.rb +48 -44
  159. data/test/test_writer.rb +47 -47
  160. data/test/tests.rb +13 -5
  161. data/test/tests_mimic.rb +12 -3
  162. data/test/tests_mimic_addition.rb +12 -3
  163. metadata +75 -127
  164. data/ext/oj/hash.c +0 -135
  165. data/ext/oj/hash.h +0 -18
  166. data/ext/oj/hash_test.c +0 -484
  167. data/test/activesupport4/decoding_test.rb +0 -108
  168. data/test/activesupport4/encoding_test.rb +0 -531
  169. data/test/activesupport4/test_helper.rb +0 -41
  170. data/test/activesupport5/test_helper.rb +0 -72
  171. data/test/bar.rb +0 -35
  172. data/test/baz.rb +0 -16
  173. data/test/zoo.rb +0 -13
data/ext/oj/fast.c CHANGED
@@ -1,98 +1,93 @@
1
1
  // Copyright (c) 2012 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for
3
+ // license details.
2
4
 
3
5
  #if !IS_WINDOWS
4
6
  #include <sys/resource.h> // for getrlimit() on linux
5
7
  #endif
6
- #include <stdlib.h>
8
+ #include <errno.h>
9
+ #include <math.h>
7
10
  #include <stdio.h>
11
+ #include <stdlib.h>
8
12
  #include <string.h>
9
- #include <math.h>
10
- #include <errno.h>
11
13
 
12
- #include "oj.h"
14
+ #include "dump.h"
13
15
  #include "encode.h"
16
+ #include "mem.h"
17
+ #include "oj.h"
14
18
 
15
19
  // maximum to allocate on the stack, arbitrary limit
16
- #define SMALL_XML 65536
17
- #define MAX_STACK 100
18
- //#define BATCH_SIZE (4096 / sizeof(struct _leaf) - 1)
19
- #define BATCH_SIZE 100
20
+ #define SMALL_JSON 65536
21
+ #define MAX_STACK 100
22
+ // #define BATCH_SIZE (4096 / sizeof(struct _leaf) - 1)
23
+ #define BATCH_SIZE 100
24
+
25
+ // Support for compaction
26
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
27
+ #define mark rb_gc_mark_movable
28
+ #else
29
+ #define mark rb_gc_mark
30
+ #endif
20
31
 
21
32
  typedef struct _batch {
22
- struct _batch *next;
23
- int next_avail;
24
- struct _leaf leaves[BATCH_SIZE];
33
+ struct _batch *next;
34
+ int next_avail;
35
+ struct _leaf leaves[BATCH_SIZE];
25
36
  } *Batch;
26
37
 
27
38
  typedef struct _doc {
28
- Leaf data;
29
- Leaf *where; // points to current location
30
- Leaf where_path[MAX_STACK]; // points to head of path
31
- char *json;
32
- unsigned long size; // number of leaves/branches in the doc
33
- VALUE self;
34
- Batch batches;
35
- struct _batch batch0;
39
+ Leaf data;
40
+ Leaf *where; // points to current location
41
+ Leaf where_path[MAX_STACK]; // points to head of path
42
+ char *json;
43
+ unsigned long size; // number of leaves/branches in the doc
44
+ VALUE self;
45
+ Batch batches;
46
+ struct _batch batch0;
36
47
  } *Doc;
37
48
 
38
49
  typedef struct _parseInfo {
39
- char *str; /* buffer being read from */
40
- char *s; /* current position in buffer */
41
- Doc doc;
42
- void *stack_min;
50
+ char *str; // buffer being read from
51
+ char *s; // current position in buffer
52
+ Doc doc;
53
+ void *stack_min;
43
54
  } *ParseInfo;
44
55
 
45
- static void leaf_init(Leaf leaf, int type);
46
- static Leaf leaf_new(Doc doc, int type);
47
- static void leaf_append_element(Leaf parent, Leaf element);
48
- static VALUE leaf_value(Doc doc, Leaf leaf);
49
- static void leaf_fixnum_value(Leaf leaf);
50
- static void leaf_float_value(Leaf leaf);
51
- static VALUE leaf_array_value(Doc doc, Leaf leaf);
52
- static VALUE leaf_hash_value(Doc doc, Leaf leaf);
53
-
54
- static Leaf read_next(ParseInfo pi);
55
- static Leaf read_obj(ParseInfo pi);
56
- static Leaf read_array(ParseInfo pi);
57
- static Leaf read_str(ParseInfo pi);
58
- static Leaf read_num(ParseInfo pi);
59
- static Leaf read_true(ParseInfo pi);
60
- static Leaf read_false(ParseInfo pi);
61
- static Leaf read_nil(ParseInfo pi);
62
- static void next_non_white(ParseInfo pi);
63
- static char* read_quoted_value(ParseInfo pi);
64
- static void skip_comment(ParseInfo pi);
65
-
66
- static VALUE protect_open_proc(VALUE x);
67
- static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated);
68
- static void each_leaf(Doc doc, VALUE self);
69
- static int move_step(Doc doc, const char *path, int loc);
70
- static Leaf get_doc_leaf(Doc doc, const char *path);
71
- static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
72
- static void each_value(Doc doc, Leaf leaf);
73
-
74
- static void doc_init(Doc doc);
75
- static void doc_free(Doc doc);
76
- static VALUE doc_open(VALUE clas, VALUE str);
77
- static VALUE doc_open_file(VALUE clas, VALUE filename);
78
- static VALUE doc_where(VALUE self);
79
- static VALUE doc_local_key(VALUE self);
80
- static VALUE doc_home(VALUE self);
81
- static VALUE doc_type(int argc, VALUE *argv, VALUE self);
82
- static VALUE doc_fetch(int argc, VALUE *argv, VALUE self);
83
- static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self);
84
- static VALUE doc_move(VALUE self, VALUE str);
85
- static VALUE doc_each_child(int argc, VALUE *argv, VALUE self);
86
- static VALUE doc_each_value(int argc, VALUE *argv, VALUE self);
87
- static VALUE doc_dump(int argc, VALUE *argv, VALUE self);
88
- static VALUE doc_size(VALUE self);
89
-
90
- VALUE oj_doc_class = Qundef;
56
+ static void leaf_init(Leaf leaf, int type);
57
+ static Leaf leaf_new(Doc doc, int type);
58
+ static void leaf_append_element(Leaf parent, Leaf element);
59
+ static VALUE leaf_value(Doc doc, Leaf leaf);
60
+ static void leaf_fixnum_value(Leaf leaf);
61
+ static void leaf_float_value(Leaf leaf);
62
+ static VALUE leaf_array_value(Doc doc, Leaf leaf);
63
+ static VALUE leaf_hash_value(Doc doc, Leaf leaf);
64
+
65
+ static Leaf read_next(ParseInfo pi);
66
+ static Leaf read_obj(ParseInfo pi);
67
+ static Leaf read_array(ParseInfo pi);
68
+ static Leaf read_str(ParseInfo pi);
69
+ static Leaf read_num(ParseInfo pi);
70
+ static Leaf read_true(ParseInfo pi);
71
+ static Leaf read_false(ParseInfo pi);
72
+ static Leaf read_nil(ParseInfo pi);
73
+ static void next_non_white(ParseInfo pi);
74
+ static char *read_quoted_value(ParseInfo pi);
75
+ static void skip_comment(ParseInfo pi);
76
+
77
+ static VALUE protect_open_proc(VALUE x);
78
+ static VALUE parse_json(VALUE clas, char *json, bool given);
79
+ static void each_leaf(Doc doc, VALUE self);
80
+ static int move_step(Doc doc, const char *path, int loc);
81
+ static Leaf get_doc_leaf(Doc doc, const char *path);
82
+ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
83
+ static void each_value(Doc doc, Leaf leaf);
84
+
85
+ VALUE oj_doc_class = Qundef;
91
86
 
92
87
  // This is only for CentOS 5.4 with Ruby 1.9.3-p0.
93
88
  #ifndef HAVE_STPCPY
94
89
  char *stpcpy(char *dest, const char *src) {
95
- size_t cnt = strlen(src);
90
+ size_t cnt = strlen(src);
96
91
 
97
92
  strcpy(dest, src);
98
93
 
@@ -100,89 +95,76 @@ char *stpcpy(char *dest, const char *src) {
100
95
  }
101
96
  #endif
102
97
 
103
- inline static void
104
- next_non_white(ParseInfo pi) {
98
+ inline static void next_non_white(ParseInfo pi) {
105
99
  for (; 1; pi->s++) {
106
- switch(*pi->s) {
107
- case ' ':
108
- case '\t':
109
- case '\f':
110
- case '\n':
111
- case '\r':
112
- break;
113
- case '/':
114
- skip_comment(pi);
115
- break;
116
- default:
117
- return;
118
- }
100
+ switch (*pi->s) {
101
+ case ' ':
102
+ case '\t':
103
+ case '\f':
104
+ case '\n':
105
+ case '\r': break;
106
+ case '/': skip_comment(pi); break;
107
+ default: return;
108
+ }
119
109
  }
120
110
  }
121
111
 
122
- inline static char*
123
- ulong_fill(char *s, size_t num) {
124
- char buf[32];
125
- char *b = buf + sizeof(buf) - 1;
112
+ inline static char *ulong_fill(char *s, size_t num) {
113
+ char buf[32];
114
+ char *b = buf + sizeof(buf) - 1;
126
115
 
127
116
  *b-- = '\0';
128
- for (; 0 < num; num /= 10, b--) {
129
- *b = (num % 10) + '0';
130
- }
131
- b++;
117
+ b = oj_longlong_to_string((long long)num, false, b);
132
118
  if ('\0' == *b) {
133
- b--;
134
- *b = '0';
119
+ b--;
120
+ *b = '0';
135
121
  }
136
122
  for (; '\0' != *b; b++, s++) {
137
- *s = *b;
123
+ *s = *b;
138
124
  }
139
125
  return s;
140
126
  }
141
127
 
142
- inline static void
143
- leaf_init(Leaf leaf, int type) {
144
- leaf->next = 0;
145
- leaf->rtype = type;
128
+ inline static void leaf_init(Leaf leaf, int type) {
129
+ leaf->next = 0;
130
+ leaf->rtype = type;
146
131
  leaf->parent_type = T_NONE;
147
132
  switch (type) {
148
133
  case T_ARRAY:
149
134
  case T_HASH:
150
- leaf->elements = 0;
151
- leaf->value_type = COL_VAL;
152
- break;
135
+ leaf->elements = 0;
136
+ leaf->value_type = COL_VAL;
137
+ break;
153
138
  case T_NIL:
154
- leaf->value = Qnil;
155
- leaf->value_type = RUBY_VAL;
156
- break;
139
+ leaf->value = Qnil;
140
+ leaf->value_type = RUBY_VAL;
141
+ break;
157
142
  case T_TRUE:
158
- leaf->value = Qtrue;
159
- leaf->value_type = RUBY_VAL;
160
- break;
143
+ leaf->value = Qtrue;
144
+ leaf->value_type = RUBY_VAL;
145
+ break;
161
146
  case T_FALSE:
162
- leaf->value = Qfalse;
163
- leaf->value_type = RUBY_VAL;
164
- break;
147
+ leaf->value = Qfalse;
148
+ leaf->value_type = RUBY_VAL;
149
+ break;
165
150
  case T_FIXNUM:
166
151
  case T_FLOAT:
167
152
  case T_STRING:
168
- default:
169
- leaf->value_type = STR_VAL;
170
- break;
153
+ default: leaf->value_type = STR_VAL; break;
171
154
  }
172
155
  }
173
156
 
174
- inline static Leaf
175
- leaf_new(Doc doc, int type) {
176
- Leaf leaf;
157
+ inline static Leaf leaf_new(Doc doc, int type) {
158
+ Leaf leaf;
177
159
 
178
160
  if (0 == doc->batches || BATCH_SIZE == doc->batches->next_avail) {
179
- Batch b = ALLOC(struct _batch);
161
+ Batch b = OJ_R_ALLOC(struct _batch);
180
162
 
181
- // Initializes all leaves with a NO_VAL value_type
182
- memset(b, 0, sizeof(struct _batch));
183
- b->next = doc->batches;
184
- doc->batches = b;
185
- b->next_avail = 0;
163
+ // Initializes all leaves with a NO_VAL value_type
164
+ memset(b, 0, sizeof(struct _batch));
165
+ b->next = doc->batches;
166
+ doc->batches = b;
167
+ b->next_avail = 0;
186
168
  }
187
169
  leaf = &doc->batches->leaves[doc->batches->next_avail];
188
170
  doc->batches->next_avail++;
@@ -191,93 +173,71 @@ leaf_new(Doc doc, int type) {
191
173
  return leaf;
192
174
  }
193
175
 
194
- inline static void
195
- leaf_append_element(Leaf parent, Leaf element) {
176
+ inline static void leaf_append_element(Leaf parent, Leaf element) {
196
177
  if (0 == parent->elements) {
197
- parent->elements = element;
198
- element->next = element;
178
+ parent->elements = element;
179
+ element->next = element;
199
180
  } else {
200
- element->next = parent->elements->next;
201
- parent->elements->next = element;
202
- parent->elements = element;
181
+ element->next = parent->elements->next;
182
+ parent->elements->next = element;
183
+ parent->elements = element;
203
184
  }
204
185
  }
205
186
 
206
- static VALUE
207
- leaf_value(Doc doc, Leaf leaf) {
187
+ static VALUE leaf_value(Doc doc, Leaf leaf) {
208
188
  if (RUBY_VAL != leaf->value_type) {
209
- switch (leaf->rtype) {
210
- case T_NIL:
211
- leaf->value = Qnil;
212
- break;
213
- case T_TRUE:
214
- leaf->value = Qtrue;
215
- break;
216
- case T_FALSE:
217
- leaf->value = Qfalse;
218
- break;
219
- case T_FIXNUM:
220
- leaf_fixnum_value(leaf);
221
- break;
222
- case T_FLOAT:
223
- leaf_float_value(leaf);
224
- break;
225
- case T_STRING:
226
- leaf->value = rb_str_new2(leaf->str);
227
- leaf->value = oj_encode(leaf->value);
228
- leaf->value_type = RUBY_VAL;
229
- break;
230
- case T_ARRAY:
231
- return leaf_array_value(doc, leaf);
232
- break;
233
- case T_HASH:
234
- return leaf_hash_value(doc, leaf);
235
- break;
236
- default:
237
- rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype);
238
- break;
239
- }
189
+ switch (leaf->rtype) {
190
+ case T_NIL: leaf->value = Qnil; break;
191
+ case T_TRUE: leaf->value = Qtrue; break;
192
+ case T_FALSE: leaf->value = Qfalse; break;
193
+ case T_FIXNUM: leaf_fixnum_value(leaf); break;
194
+ case T_FLOAT: leaf_float_value(leaf); break;
195
+ case T_STRING:
196
+ leaf->value = rb_str_new2(leaf->str);
197
+ leaf->value = oj_encode(leaf->value);
198
+ leaf->value_type = RUBY_VAL;
199
+ break;
200
+ case T_ARRAY: return leaf_array_value(doc, leaf); break;
201
+ case T_HASH: return leaf_hash_value(doc, leaf); break;
202
+ default: rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype); break;
203
+ }
240
204
  }
241
205
  return leaf->value;
242
206
  }
243
207
 
244
- inline static Doc
245
- self_doc(VALUE self) {
246
- Doc doc = DATA_PTR(self);
208
+ inline static Doc self_doc(VALUE self) {
209
+ Doc doc = DATA_PTR(self);
247
210
 
248
211
  if (0 == doc) {
249
- rb_raise(rb_eIOError, "Document already closed or not open.");
212
+ rb_raise(rb_eIOError, "Document already closed or not open.");
250
213
  }
251
214
  return doc;
252
215
  }
253
216
 
254
- static void
255
- skip_comment(ParseInfo pi) {
256
- pi->s++; // skip first /
217
+ static void skip_comment(ParseInfo pi) {
218
+ pi->s++; // skip first /
257
219
  if ('*' == *pi->s) {
258
- pi->s++;
259
- for (; '\0' != *pi->s; pi->s++) {
260
- if ('*' == *pi->s && '/' == *(pi->s + 1)) {
261
- pi->s++;
262
- return;
263
- } else if ('\0' == *pi->s) {
264
- raise_error("comment not terminated", pi->str, pi->s);
265
- }
266
- }
220
+ pi->s++;
221
+ for (; '\0' != *pi->s; pi->s++) {
222
+ if ('*' == *pi->s && '/' == *(pi->s + 1)) {
223
+ pi->s++;
224
+ return;
225
+ } else if ('\0' == *pi->s) {
226
+ raise_error("comment not terminated", pi->str, pi->s);
227
+ }
228
+ }
267
229
  } else if ('/' == *pi->s) {
268
- for (; 1; pi->s++) {
269
- switch (*pi->s) {
270
- case '\n':
271
- case '\r':
272
- case '\f':
273
- case '\0':
274
- return;
275
- default:
276
- break;
277
- }
278
- }
230
+ for (; 1; pi->s++) {
231
+ switch (*pi->s) {
232
+ case '\n':
233
+ case '\r':
234
+ case '\f':
235
+ case '\0': return;
236
+ default: break;
237
+ }
238
+ }
279
239
  } else {
280
- raise_error("invalid comment", pi->str, pi->s);
240
+ raise_error("invalid comment", pi->str, pi->s);
281
241
  }
282
242
  }
283
243
 
@@ -287,100 +247,88 @@ skip_comment(ParseInfo pi) {
287
247
  #define NUM_MAX (FIXNUM_MAX >> 8)
288
248
  #endif
289
249
 
290
-
291
- static void
292
- leaf_fixnum_value(Leaf leaf) {
293
- char *s = leaf->str;
294
- int64_t n = 0;
295
- int neg = 0;
296
- int big = 0;
250
+ static void leaf_fixnum_value(Leaf leaf) {
251
+ char *s = leaf->str;
252
+ int64_t n = 0;
253
+ int neg = 0;
254
+ int big = 0;
297
255
 
298
256
  if ('-' == *s) {
299
- s++;
300
- neg = 1;
257
+ s++;
258
+ neg = 1;
301
259
  } else if ('+' == *s) {
302
- s++;
260
+ s++;
303
261
  }
304
262
  for (; '0' <= *s && *s <= '9'; s++) {
305
- n = n * 10 + (*s - '0');
306
- if (NUM_MAX <= n) {
307
- big = 1;
308
- }
263
+ n = n * 10 + (*s - '0');
264
+ if (NUM_MAX <= n) {
265
+ big = 1;
266
+ }
309
267
  }
310
268
  if (big) {
311
- char c = *s;
269
+ char c = *s;
312
270
 
313
- *s = '\0';
314
- leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
315
- *s = c;
271
+ *s = '\0';
272
+ leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
273
+ *s = c;
316
274
  } else {
317
- if (neg) {
318
- n = -n;
319
- }
320
- leaf->value = rb_ll2inum(n);
275
+ if (neg) {
276
+ n = -n;
277
+ }
278
+ leaf->value = rb_ll2inum(n);
321
279
  }
322
280
  leaf->value_type = RUBY_VAL;
323
281
  }
324
282
 
325
- static void
326
- leaf_float_value(Leaf leaf) {
327
- leaf->value = rb_float_new(rb_cstr_to_dbl(leaf->str, 1));
283
+ static void leaf_float_value(Leaf leaf) {
284
+ leaf->value = rb_float_new(rb_cstr_to_dbl(leaf->str, 1));
328
285
  leaf->value_type = RUBY_VAL;
329
286
  }
330
287
 
331
- static VALUE
332
- leaf_array_value(Doc doc, Leaf leaf) {
333
- volatile VALUE a = rb_ary_new();
288
+ static VALUE leaf_array_value(Doc doc, Leaf leaf) {
289
+ volatile VALUE a = rb_ary_new();
334
290
 
335
291
  if (0 != leaf->elements) {
336
- Leaf first = leaf->elements->next;
337
- Leaf e = first;
292
+ Leaf first = leaf->elements->next;
293
+ Leaf e = first;
338
294
 
339
- do {
340
- rb_ary_push(a, leaf_value(doc, e));
341
- e = e->next;
342
- } while (e != first);
295
+ do {
296
+ rb_ary_push(a, leaf_value(doc, e));
297
+ e = e->next;
298
+ } while (e != first);
343
299
  }
344
300
  return a;
345
301
  }
346
302
 
347
- static VALUE
348
- leaf_hash_value(Doc doc, Leaf leaf) {
349
- volatile VALUE h = rb_hash_new();
303
+ static VALUE leaf_hash_value(Doc doc, Leaf leaf) {
304
+ volatile VALUE h = rb_hash_new();
350
305
 
351
306
  if (0 != leaf->elements) {
352
- Leaf first = leaf->elements->next;
353
- Leaf e = first;
354
- volatile VALUE key;
307
+ Leaf first = leaf->elements->next;
308
+ Leaf e = first;
309
+ volatile VALUE key;
355
310
 
356
- do {
357
- key = rb_str_new2(e->key);
358
- key = oj_encode(key);
359
- rb_hash_aset(h, key, leaf_value(doc, e));
360
- e = e->next;
361
- } while (e != first);
311
+ do {
312
+ key = rb_str_new2(e->key);
313
+ key = oj_encode(key);
314
+ rb_hash_aset(h, key, leaf_value(doc, e));
315
+ e = e->next;
316
+ } while (e != first);
362
317
  }
363
318
  return h;
364
319
  }
365
320
 
366
- static Leaf
367
- read_next(ParseInfo pi) {
368
- Leaf leaf = 0;
321
+ static Leaf read_next(ParseInfo pi) {
322
+ Leaf leaf = 0;
369
323
 
370
- if ((void*)&leaf < pi->stack_min) {
371
- rb_raise(rb_eSysStackError, "JSON is too deeply nested");
324
+ if ((void *)&leaf < pi->stack_min) {
325
+ rb_raise(rb_eSysStackError, "JSON is too deeply nested");
372
326
  }
373
- next_non_white(pi); // skip white space
327
+ next_non_white(pi); // skip white space
374
328
  switch (*pi->s) {
375
- case '{':
376
- leaf = read_obj(pi);
377
- break;
378
- case '[':
379
- leaf = read_array(pi);
380
- break;
381
- case '"':
382
- leaf = read_str(pi);
383
- break;
329
+ case '{': leaf = read_obj(pi); break;
330
+ case '[': leaf = read_array(pi); break;
331
+ case '"': leaf = read_str(pi); break;
384
332
  case '+':
385
333
  case '-':
386
334
  case '0':
@@ -392,716 +340,724 @@ read_next(ParseInfo pi) {
392
340
  case '6':
393
341
  case '7':
394
342
  case '8':
395
- case '9':
396
- leaf = read_num(pi);
397
- break;
398
- case 't':
399
- leaf = read_true(pi);
400
- break;
401
- case 'f':
402
- leaf = read_false(pi);
403
- break;
404
- case 'n':
405
- leaf = read_nil(pi);
406
- break;
343
+ case '9': leaf = read_num(pi); break;
344
+ case 't': leaf = read_true(pi); break;
345
+ case 'f': leaf = read_false(pi); break;
346
+ case 'n': leaf = read_nil(pi); break;
407
347
  case '\0':
408
- default:
409
- break; // returns 0
348
+ default: break; // returns 0
410
349
  }
411
350
  pi->doc->size++;
412
351
 
413
352
  return leaf;
414
353
  }
415
354
 
416
- static Leaf
417
- read_obj(ParseInfo pi) {
418
- Leaf h = leaf_new(pi->doc, T_HASH);
419
- char *end;
420
- const char *key = 0;
421
- Leaf val = 0;
355
+ static Leaf read_obj(ParseInfo pi) {
356
+ Leaf h = leaf_new(pi->doc, T_HASH);
357
+ char *end;
358
+ const char *key = 0;
359
+ Leaf val = 0;
422
360
 
423
361
  pi->s++;
424
362
  next_non_white(pi);
425
363
  if ('}' == *pi->s) {
426
- pi->s++;
427
- return h;
364
+ pi->s++;
365
+ return h;
428
366
  }
429
367
  while (1) {
430
- next_non_white(pi);
431
- key = 0;
432
- val = 0;
433
- if ('"' != *pi->s || 0 == (key = read_quoted_value(pi))) {
434
- raise_error("unexpected character", pi->str, pi->s);
435
- }
436
- next_non_white(pi);
437
- if (':' == *pi->s) {
438
- pi->s++;
439
- } else {
440
- raise_error("invalid format, expected :", pi->str, pi->s);
441
- }
442
- if (0 == (val = read_next(pi))) {
443
- //printf("*** '%s'\n", pi->s);
444
- raise_error("unexpected character", pi->str, pi->s);
445
- }
446
- end = pi->s;
447
- val->key = key;
448
- val->parent_type = T_HASH;
449
- leaf_append_element(h, val);
450
- next_non_white(pi);
451
- if ('}' == *pi->s) {
452
- pi->s++;
453
- *end = '\0';
454
- break;
455
- } else if (',' == *pi->s) {
456
- pi->s++;
457
- } else {
458
- //printf("*** '%s'\n", pi->s);
459
- raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
460
- }
461
- *end = '\0';
368
+ next_non_white(pi);
369
+ key = 0;
370
+ val = 0;
371
+ if ('"' != *pi->s || 0 == (key = read_quoted_value(pi))) {
372
+ raise_error("unexpected character", pi->str, pi->s);
373
+ }
374
+ next_non_white(pi);
375
+ if (':' == *pi->s) {
376
+ pi->s++;
377
+ } else {
378
+ raise_error("invalid format, expected :", pi->str, pi->s);
379
+ }
380
+ if (0 == (val = read_next(pi))) {
381
+ // printf("*** '%s'\n", pi->s);
382
+ raise_error("unexpected character", pi->str, pi->s);
383
+ }
384
+ end = pi->s;
385
+ val->key = key;
386
+ val->parent_type = T_HASH;
387
+ leaf_append_element(h, val);
388
+ next_non_white(pi);
389
+ if ('}' == *pi->s) {
390
+ pi->s++;
391
+ *end = '\0';
392
+ break;
393
+ } else if (',' == *pi->s) {
394
+ pi->s++;
395
+ } else {
396
+ // printf("*** '%s'\n", pi->s);
397
+ raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
398
+ }
399
+ *end = '\0';
462
400
  }
463
401
  return h;
464
402
  }
465
403
 
466
- static Leaf
467
- read_array(ParseInfo pi) {
468
- Leaf a = leaf_new(pi->doc, T_ARRAY);
469
- Leaf e;
470
- char *end;
471
- int cnt = 0;
404
+ static Leaf read_array(ParseInfo pi) {
405
+ Leaf a = leaf_new(pi->doc, T_ARRAY);
406
+ Leaf e;
407
+ char *end;
408
+ int cnt = 0;
472
409
 
473
410
  pi->s++;
474
411
  next_non_white(pi);
475
412
  if (']' == *pi->s) {
476
- pi->s++;
477
- return a;
413
+ pi->s++;
414
+ return a;
478
415
  }
479
416
  while (1) {
480
- next_non_white(pi);
481
- if (0 == (e = read_next(pi))) {
482
- raise_error("unexpected character", pi->str, pi->s);
483
- }
484
- cnt++;
485
- e->index = cnt;
486
- e->parent_type = T_ARRAY;
487
- leaf_append_element(a, e);
488
- end = pi->s;
489
- next_non_white(pi);
490
- if (',' == *pi->s) {
491
- pi->s++;
492
- } else if (']' == *pi->s) {
493
- pi->s++;
494
- *end = '\0';
495
- break;
496
- } else {
497
- raise_error("invalid format, expected , or ] while in an array", pi->str, pi->s);
498
- }
499
- *end = '\0';
417
+ next_non_white(pi);
418
+ if (0 == (e = read_next(pi))) {
419
+ raise_error("unexpected character", pi->str, pi->s);
420
+ }
421
+ cnt++;
422
+ e->index = cnt;
423
+ e->parent_type = T_ARRAY;
424
+ leaf_append_element(a, e);
425
+ end = pi->s;
426
+ next_non_white(pi);
427
+ if (',' == *pi->s) {
428
+ pi->s++;
429
+ } else if (']' == *pi->s) {
430
+ pi->s++;
431
+ *end = '\0';
432
+ break;
433
+ } else {
434
+ raise_error("invalid format, expected , or ] while in an array", pi->str, pi->s);
435
+ }
436
+ *end = '\0';
500
437
  }
501
438
  return a;
502
439
  }
503
440
 
504
- static Leaf
505
- read_str(ParseInfo pi) {
506
- Leaf leaf = leaf_new(pi->doc, T_STRING);
441
+ static Leaf read_str(ParseInfo pi) {
442
+ Leaf leaf = leaf_new(pi->doc, T_STRING);
507
443
 
508
444
  leaf->str = read_quoted_value(pi);
509
445
 
510
446
  return leaf;
511
447
  }
512
448
 
513
- static Leaf
514
- read_num(ParseInfo pi) {
515
- char *start = pi->s;
516
- int type = T_FIXNUM;
517
- Leaf leaf;
449
+ static Leaf read_num(ParseInfo pi) {
450
+ char *start = pi->s;
451
+ int type = T_FIXNUM;
452
+ Leaf leaf;
518
453
 
519
454
  if ('-' == *pi->s) {
520
- pi->s++;
455
+ pi->s++;
521
456
  }
522
457
  // digits
523
458
  for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
524
459
  }
525
460
  if ('.' == *pi->s) {
526
- type = T_FLOAT;
527
- pi->s++;
528
- for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
529
- }
461
+ type = T_FLOAT;
462
+ pi->s++;
463
+ for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
464
+ }
530
465
  }
531
466
  if ('e' == *pi->s || 'E' == *pi->s) {
532
- pi->s++;
533
- if ('-' == *pi->s || '+' == *pi->s) {
534
- pi->s++;
535
- }
536
- for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
537
- }
538
- }
539
- leaf = leaf_new(pi->doc, type);
467
+ pi->s++;
468
+ if ('-' == *pi->s || '+' == *pi->s) {
469
+ pi->s++;
470
+ }
471
+ for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
472
+ }
473
+ }
474
+ leaf = leaf_new(pi->doc, type);
540
475
  leaf->str = start;
541
476
 
542
477
  return leaf;
543
478
  }
544
479
 
545
- static Leaf
546
- read_true(ParseInfo pi) {
547
- Leaf leaf = leaf_new(pi->doc, T_TRUE);
480
+ static Leaf read_true(ParseInfo pi) {
481
+ Leaf leaf = leaf_new(pi->doc, T_TRUE);
548
482
 
549
483
  pi->s++;
550
484
  if ('r' != *pi->s || 'u' != *(pi->s + 1) || 'e' != *(pi->s + 2)) {
551
- raise_error("invalid format, expected 'true'", pi->str, pi->s);
485
+ raise_error("invalid format, expected 'true'", pi->str, pi->s);
552
486
  }
553
487
  pi->s += 3;
554
488
 
555
489
  return leaf;
556
490
  }
557
491
 
558
- static Leaf
559
- read_false(ParseInfo pi) {
560
- Leaf leaf = leaf_new(pi->doc, T_FALSE);
492
+ static Leaf read_false(ParseInfo pi) {
493
+ Leaf leaf = leaf_new(pi->doc, T_FALSE);
561
494
 
562
495
  pi->s++;
563
496
  if ('a' != *pi->s || 'l' != *(pi->s + 1) || 's' != *(pi->s + 2) || 'e' != *(pi->s + 3)) {
564
- raise_error("invalid format, expected 'false'", pi->str, pi->s);
497
+ raise_error("invalid format, expected 'false'", pi->str, pi->s);
565
498
  }
566
499
  pi->s += 4;
567
500
 
568
501
  return leaf;
569
502
  }
570
503
 
571
- static Leaf
572
- read_nil(ParseInfo pi) {
573
- Leaf leaf = leaf_new(pi->doc, T_NIL);
504
+ static Leaf read_nil(ParseInfo pi) {
505
+ Leaf leaf = leaf_new(pi->doc, T_NIL);
574
506
 
575
507
  pi->s++;
576
508
  if ('u' != *pi->s || 'l' != *(pi->s + 1) || 'l' != *(pi->s + 2)) {
577
- raise_error("invalid format, expected 'nil'", pi->str, pi->s);
509
+ raise_error("invalid format, expected 'nil'", pi->str, pi->s);
578
510
  }
579
511
  pi->s += 3;
580
512
 
581
513
  return leaf;
582
514
  }
583
515
 
584
- static uint32_t
585
- read_4hex(ParseInfo pi, const char *h) {
586
- uint32_t b = 0;
587
- int i;
516
+ static uint32_t read_4hex(ParseInfo pi, const char *h) {
517
+ uint32_t b = 0;
518
+ int i;
588
519
 
589
520
  for (i = 0; i < 4; i++, h++) {
590
- b = b << 4;
591
- if ('0' <= *h && *h <= '9') {
592
- b += *h - '0';
593
- } else if ('A' <= *h && *h <= 'F') {
594
- b += *h - 'A' + 10;
595
- } else if ('a' <= *h && *h <= 'f') {
596
- b += *h - 'a' + 10;
597
- } else {
598
- raise_error("invalid hex character", pi->str, pi->s);
599
- }
521
+ b = b << 4;
522
+ if ('0' <= *h && *h <= '9') {
523
+ b += *h - '0';
524
+ } else if ('A' <= *h && *h <= 'F') {
525
+ b += *h - 'A' + 10;
526
+ } else if ('a' <= *h && *h <= 'f') {
527
+ b += *h - 'a' + 10;
528
+ } else {
529
+ raise_error("invalid hex character", pi->str, pi->s);
530
+ }
600
531
  }
601
532
  return b;
602
533
  }
603
534
 
604
- static char*
605
- unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
535
+ static char *unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
606
536
  if (0x0000007F >= code) {
607
- *t++ = (char)code;
537
+ *t++ = (char)code;
608
538
  } else if (0x000007FF >= code) {
609
- *t++ = 0xC0 | (code >> 6);
610
- *t++ = 0x80 | (0x3F & code);
539
+ *t++ = 0xC0 | (code >> 6);
540
+ *t++ = 0x80 | (0x3F & code);
611
541
  } else if (0x0000FFFF >= code) {
612
- *t++ = 0xE0 | (code >> 12);
613
- *t++ = 0x80 | ((code >> 6) & 0x3F);
614
- *t++ = 0x80 | (0x3F & code);
542
+ *t++ = 0xE0 | (code >> 12);
543
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
544
+ *t++ = 0x80 | (0x3F & code);
615
545
  } else if (0x001FFFFF >= code) {
616
- *t++ = 0xF0 | (code >> 18);
617
- *t++ = 0x80 | ((code >> 12) & 0x3F);
618
- *t++ = 0x80 | ((code >> 6) & 0x3F);
619
- *t++ = 0x80 | (0x3F & code);
546
+ *t++ = 0xF0 | (code >> 18);
547
+ *t++ = 0x80 | ((code >> 12) & 0x3F);
548
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
549
+ *t++ = 0x80 | (0x3F & code);
620
550
  } else if (0x03FFFFFF >= code) {
621
- *t++ = 0xF8 | (code >> 24);
622
- *t++ = 0x80 | ((code >> 18) & 0x3F);
623
- *t++ = 0x80 | ((code >> 12) & 0x3F);
624
- *t++ = 0x80 | ((code >> 6) & 0x3F);
625
- *t++ = 0x80 | (0x3F & code);
551
+ *t++ = 0xF8 | (code >> 24);
552
+ *t++ = 0x80 | ((code >> 18) & 0x3F);
553
+ *t++ = 0x80 | ((code >> 12) & 0x3F);
554
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
555
+ *t++ = 0x80 | (0x3F & code);
626
556
  } else if (0x7FFFFFFF >= code) {
627
- *t++ = 0xFC | (code >> 30);
628
- *t++ = 0x80 | ((code >> 24) & 0x3F);
629
- *t++ = 0x80 | ((code >> 18) & 0x3F);
630
- *t++ = 0x80 | ((code >> 12) & 0x3F);
631
- *t++ = 0x80 | ((code >> 6) & 0x3F);
632
- *t++ = 0x80 | (0x3F & code);
557
+ *t++ = 0xFC | (code >> 30);
558
+ *t++ = 0x80 | ((code >> 24) & 0x3F);
559
+ *t++ = 0x80 | ((code >> 18) & 0x3F);
560
+ *t++ = 0x80 | ((code >> 12) & 0x3F);
561
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
562
+ *t++ = 0x80 | (0x3F & code);
633
563
  } else {
634
- raise_error("invalid Unicode character", pi->str, pi->s);
564
+ raise_error("invalid Unicode character", pi->str, pi->s);
635
565
  }
636
566
  return t;
637
567
  }
638
568
 
639
- /* Assume the value starts immediately and goes until the quote character is
640
- * reached again. Do not read the character after the terminating quote.
641
- */
642
- static char*
643
- read_quoted_value(ParseInfo pi) {
644
- char *value = 0;
645
- char *h = pi->s; // head
646
- char *t = h; // tail
569
+ // Assume the value starts immediately and goes until the quote character is
570
+ // reached again. Do not read the character after the terminating quote.
571
+ static char *read_quoted_value(ParseInfo pi) {
572
+ char *value = 0;
573
+ char *h = pi->s; // head
574
+ char *t = h; // tail
647
575
 
648
- h++; // skip quote character
576
+ h++; // skip quote character
649
577
  t++;
650
578
  value = h;
651
579
  for (; '"' != *h; h++, t++) {
652
- if ('\0' == *h) {
653
- pi->s = h;
654
- raise_error("quoted string not terminated", pi->str, pi->s);
655
- } else if ('\\' == *h) {
656
- h++;
657
- switch (*h) {
658
- case 'n': *t = '\n'; break;
659
- case 'r': *t = '\r'; break;
660
- case 't': *t = '\t'; break;
661
- case 'f': *t = '\f'; break;
662
- case 'b': *t = '\b'; break;
663
- case '"': *t = '"'; break;
664
- case '/': *t = '/'; break;
665
- case '\\': *t = '\\'; break;
666
- case 'u': {
667
- uint32_t code;
668
-
669
- h++;
670
- code = read_4hex(pi, h);
671
- h += 3;
672
- if (0x0000D800 <= code && code <= 0x0000DFFF) {
673
- uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
674
- uint32_t c2;
675
-
676
- h++;
677
- if ('\\' != *h || 'u' != *(h + 1)) {
678
- pi->s = h;
679
- raise_error("invalid escaped character", pi->str, pi->s);
680
- }
681
- h += 2;
682
- c2 = read_4hex(pi, h);
683
- h += 3;
684
- c2 = (c2 - 0x0000DC00) & 0x000003FF;
685
- code = ((c1 << 10) | c2) + 0x00010000;
686
- }
687
- t = unicode_to_chars(pi, t, code);
688
- t--;
689
- break;
690
- }
691
- default:
692
- pi->s = h;
693
- raise_error("invalid escaped character", pi->str, pi->s);
694
- break;
695
- }
696
- } else if (t != h) {
697
- *t = *h;
698
- }
699
- }
700
- *t = '\0'; // terminate value
580
+ if ('\0' == *h) {
581
+ pi->s = h;
582
+ raise_error("quoted string not terminated", pi->str, pi->s);
583
+ } else if ('\\' == *h) {
584
+ h++;
585
+ switch (*h) {
586
+ case 'n': *t = '\n'; break;
587
+ case 'r': *t = '\r'; break;
588
+ case 't': *t = '\t'; break;
589
+ case 'f': *t = '\f'; break;
590
+ case 'b': *t = '\b'; break;
591
+ case '"': *t = '"'; break;
592
+ case '/': *t = '/'; break;
593
+ case '\\': *t = '\\'; break;
594
+ case 'u': {
595
+ uint32_t code;
596
+
597
+ h++;
598
+ code = read_4hex(pi, h);
599
+ h += 3;
600
+ if (0x0000D800 <= code && code <= 0x0000DFFF) {
601
+ uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
602
+ uint32_t c2;
603
+
604
+ h++;
605
+ if ('\\' != *h || 'u' != *(h + 1)) {
606
+ pi->s = h;
607
+ raise_error("invalid escaped character", pi->str, pi->s);
608
+ }
609
+ h += 2;
610
+ c2 = read_4hex(pi, h);
611
+ h += 3;
612
+ c2 = (c2 - 0x0000DC00) & 0x000003FF;
613
+ code = ((c1 << 10) | c2) + 0x00010000;
614
+ }
615
+ t = unicode_to_chars(pi, t, code);
616
+ t--;
617
+ break;
618
+ }
619
+ default:
620
+ pi->s = h;
621
+ raise_error("invalid escaped character", pi->str, pi->s);
622
+ break;
623
+ }
624
+ } else if (t != h) {
625
+ *t = *h;
626
+ }
627
+ }
628
+ *t = '\0'; // terminate value
701
629
  pi->s = h + 1;
702
630
 
703
631
  return value;
704
632
  }
705
633
 
706
634
  // doc support functions
707
- inline static void
708
- doc_init(Doc doc) {
635
+ inline static void doc_init(Doc doc) {
709
636
  memset(doc, 0, sizeof(struct _doc));
710
- doc->where = doc->where_path;
711
- doc->self = Qundef;
637
+ doc->where = doc->where_path;
638
+ doc->self = Qundef;
712
639
  doc->batches = &doc->batch0;
713
640
  }
714
641
 
715
- static void
716
- doc_free(Doc doc) {
642
+ static void doc_free(Doc doc) {
717
643
  if (0 != doc) {
718
- Batch b;
644
+ Batch b;
719
645
 
720
- while (0 != (b = doc->batches)) {
721
- doc->batches = doc->batches->next;
722
- if (&doc->batch0 != b) {
723
- xfree(b);
724
- }
725
- }
726
- //xfree(f);
646
+ while (0 != (b = doc->batches)) {
647
+ doc->batches = doc->batches->next;
648
+ if (&doc->batch0 != b) {
649
+ OJ_R_FREE(b);
650
+ }
651
+ }
652
+ OJ_R_FREE(doc->json);
653
+ OJ_R_FREE(doc);
727
654
  }
728
655
  }
729
656
 
730
- static VALUE
731
- protect_open_proc(VALUE x) {
732
- ParseInfo pi = (ParseInfo)x;
657
+ static VALUE protect_open_proc(VALUE x) {
658
+ ParseInfo pi = (ParseInfo)x;
733
659
 
734
- pi->doc->data = read_next(pi); // parse
660
+ pi->doc->data = read_next(pi); // parse
735
661
  *pi->doc->where = pi->doc->data;
736
- pi->doc->where = pi->doc->where_path;
662
+ pi->doc->where = pi->doc->where_path;
737
663
  if (rb_block_given_p()) {
738
- return rb_yield(pi->doc->self); // caller processing
664
+ return rb_yield(pi->doc->self); // caller processing
739
665
  }
740
666
  return Qnil;
741
667
  }
742
668
 
743
- static void
744
- free_doc_cb(void *x) {
745
- Doc doc = (Doc)x;
669
+ static void free_doc_cb(void *x) {
670
+ Doc doc = (Doc)x;
746
671
 
747
672
  if (0 != doc) {
748
- xfree(doc->json);
749
- doc_free(doc);
673
+ doc_free(doc);
750
674
  }
751
675
  }
752
676
 
753
- static void
754
- mark_leaf(Leaf leaf) {
755
- switch (leaf->value_type) {
756
- case COL_VAL:
757
- if (NULL != leaf->elements) {
758
- Leaf first = leaf->elements->next;
759
- Leaf e = first;
760
-
761
- do {
762
- mark_leaf(e);
763
- e = e->next;
764
- } while (e != first);
765
- }
766
- break;
767
- case RUBY_VAL:
768
- rb_gc_mark(leaf->value);
769
- break;
770
-
771
- default:
772
- break;
677
+ static void mark_leaf(Leaf leaf) {
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;
684
+
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;
692
+
693
+ default: break;
694
+ }
773
695
  }
774
696
  }
775
697
 
776
- static void
777
- mark_doc(void *ptr) {
698
+ static void mark_doc(void *ptr) {
778
699
  if (NULL != ptr) {
779
- Doc doc = (Doc)ptr;
700
+ Doc doc = (Doc)ptr;
780
701
 
781
- rb_gc_mark(doc->self);
782
- mark_leaf(doc->data);
702
+ mark(doc->self);
703
+ mark_leaf(doc->data);
783
704
  }
784
705
  }
706
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
707
+ static void compact_leaf(Leaf leaf) {
708
+ switch (leaf->value_type) {
709
+ case COL_VAL:
710
+ if (NULL != leaf->elements) {
711
+ Leaf first = leaf->elements->next;
712
+ Leaf e = first;
785
713
 
786
- static VALUE
787
- parse_json(VALUE clas, char *json, bool given, bool allocated) {
788
- struct _parseInfo pi;
789
- volatile VALUE result = Qnil;
790
- Doc doc;
791
- int ex = 0;
792
- volatile VALUE self;
714
+ do {
715
+ compact_leaf(e);
716
+ e = e->next;
717
+ } while (e != first);
718
+ }
719
+ break;
720
+ case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
793
721
 
794
- // TBD are both needed? is stack allocation ever needed?
722
+ default: break;
723
+ }
724
+ }
795
725
 
796
- if (given) {
797
- doc = ALLOCA_N(struct _doc, 1);
798
- } else {
799
- doc = ALLOC(struct _doc);
726
+ static void compact_doc(void *ptr) {
727
+ Doc doc = (Doc)ptr;
728
+
729
+ if (doc) {
730
+ doc->self = rb_gc_location(doc->self);
731
+ compact_leaf(doc->data);
800
732
  }
801
- /* skip UTF-8 BOM if present */
733
+ }
734
+ #endif
735
+
736
+ static const rb_data_type_t oj_doc_type = {
737
+ "Oj/doc",
738
+ {
739
+ mark_doc,
740
+ free_doc_cb,
741
+ NULL,
742
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
743
+ compact_doc,
744
+ #endif
745
+ },
746
+ 0,
747
+ 0,
748
+ };
749
+
750
+ static VALUE parse_json(VALUE clas, char *json, bool given) {
751
+ struct _parseInfo pi;
752
+ volatile VALUE result = Qnil;
753
+ Doc doc;
754
+ int ex = 0;
755
+ volatile VALUE self;
756
+
757
+ doc = OJ_R_ALLOC_N(struct _doc, 1);
758
+
759
+ // skip UTF-8 BOM if present
802
760
  if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
803
- pi.str = json + 3;
761
+ pi.str = json + 3;
804
762
  } else {
805
- pi.str = json;
763
+ pi.str = json;
806
764
  }
807
765
  pi.s = pi.str;
808
766
  doc_init(doc);
809
767
  pi.doc = doc;
810
- #if IS_WINDOWS
811
- pi.stack_min = (void*)((char*)&pi - (512 * 1024)); // assume a 1M stack and give half to ruby
768
+ #if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
769
+ // assume a 1M stack and give half to ruby
770
+ pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
812
771
  #else
813
772
  {
814
- struct rlimit lim;
773
+ struct rlimit lim;
815
774
 
816
- if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
817
- pi.stack_min = (void*)((char*)&lim - (lim.rlim_cur / 4 * 3)); // let 3/4ths of the stack be used only
818
- } else {
819
- pi.stack_min = 0; // indicates not to check stack limit
820
- }
775
+ if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
776
+ // let 3/4ths of the stack be used only
777
+ pi.stack_min = (void *)((char *)&lim - (lim.rlim_cur / 4 * 3));
778
+ } else {
779
+ pi.stack_min = 0; // indicates not to check stack limit
780
+ }
821
781
  }
822
782
  #endif
823
- // last arg is free func void* func(void*)
824
- #ifdef HAVE_RB_DATA_OBJECT_WRAP
825
- self = rb_data_object_wrap(clas, doc, mark_doc, free_doc_cb);
826
- #else
827
- self = rb_data_object_alloc(clas, doc, mark_doc, free_doc_cb);
828
- #endif
829
- doc->self = self;
830
783
  doc->json = json;
831
- DATA_PTR(doc->self) = doc;
832
- result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
784
+ self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
785
+ doc->self = self;
786
+ result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
833
787
  if (given || 0 != ex) {
834
- DATA_PTR(doc->self) = NULL;
835
- doc_free(pi.doc);
836
- if (allocated && 0 != ex) { // will jump so caller will not free
837
- xfree(json);
838
- }
839
- rb_gc_enable();
788
+ DATA_PTR(doc->self) = NULL;
789
+ // TBD is this needed?
790
+ /*
791
+ doc_free(pi.doc);
792
+ if (0 != ex) { // will jump so caller will not free
793
+ OJ_R_FREE(json);
794
+ }
795
+ */
840
796
  } else {
841
- result = doc->self;
797
+ result = doc->self;
842
798
  }
843
799
  if (0 != ex) {
844
- rb_jump_tag(ex);
800
+ rb_jump_tag(ex);
845
801
  }
846
802
  return result;
847
803
  }
848
804
 
849
- static Leaf
850
- get_doc_leaf(Doc doc, const char *path) {
851
- Leaf leaf = *doc->where;
805
+ static Leaf get_doc_leaf(Doc doc, const char *path) {
806
+ Leaf leaf = *doc->where;
852
807
 
853
808
  if (0 != doc->data && 0 != path) {
854
- Leaf stack[MAX_STACK];
855
- Leaf *lp;
856
-
857
- if ('/' == *path) {
858
- path++;
859
- *stack = doc->data;
860
- lp = stack;
861
- } else if (doc->where == doc->where_path) {
862
- *stack = doc->data;
863
- lp = stack;
864
- } else {
865
- size_t cnt = doc->where - doc->where_path;
866
-
867
- if (MAX_STACK <= cnt) {
868
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
869
- }
870
- memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
871
- lp = stack + cnt;
872
- }
873
- return get_leaf(stack, lp, path);
809
+ Leaf stack[MAX_STACK];
810
+ Leaf *lp;
811
+
812
+ if ('/' == *path) {
813
+ path++;
814
+ *stack = doc->data;
815
+ lp = stack;
816
+ } else if (doc->where == doc->where_path) {
817
+ *stack = doc->data;
818
+ lp = stack;
819
+ } else {
820
+ size_t cnt = doc->where - doc->where_path;
821
+
822
+ if (MAX_STACK <= cnt) {
823
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
824
+ }
825
+ memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
826
+ lp = stack + cnt;
827
+ }
828
+ return get_leaf(stack, lp, path);
874
829
  }
875
830
  return leaf;
876
831
  }
877
832
 
878
- static const char*
879
- next_slash(const char *s) {
833
+ static const char *next_slash(const char *s) {
880
834
  for (; '\0' != *s; s++) {
881
- if ('\\' == *s) {
882
- s++;
883
- if ('\0' == *s) {
884
- break;
885
- }
886
- } else if ('/' == *s) {
887
- return s;
888
- }
835
+ if ('\\' == *s) {
836
+ s++;
837
+ if ('\0' == *s) {
838
+ break;
839
+ }
840
+ } else if ('/' == *s) {
841
+ return s;
842
+ }
889
843
  }
890
844
  return NULL;
891
845
  }
892
846
 
893
- static bool
894
- key_match(const char *pat, const char *key, int plen) {
847
+ static bool key_match(const char *pat, const char *key, int plen) {
895
848
  for (; 0 < plen; plen--, pat++, key++) {
896
- if ('\\' == *pat) {
897
- plen--;
898
- pat++;
899
- }
900
- if (*pat != *key) {
901
- return false;
902
- }
849
+ if ('\\' == *pat) {
850
+ plen--;
851
+ pat++;
852
+ }
853
+ if (*pat != *key) {
854
+ return false;
855
+ }
903
856
  }
904
857
  return '\0' == *key;
905
858
  }
906
859
 
907
- static Leaf
908
- get_leaf(Leaf *stack, Leaf *lp, const char *path) {
909
- Leaf leaf = *lp;
860
+ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
861
+ Leaf leaf = *lp;
910
862
 
911
863
  if (MAX_STACK <= lp - stack) {
912
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
864
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
913
865
  }
914
866
  if ('\0' != *path) {
915
- if ('.' == *path && '.' == *(path + 1)) {
916
- path += 2;
917
- if ('/' == *path) {
918
- path++;
919
- }
920
- if (stack < lp) {
921
- leaf = get_leaf(stack, lp - 1, path);
922
- } else {
923
- return 0;
924
- }
925
- } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
926
- Leaf first = leaf->elements->next;
927
- Leaf e = first;
928
- int type = leaf->rtype;
929
-
930
- leaf = 0;
931
- if (T_ARRAY == type) {
932
- int cnt = 0;
933
-
934
- for (; '0' <= *path && *path <= '9'; path++) {
935
- cnt = cnt * 10 + (*path - '0');
936
- }
937
- if ('/' == *path) {
938
- path++;
939
- }
940
- do {
941
- if (1 >= cnt) {
942
- lp++;
943
- *lp = e;
944
- leaf = get_leaf(stack, lp, path);
945
- break;
946
- }
947
- cnt--;
948
- e = e->next;
949
- } while (e != first);
950
- } else if (T_HASH == type) {
951
- const char *key = path;
952
- const char *slash = next_slash(path);
953
- int klen;
954
-
955
- if (0 == slash) {
956
- klen = (int)strlen(key);
957
- path += klen;
958
- } else {
959
- klen = (int)(slash - key);
960
- path += klen + 1;
961
- }
962
- do {
963
- if (key_match(key, e->key, klen)) {
964
- lp++;
965
- *lp = e;
966
- leaf = get_leaf(stack, lp, path);
967
- break;
968
- }
969
- e = e->next;
970
- } while (e != first);
971
- }
972
- }
867
+ if ('.' == *path && '.' == *(path + 1)) {
868
+ path += 2;
869
+ if ('/' == *path) {
870
+ path++;
871
+ }
872
+ if (stack < lp) {
873
+ leaf = get_leaf(stack, lp - 1, path);
874
+ } else {
875
+ return 0;
876
+ }
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) {
884
+ Leaf first = leaf->elements->next;
885
+ Leaf e = first;
886
+ int type = leaf->rtype;
887
+
888
+ leaf = NULL;
889
+ if (T_ARRAY == type) {
890
+ int cnt = 0;
891
+
892
+ for (; '0' <= *path && *path <= '9'; path++) {
893
+ cnt = cnt * 10 + (*path - '0');
894
+ }
895
+ if ('/' == *path) {
896
+ path++;
897
+ }
898
+ do {
899
+ if (1 >= cnt) {
900
+ lp++;
901
+ *lp = e;
902
+ leaf = get_leaf(stack, lp, path);
903
+ break;
904
+ }
905
+ cnt--;
906
+ e = e->next;
907
+ } while (e != first);
908
+ } else if (T_HASH == type) {
909
+ const char *key = path;
910
+ const char *slash = next_slash(path);
911
+ int klen;
912
+
913
+ leaf = NULL;
914
+ if (0 == slash) {
915
+ klen = (int)strlen(key);
916
+ path += klen;
917
+ } else {
918
+ klen = (int)(slash - key);
919
+ path += klen + 1;
920
+ }
921
+ do {
922
+ if (key_match(key, e->key, klen)) {
923
+ lp++;
924
+ *lp = e;
925
+ leaf = get_leaf(stack, lp, path);
926
+ break;
927
+ }
928
+ e = e->next;
929
+ } while (e != first);
930
+ }
931
+ }
973
932
  }
974
933
  return leaf;
975
934
  }
976
935
 
977
- static void
978
- each_leaf(Doc doc, VALUE self) {
936
+ static void each_leaf(Doc doc, VALUE self) {
979
937
  if (COL_VAL == (*doc->where)->value_type) {
980
- if (0 != (*doc->where)->elements) {
981
- Leaf first = (*doc->where)->elements->next;
982
- Leaf e = first;
983
-
984
- doc->where++;
985
- if (MAX_STACK <= doc->where - doc->where_path) {
986
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
987
- }
988
- do {
989
- *doc->where = e;
990
- each_leaf(doc, self);
991
- e = e->next;
992
- } while (e != first);
993
- doc->where--;
994
- }
938
+ if (0 != (*doc->where)->elements) {
939
+ Leaf first = (*doc->where)->elements->next;
940
+ Leaf e = first;
941
+
942
+ doc->where++;
943
+ if (MAX_STACK <= doc->where - doc->where_path) {
944
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
945
+ }
946
+ do {
947
+ *doc->where = e;
948
+ each_leaf(doc, self);
949
+ e = e->next;
950
+ } while (e != first);
951
+ doc->where--;
952
+ }
995
953
  } else {
996
- rb_yield(self);
954
+ rb_yield(self);
997
955
  }
998
956
  }
999
957
 
1000
- static int
1001
- move_step(Doc doc, const char *path, int loc) {
958
+ static int move_step(Doc doc, const char *path, int loc) {
1002
959
  if (MAX_STACK <= doc->where - doc->where_path) {
1003
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
960
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
1004
961
  }
1005
962
  if ('\0' == *path) {
1006
- loc = 0;
963
+ loc = 0;
1007
964
  } else {
1008
- Leaf leaf;
1009
-
1010
- if (0 == doc->where || 0 == (leaf = *doc->where)) {
1011
- printf("*** Internal error at %s\n", path);
1012
- return loc;
1013
- }
1014
- if ('.' == *path && '.' == *(path + 1)) {
1015
- Leaf init = *doc->where;
1016
-
1017
- path += 2;
1018
- if (doc->where == doc->where_path) {
1019
- return loc;
1020
- }
1021
- if ('/' == *path) {
1022
- path++;
1023
- }
1024
- *doc->where = 0;
1025
- doc->where--;
1026
- loc = move_step(doc, path, loc + 1);
1027
- if (0 != loc) {
1028
- *doc->where = init;
1029
- doc->where++;
1030
- }
1031
- } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
1032
- Leaf first = leaf->elements->next;
1033
- Leaf e = first;
1034
-
1035
- if (T_ARRAY == leaf->rtype) {
1036
- int cnt = 0;
1037
-
1038
- for (; '0' <= *path && *path <= '9'; path++) {
1039
- cnt = cnt * 10 + (*path - '0');
1040
- }
1041
- if ('/' == *path) {
1042
- path++;
1043
- } else if ('\0' != *path) {
1044
- return loc;
1045
- }
1046
- do {
1047
- if (1 >= cnt) {
1048
- doc->where++;
1049
- *doc->where = e;
1050
- loc = move_step(doc, path, loc + 1);
1051
- if (0 != loc) {
1052
- *doc->where = 0;
1053
- doc->where--;
1054
- }
1055
- break;
1056
- }
1057
- cnt--;
1058
- e = e->next;
1059
- } while (e != first);
1060
- } else if (T_HASH == leaf->rtype) {
1061
- const char *key = path;
1062
- const char *slash = next_slash(path);
1063
- int klen;
1064
-
1065
- if (0 == slash) {
1066
- klen = (int)strlen(key);
1067
- path += klen;
1068
- } else {
1069
- klen = (int)(slash - key);
1070
- path += klen + 1;
1071
- }
1072
- do {
1073
- if (key_match(key, e->key, klen)) {
1074
- doc->where++;
1075
- *doc->where = e;
1076
- loc = move_step(doc, path, loc + 1);
1077
- if (0 != loc) {
1078
- *doc->where = 0;
1079
- doc->where--;
1080
- }
1081
- break;
1082
- }
1083
- e = e->next;
1084
- } while (e != first);
1085
- }
1086
- }
965
+ Leaf leaf;
966
+
967
+ if (0 == doc->where || 0 == (leaf = *doc->where)) {
968
+ printf("*** Internal error at %s\n", path);
969
+ return loc;
970
+ }
971
+ if ('.' == *path && '.' == *(path + 1)) {
972
+ Leaf init = *doc->where;
973
+
974
+ path += 2;
975
+ if (doc->where == doc->where_path) {
976
+ return loc;
977
+ }
978
+ if ('/' == *path) {
979
+ path++;
980
+ }
981
+ *doc->where = 0;
982
+ doc->where--;
983
+ loc = move_step(doc, path, loc + 1);
984
+ if (0 != loc) {
985
+ *doc->where = init;
986
+ doc->where++;
987
+ }
988
+ } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
989
+ Leaf first = leaf->elements->next;
990
+ Leaf e = first;
991
+
992
+ if (T_ARRAY == leaf->rtype) {
993
+ int cnt = 0;
994
+
995
+ for (; '0' <= *path && *path <= '9'; path++) {
996
+ cnt = cnt * 10 + (*path - '0');
997
+ }
998
+ if ('/' == *path) {
999
+ path++;
1000
+ } else if ('\0' != *path) {
1001
+ return loc;
1002
+ }
1003
+ do {
1004
+ if (1 >= cnt) {
1005
+ doc->where++;
1006
+ *doc->where = e;
1007
+ loc = move_step(doc, path, loc + 1);
1008
+ if (0 != loc) {
1009
+ *doc->where = 0;
1010
+ doc->where--;
1011
+ }
1012
+ break;
1013
+ }
1014
+ cnt--;
1015
+ e = e->next;
1016
+ } while (e != first);
1017
+ } else if (T_HASH == leaf->rtype) {
1018
+ const char *key = path;
1019
+ const char *slash = next_slash(path);
1020
+ int klen;
1021
+
1022
+ if (0 == slash) {
1023
+ klen = (int)strlen(key);
1024
+ path += klen;
1025
+ } else {
1026
+ klen = (int)(slash - key);
1027
+ path += klen + 1;
1028
+ }
1029
+ do {
1030
+ if (key_match(key, e->key, klen)) {
1031
+ doc->where++;
1032
+ *doc->where = e;
1033
+ loc = move_step(doc, path, loc + 1);
1034
+ if (0 != loc) {
1035
+ *doc->where = 0;
1036
+ doc->where--;
1037
+ }
1038
+ break;
1039
+ }
1040
+ e = e->next;
1041
+ } while (e != first);
1042
+ }
1043
+ }
1087
1044
  }
1088
1045
  return loc;
1089
1046
  }
1090
1047
 
1091
- static void
1092
- each_value(Doc doc, Leaf leaf) {
1048
+ static void each_value(Doc doc, Leaf leaf) {
1093
1049
  if (COL_VAL == leaf->value_type) {
1094
- if (0 != leaf->elements) {
1095
- Leaf first = leaf->elements->next;
1096
- Leaf e = first;
1097
-
1098
- do {
1099
- each_value(doc, e);
1100
- e = e->next;
1101
- } while (e != first);
1102
- }
1050
+ if (0 != leaf->elements) {
1051
+ Leaf first = leaf->elements->next;
1052
+ Leaf e = first;
1053
+
1054
+ do {
1055
+ each_value(doc, e);
1056
+ e = e->next;
1057
+ } while (e != first);
1058
+ }
1103
1059
  } else {
1104
- rb_yield(leaf_value(doc, leaf));
1060
+ rb_yield(leaf_value(doc, leaf));
1105
1061
  }
1106
1062
  }
1107
1063
 
@@ -1116,7 +1072,8 @@ each_value(Doc doc, Leaf leaf) {
1116
1072
  *
1117
1073
  * @param [String] json JSON document string
1118
1074
  * @yieldparam [Oj::Doc] doc parsed JSON document
1119
- * @yieldreturn [Object] returns the result of the yield as the result of the method call
1075
+ * @yieldreturn [Object] returns the result of the yield as the result of the
1076
+ * method call
1120
1077
  * @example
1121
1078
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1122
1079
  * # or as an alternative
@@ -1124,33 +1081,24 @@ each_value(Doc doc, Leaf leaf) {
1124
1081
  * doc.size() #=> 4
1125
1082
  * doc.close()
1126
1083
  */
1127
- static VALUE
1128
- doc_open(VALUE clas, VALUE str) {
1129
- char *json;
1130
- size_t len;
1131
- volatile VALUE obj;
1132
- int given = rb_block_given_p();
1133
- int allocate;
1084
+ static VALUE doc_open(VALUE clas, VALUE str) {
1085
+ char *json;
1086
+ size_t len;
1087
+ volatile VALUE obj;
1088
+ int given = rb_block_given_p();
1134
1089
 
1135
1090
  Check_Type(str, T_STRING);
1136
- len = (int)RSTRING_LEN(str) + 1;
1137
- allocate = (SMALL_XML < len || !given);
1138
- if (allocate) {
1139
- json = ALLOC_N(char, len);
1140
- } else {
1141
- json = ALLOCA_N(char, len);
1142
- }
1143
- // It should not be necessaary to stop GC but if it is not stopped and a
1144
- // large string is parsed that string is corrupted or freed during
1145
- // parsing. I'm not sure what is going on exactly but disabling GC avoids
1146
- // the issue.
1147
- rb_gc_disable();
1091
+ len = (int)RSTRING_LEN(str) + 1;
1092
+ json = OJ_R_ALLOC_N(char, len);
1093
+
1148
1094
  memcpy(json, StringValuePtr(str), len);
1149
- obj = parse_json(clas, json, given, allocate);
1150
- rb_gc_enable();
1151
- if (given && allocate) {
1152
- xfree(json);
1095
+ obj = parse_json(clas, json, given);
1096
+ // TBD is this needed
1097
+ /*
1098
+ if (given) {
1099
+ OJ_R_FREE(json);
1153
1100
  }
1101
+ */
1154
1102
  return obj;
1155
1103
  }
1156
1104
 
@@ -1163,7 +1111,8 @@ doc_open(VALUE clas, VALUE str) {
1163
1111
  *
1164
1112
  * @param [String] filename name of file that contains a JSON document
1165
1113
  * @yieldparam [Oj::Doc] doc parsed JSON document
1166
- * @yieldreturn [Object] returns the result of the yield as the result of the method call
1114
+ * @yieldreturn [Object] returns the result of the yield as the result of the
1115
+ * method call
1167
1116
  * @example
1168
1117
  * File.open('array.json', 'w') { |f| f.write('[1,2,3]') }
1169
1118
  * Oj::Doc.open_file(filename) { |doc| doc.size() } #=> 4
@@ -1172,65 +1121,59 @@ doc_open(VALUE clas, VALUE str) {
1172
1121
  * doc.size() #=> 4
1173
1122
  * doc.close()
1174
1123
  */
1175
- static VALUE
1176
- doc_open_file(VALUE clas, VALUE filename) {
1177
- char *path;
1178
- char *json;
1179
- FILE *f;
1180
- size_t len;
1181
- volatile VALUE obj;
1182
- int given = rb_block_given_p();
1183
- int allocate;
1184
-
1185
- Check_Type(filename, T_STRING);
1124
+ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1125
+ char *path;
1126
+ char *json;
1127
+ FILE *f;
1128
+ size_t len;
1129
+ volatile VALUE obj;
1130
+ int given = rb_block_given_p();
1131
+
1186
1132
  path = StringValuePtr(filename);
1187
1133
  if (0 == (f = fopen(path, "r"))) {
1188
- rb_raise(rb_eIOError, "%s", strerror(errno));
1134
+ rb_raise(rb_eIOError, "%s", strerror(errno));
1189
1135
  }
1190
1136
  fseek(f, 0, SEEK_END);
1191
- len = ftell(f);
1192
- allocate = (SMALL_XML < len || !given);
1193
- if (allocate) {
1194
- json = ALLOC_N(char, len + 1);
1195
- } else {
1196
- json = ALLOCA_N(char, len + 1);
1197
- }
1137
+ len = ftell(f);
1138
+ json = OJ_R_ALLOC_N(char, len + 1);
1139
+
1198
1140
  fseek(f, 0, SEEK_SET);
1199
1141
  if (len != fread(json, 1, len, f)) {
1200
- fclose(f);
1201
- rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")),
1202
- "Failed to read %lu bytes from %s.", (unsigned long)len, path);
1142
+ fclose(f);
1143
+ rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")),
1144
+ "Failed to read %lu bytes from %s.",
1145
+ (unsigned long)len,
1146
+ path);
1203
1147
  }
1204
1148
  fclose(f);
1205
1149
  json[len] = '\0';
1206
- rb_gc_disable();
1207
- obj = parse_json(clas, json, given, allocate);
1208
- rb_gc_enable();
1209
- if (given && allocate) {
1210
- xfree(json);
1150
+ obj = parse_json(clas, json, given);
1151
+ // TBD is this needed
1152
+ /*
1153
+ if (given) {
1154
+ OJ_R_FREE(json);
1211
1155
  }
1156
+ */
1212
1157
  return obj;
1213
1158
  }
1214
1159
 
1215
- static int
1216
- esc_strlen(const char *s) {
1217
- int cnt = 0;
1160
+ static int esc_strlen(const char *s) {
1161
+ int cnt = 0;
1218
1162
 
1219
1163
  for (; '\0' != *s; s++, cnt++) {
1220
- if ('/' == *s) {
1221
- cnt++;
1222
- }
1164
+ if ('/' == *s) {
1165
+ cnt++;
1166
+ }
1223
1167
  }
1224
1168
  return cnt;
1225
1169
  }
1226
1170
 
1227
- static char*
1228
- append_key(char *p, const char *key) {
1171
+ static char *append_key(char *p, const char *key) {
1229
1172
  for (; '\0' != *key; p++, key++) {
1230
- if ('/' == *key) {
1231
- *p++ = '\\';
1232
- }
1233
- *p = *key;
1173
+ if ('/' == *key) {
1174
+ *p++ = '\\';
1175
+ }
1176
+ *p = *key;
1234
1177
  }
1235
1178
  return p;
1236
1179
  }
@@ -1239,68 +1182,85 @@ append_key(char *p, const char *key) {
1239
1182
  * @see Oj::Doc.open
1240
1183
  */
1241
1184
 
1242
- /* @overload where?() => String
1185
+ /* @overload where() => String
1243
1186
  *
1244
1187
  * Returns a String that describes the absolute path to the current location
1245
1188
  * in the JSON document.
1246
1189
  */
1247
- static VALUE
1248
- doc_where(VALUE self) {
1249
- Doc doc = self_doc(self);
1190
+ static VALUE doc_where(VALUE self) {
1191
+ Doc doc = self_doc(self);
1250
1192
 
1251
1193
  if (0 == *doc->where_path || doc->where == doc->where_path) {
1252
- return oj_slash_string;
1194
+ return oj_slash_string;
1253
1195
  } else {
1254
- Leaf *lp;
1255
- Leaf leaf;
1256
- size_t size = 3; // leading / and terminating \0
1257
- char *path;
1258
- char *p;
1259
-
1260
- for (lp = doc->where_path; lp <= doc->where; lp++) {
1261
- leaf = *lp;
1262
- if (T_HASH == leaf->parent_type) {
1263
- size += esc_strlen((*lp)->key) + 1;
1264
- } else if (T_ARRAY == leaf->parent_type) {
1265
- size += ((*lp)->index < 100) ? 3 : 11;
1266
- }
1267
- }
1268
- path = ALLOCA_N(char, size);
1269
- p = path;
1270
- for (lp = doc->where_path; lp <= doc->where; lp++) {
1271
- leaf = *lp;
1272
- if (T_HASH == leaf->parent_type) {
1273
- p = append_key(p, (*lp)->key);
1274
- } else if (T_ARRAY == leaf->parent_type) {
1275
- p = ulong_fill(p, (*lp)->index);
1276
- }
1277
- *p++ = '/';
1278
- }
1279
- *--p = '\0';
1280
-
1281
- return rb_str_new(path, p - path);
1196
+ Leaf *lp;
1197
+ Leaf leaf;
1198
+ size_t size = 3; // leading / and terminating \0
1199
+ char *path;
1200
+ char *p;
1201
+
1202
+ for (lp = doc->where_path; lp <= doc->where; lp++) {
1203
+ leaf = *lp;
1204
+ if (T_HASH == leaf->parent_type) {
1205
+ size += esc_strlen((*lp)->key) + 1;
1206
+ } else if (T_ARRAY == leaf->parent_type) {
1207
+ size += ((*lp)->index < 100) ? 3 : 11;
1208
+ }
1209
+ }
1210
+ path = ALLOCA_N(char, size);
1211
+ p = path;
1212
+ for (lp = doc->where_path; lp <= doc->where; lp++) {
1213
+ leaf = *lp;
1214
+ if (T_HASH == leaf->parent_type) {
1215
+ p = append_key(p, (*lp)->key);
1216
+ } else if (T_ARRAY == leaf->parent_type) {
1217
+ p = ulong_fill(p, (*lp)->index);
1218
+ }
1219
+ *p++ = '/';
1220
+ }
1221
+ *--p = '\0';
1222
+
1223
+ return rb_str_new(path, p - path);
1282
1224
  }
1283
1225
  }
1284
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
+
1285
1245
  /* @overload local_key() => String, Fixnum, nil
1286
1246
  *
1287
1247
  * Returns the final key to the current location.
1288
1248
  * @example
1289
- * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() } #=> 2
1290
- * Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=> "one"
1291
- * Oj::Doc.open('[1,2,3]') { |doc| doc.local_key() } #=> nil
1249
+ * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() } #=> 2
1250
+ * Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=>
1251
+ * "one" Oj::Doc.open('[1,2,3]') { |doc| doc.local_key() }
1252
+ * #=> nil
1292
1253
  */
1293
- static VALUE
1294
- doc_local_key(VALUE self) {
1295
- Doc doc = self_doc(self);
1296
- Leaf leaf = *doc->where;
1297
- volatile VALUE key = Qnil;
1254
+ static VALUE doc_local_key(VALUE self) {
1255
+ Doc doc = self_doc(self);
1256
+ Leaf leaf = *doc->where;
1257
+ volatile VALUE key = Qnil;
1298
1258
 
1299
1259
  if (T_HASH == leaf->parent_type) {
1300
- key = rb_str_new2(leaf->key);
1301
- key = oj_encode(key);
1260
+ key = rb_str_new2(leaf->key);
1261
+ key = oj_encode(key);
1302
1262
  } else if (T_ARRAY == leaf->parent_type) {
1303
- key = LONG2NUM(leaf->index);
1263
+ key = LONG2NUM(leaf->index);
1304
1264
  }
1305
1265
  return key;
1306
1266
  }
@@ -1310,14 +1270,14 @@ doc_local_key(VALUE self) {
1310
1270
  * Moves the document marker or location to the hoot or home position. The
1311
1271
  * same operation can be performed with a Oj::Doc.move('/').
1312
1272
  * @example
1313
- * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? } #=> '/'
1273
+ * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? }
1274
+ * #=> '/'
1314
1275
  */
1315
- static VALUE
1316
- doc_home(VALUE self) {
1317
- Doc doc = self_doc(self);
1276
+ static VALUE doc_home(VALUE self) {
1277
+ Doc doc = self_doc(self);
1318
1278
 
1319
1279
  *doc->where_path = doc->data;
1320
- doc->where = doc->where_path;
1280
+ doc->where = doc->where_path;
1321
1281
 
1322
1282
  return oj_slash_string;
1323
1283
  }
@@ -1333,76 +1293,91 @@ doc_home(VALUE self) {
1333
1293
  * Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
1334
1294
  * Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
1335
1295
  */
1336
- static VALUE
1337
- doc_type(int argc, VALUE *argv, VALUE self) {
1338
- Doc doc = self_doc(self);
1339
- Leaf leaf;
1340
- const char *path = 0;
1341
- VALUE type = Qnil;
1296
+ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
1297
+ Doc doc = self_doc(self);
1298
+ Leaf leaf;
1299
+ const char *path = 0;
1300
+ VALUE type = Qnil;
1342
1301
 
1343
1302
  if (1 <= argc) {
1344
- Check_Type(*argv, T_STRING);
1345
- path = StringValuePtr(*argv);
1303
+ path = StringValuePtr(*argv);
1346
1304
  }
1347
1305
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1348
- switch (leaf->rtype) {
1349
- case T_NIL: type = rb_cNilClass; break;
1350
- case T_TRUE: type = rb_cTrueClass; break;
1351
- case T_FALSE: type = rb_cFalseClass; break;
1352
- case T_STRING: type = rb_cString; break;
1353
- #ifdef RUBY_INTEGER_UNIFICATION
1354
- case T_FIXNUM: type = rb_cInteger; break;
1355
- #else
1356
- case T_FIXNUM: type = rb_cFixnum; break;
1357
- #endif
1358
- case T_FLOAT: type = rb_cFloat; break;
1359
- case T_ARRAY: type = rb_cArray; break;
1360
- case T_HASH: type = rb_cHash; break;
1361
- default: break;
1362
- }
1306
+ switch (leaf->rtype) {
1307
+ case T_NIL: type = rb_cNilClass; break;
1308
+ case T_TRUE: type = rb_cTrueClass; break;
1309
+ case T_FALSE: type = rb_cFalseClass; break;
1310
+ case T_STRING: type = rb_cString; break;
1311
+ case T_FIXNUM: type = rb_cInteger; break;
1312
+ case T_FLOAT: type = rb_cFloat; break;
1313
+ case T_ARRAY: type = rb_cArray; break;
1314
+ case T_HASH: type = rb_cHash; break;
1315
+ default: break;
1316
+ }
1363
1317
  }
1364
1318
  return type;
1365
1319
  }
1366
1320
 
1367
- /* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array, Hash
1321
+ /* @overload fetch(path=nil,default=nil) => nil, true, false, Fixnum, Float, String, Array,
1322
+ * Hash
1368
1323
  *
1369
1324
  * Returns the value at the location identified by the path or the current
1370
1325
  * location if the path is nil or not provided. This method will create and
1371
1326
  * return an Array or Hash if that is the type of Object at the location
1372
1327
  * specified. This is more expensive than navigating to the leaves of the JSON
1373
- * document.
1328
+ * document. If a default is provided that is used if no value if found.
1374
1329
  * @param [String] path path to the location to get the type of if provided
1375
1330
  * @example
1376
1331
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
1377
1332
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
1378
1333
  */
1379
- static VALUE
1380
- doc_fetch(int argc, VALUE *argv, VALUE self) {
1381
- Doc doc;
1382
- Leaf leaf;
1383
- volatile VALUE val = Qnil;
1384
- const char *path = 0;
1334
+ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
1335
+ Doc doc;
1336
+ Leaf leaf;
1337
+ volatile VALUE val = Qnil;
1338
+ const char *path = 0;
1385
1339
 
1386
1340
  doc = self_doc(self);
1387
1341
  if (1 <= argc) {
1388
- Check_Type(*argv, T_STRING);
1389
- path = StringValuePtr(*argv);
1390
- if (2 == argc) {
1391
- val = argv[1];
1392
- }
1342
+ path = StringValuePtr(*argv);
1343
+ if (2 == argc) {
1344
+ val = argv[1];
1345
+ }
1393
1346
  }
1394
1347
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1395
- val = leaf_value(doc, leaf);
1348
+ val = leaf_value(doc, leaf);
1396
1349
  }
1397
1350
  return val;
1398
1351
  }
1399
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
+
1400
1374
  /* @overload each_leaf(path=nil) => nil
1401
1375
  *
1402
1376
  * Yields to the provided block for each leaf node with the identified
1403
1377
  * location of the JSON document as the root. The parameter passed to the
1404
1378
  * block on yield is the Doc instance after moving to the child location.
1405
- * @param [String] path if provided it identified the top of the branch to process the leaves of
1379
+ * @param [String] path if provided it identified the top of the branch to
1380
+ * process the leaves of
1406
1381
  * @yieldparam [Doc] Doc at the child location
1407
1382
  * @example
1408
1383
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1412,36 +1387,34 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
1412
1387
  * }
1413
1388
  * #=> ["/1" => 3, "/2/1" => 2, "/2/2" => 1]
1414
1389
  */
1415
- static VALUE
1416
- doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1390
+ static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1417
1391
  if (rb_block_given_p()) {
1418
- Leaf save_path[MAX_STACK];
1419
- Doc doc = self_doc(self);
1420
- const char *path = 0;
1421
- size_t wlen;
1422
-
1423
- wlen = doc->where - doc->where_path;
1424
- if (0 < wlen) {
1425
- memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1426
- }
1427
- if (1 <= argc) {
1428
- Check_Type(*argv, T_STRING);
1429
- path = StringValuePtr(*argv);
1430
- if ('/' == *path) {
1431
- doc->where = doc->where_path;
1432
- path++;
1433
- }
1434
- if (0 != move_step(doc, path, 1)) {
1435
- if (0 < wlen) {
1436
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1437
- }
1438
- return Qnil;
1439
- }
1440
- }
1441
- each_leaf(doc, self);
1442
- if (0 < wlen) {
1443
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1444
- }
1392
+ Leaf save_path[MAX_STACK];
1393
+ Doc doc = self_doc(self);
1394
+ const char *path = 0;
1395
+ size_t wlen;
1396
+
1397
+ wlen = doc->where - doc->where_path;
1398
+ if (0 < wlen) {
1399
+ memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1400
+ }
1401
+ if (1 <= argc) {
1402
+ path = StringValuePtr(*argv);
1403
+ if ('/' == *path) {
1404
+ doc->where = doc->where_path;
1405
+ path++;
1406
+ }
1407
+ if (0 != move_step(doc, path, 1)) {
1408
+ if (0 < wlen) {
1409
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1410
+ }
1411
+ return Qnil;
1412
+ }
1413
+ }
1414
+ each_leaf(doc, self);
1415
+ if (0 < wlen) {
1416
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1417
+ }
1445
1418
  }
1446
1419
  return Qnil;
1447
1420
  }
@@ -1452,22 +1425,21 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1452
1425
  * path or a relative path.
1453
1426
  * @param [String] path path to the location to move to
1454
1427
  * @example
1455
- * Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=> "/one/2"
1428
+ * Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=>
1429
+ * "/one/2"
1456
1430
  */
1457
- static VALUE
1458
- doc_move(VALUE self, VALUE str) {
1459
- Doc doc = self_doc(self);
1460
- const char *path;
1461
- int loc;
1431
+ static VALUE doc_move(VALUE self, VALUE str) {
1432
+ Doc doc = self_doc(self);
1433
+ const char *path;
1434
+ int loc;
1462
1435
 
1463
- Check_Type(str, T_STRING);
1464
1436
  path = StringValuePtr(str);
1465
1437
  if ('/' == *path) {
1466
- doc->where = doc->where_path;
1467
- path++;
1438
+ doc->where = doc->where_path;
1439
+ path++;
1468
1440
  }
1469
1441
  if (0 != (loc = move_step(doc, path, 1))) {
1470
- rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
1442
+ rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
1471
1443
  }
1472
1444
  return Qnil;
1473
1445
  }
@@ -1478,7 +1450,8 @@ doc_move(VALUE self, VALUE str) {
1478
1450
  * identified location of the JSON document as the root. The parameter passed
1479
1451
  * to the block on yield is the Doc instance after moving to the child
1480
1452
  * location.
1481
- * @param [String] path if provided it identified the top of the branch to process the chilren of
1453
+ * @param [String] path if provided it identified the top of the branch to
1454
+ * process the children of
1482
1455
  * @yieldparam [Doc] Doc at the child location
1483
1456
  * @example
1484
1457
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1488,46 +1461,50 @@ doc_move(VALUE self, VALUE str) {
1488
1461
  * }
1489
1462
  * #=> ["/2/1", "/2/2"]
1490
1463
  */
1491
- static VALUE
1492
- doc_each_child(int argc, VALUE *argv, VALUE self) {
1464
+ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1493
1465
  if (rb_block_given_p()) {
1494
- Leaf save_path[MAX_STACK];
1495
- Doc doc = self_doc(self);
1496
- const char *path = 0;
1497
- size_t wlen;
1498
-
1499
- wlen = doc->where - doc->where_path;
1500
- if (0 < wlen) {
1501
- memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1502
- }
1503
- if (1 <= argc) {
1504
- Check_Type(*argv, T_STRING);
1505
- path = StringValuePtr(*argv);
1506
- if ('/' == *path) {
1507
- doc->where = doc->where_path;
1508
- path++;
1509
- }
1510
- if (0 != move_step(doc, path, 1)) {
1511
- if (0 < wlen) {
1512
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1513
- }
1514
- return Qnil;
1515
- }
1516
- }
1517
- if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
1518
- Leaf first = (*doc->where)->elements->next;
1519
- Leaf e = first;
1520
-
1521
- doc->where++;
1522
- do {
1523
- *doc->where = e;
1524
- rb_yield(self);
1525
- e = e->next;
1526
- } while (e != first);
1527
- }
1528
- if (0 < wlen) {
1529
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1530
- }
1466
+ Leaf save_path[MAX_STACK];
1467
+ Doc doc = self_doc(self);
1468
+ const char *path = 0;
1469
+ size_t wlen;
1470
+ Leaf *where_orig = doc->where;
1471
+
1472
+ wlen = doc->where - doc->where_path;
1473
+ if (0 < wlen) {
1474
+ memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1475
+ }
1476
+ if (1 <= argc) {
1477
+ path = StringValuePtr(*argv);
1478
+ if ('/' == *path) {
1479
+ doc->where = doc->where_path;
1480
+ path++;
1481
+ }
1482
+ if (0 != move_step(doc, path, 1)) {
1483
+ if (0 < wlen) {
1484
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1485
+ }
1486
+ doc->where = where_orig;
1487
+ return Qnil;
1488
+ }
1489
+ }
1490
+ if (NULL == doc->where || NULL == *doc->where) {
1491
+ return Qnil;
1492
+ }
1493
+ if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
1494
+ Leaf first = (*doc->where)->elements->next;
1495
+ Leaf e = first;
1496
+
1497
+ doc->where++;
1498
+ do {
1499
+ *doc->where = e;
1500
+ rb_yield(self);
1501
+ e = e->next;
1502
+ } while (e != first);
1503
+ }
1504
+ if (0 < wlen) {
1505
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1506
+ }
1507
+ doc->where = where_orig;
1531
1508
  }
1532
1509
  return Qnil;
1533
1510
  }
@@ -1538,7 +1515,8 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1538
1515
  * of the JSON document. The parameter passed to the block on yield is the
1539
1516
  * value of the leaf. Only those leaves below the element specified by the
1540
1517
  * path parameter are processed.
1541
- * @param [String] path if provided it identified the top of the branch to process the leaf values of
1518
+ * @param [String] path if provided it identified the top of the branch to
1519
+ * process the leaf values of
1542
1520
  * @yieldparam [Object] val each leaf value
1543
1521
  * @example
1544
1522
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1555,20 +1533,18 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1555
1533
  * }
1556
1534
  * #=> [2, 1]
1557
1535
  */
1558
- static VALUE
1559
- doc_each_value(int argc, VALUE *argv, VALUE self) {
1536
+ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
1560
1537
  if (rb_block_given_p()) {
1561
- Doc doc = self_doc(self);
1562
- const char *path = 0;
1563
- Leaf leaf;
1564
-
1565
- if (1 <= argc) {
1566
- Check_Type(*argv, T_STRING);
1567
- path = StringValuePtr(*argv);
1568
- }
1569
- if (0 != (leaf = get_doc_leaf(doc, path))) {
1570
- each_value(doc, leaf);
1571
- }
1538
+ Doc doc = self_doc(self);
1539
+ const char *path = 0;
1540
+ Leaf leaf;
1541
+
1542
+ if (1 <= argc) {
1543
+ path = StringValuePtr(*argv);
1544
+ }
1545
+ if (0 != (leaf = get_doc_leaf(doc, path))) {
1546
+ each_value(doc, leaf);
1547
+ }
1572
1548
  }
1573
1549
  return Qnil;
1574
1550
  }
@@ -1577,52 +1553,48 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
1577
1553
  *
1578
1554
  * Dumps the document or nodes to a new JSON document. It uses the default
1579
1555
  * options for generating the JSON.
1580
- * @param path [String] if provided it identified the top of the branch to dump to JSON
1581
- * @param filename [String] if provided it is the filename to write the output to
1556
+ * @param path [String] if provided it identified the top of the branch to
1557
+ * dump to JSON
1558
+ * @param filename [String] if provided it is the filename to write the output
1559
+ * to
1582
1560
  * @example
1583
1561
  * Oj::Doc.open('[3,[2,1]]') { |doc|
1584
1562
  * doc.dump('/2')
1585
1563
  * }
1586
1564
  * #=> "[2,1]"
1587
1565
  */
1588
- static VALUE
1589
- doc_dump(int argc, VALUE *argv, VALUE self) {
1590
- Doc doc = self_doc(self);
1591
- Leaf leaf;
1592
- const char *path = 0;
1593
- const char *filename = 0;
1566
+ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
1567
+ Doc doc = self_doc(self);
1568
+ Leaf leaf;
1569
+ const char *path = 0;
1570
+ const char *filename = 0;
1594
1571
 
1595
1572
  if (1 <= argc) {
1596
- if (Qnil != *argv) {
1597
- Check_Type(*argv, T_STRING);
1598
- path = StringValuePtr(*argv);
1599
- }
1600
- if (2 <= argc) {
1601
- Check_Type(argv[1], T_STRING);
1602
- filename = StringValuePtr(argv[1]);
1603
- }
1573
+ if (Qnil != *argv) {
1574
+ path = StringValuePtr(*argv);
1575
+ }
1576
+ if (2 <= argc) {
1577
+ filename = StringValuePtr(argv[1]);
1578
+ }
1604
1579
  }
1605
1580
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1606
- volatile VALUE rjson;
1607
-
1608
- if (0 == filename) {
1609
- char buf[4096];
1610
- struct _out out;
1611
-
1612
- out.buf = buf;
1613
- out.end = buf + sizeof(buf) - 10;
1614
- out.allocated = false;
1615
- out.omit_nil = oj_default_options.dump_opts.omit_nil;
1616
- oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
1617
- rjson = rb_str_new2(out.buf);
1618
- if (out.allocated) {
1619
- xfree(out.buf);
1620
- }
1621
- } else {
1622
- oj_write_leaf_to_file(leaf, filename, &oj_default_options);
1623
- rjson = Qnil;
1624
- }
1625
- return rjson;
1581
+ volatile VALUE rjson;
1582
+
1583
+ if (0 == filename) {
1584
+ struct _out out;
1585
+
1586
+ oj_out_init(&out);
1587
+
1588
+ out.omit_nil = oj_default_options.dump_opts.omit_nil;
1589
+ oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
1590
+ rjson = rb_str_new2(out.buf);
1591
+
1592
+ oj_out_free(&out);
1593
+ } else {
1594
+ oj_write_leaf_to_file(leaf, filename, &oj_default_options);
1595
+ rjson = Qnil;
1596
+ }
1597
+ return rjson;
1626
1598
  }
1627
1599
  return Qnil;
1628
1600
  }
@@ -1635,9 +1607,10 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
1635
1607
  * @example
1636
1608
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1637
1609
  */
1638
- static VALUE
1639
- doc_size(VALUE self) {
1640
- return ULONG2NUM(((Doc)DATA_PTR(self))->size);
1610
+ static VALUE doc_size(VALUE self) {
1611
+ Doc d;
1612
+ TypedData_Get_Struct(self, struct _doc, &oj_doc_type, d);
1613
+ return ULONG2NUM(d->size);
1641
1614
  }
1642
1615
 
1643
1616
  /* @overload close() => nil
@@ -1649,16 +1622,13 @@ doc_size(VALUE self) {
1649
1622
  * doc.size() #=> 4
1650
1623
  * doc.close()
1651
1624
  */
1652
- static VALUE
1653
- doc_close(VALUE self) {
1654
- Doc doc = self_doc(self);
1625
+ static VALUE doc_close(VALUE self) {
1626
+ Doc doc = self_doc(self);
1655
1627
 
1656
1628
  rb_gc_unregister_address(&doc->self);
1657
- DATA_PTR(doc->self) = 0;
1629
+ DATA_PTR(doc->self) = NULL;
1658
1630
  if (0 != doc) {
1659
- xfree(doc->json);
1660
- doc_free(doc);
1661
- xfree(doc);
1631
+ doc_free(doc);
1662
1632
  }
1663
1633
  return Qnil;
1664
1634
  }
@@ -1667,8 +1637,7 @@ doc_close(VALUE self) {
1667
1637
  Oj = rb_define_module("Oj");
1668
1638
  #endif
1669
1639
 
1670
- static VALUE
1671
- doc_not_implemented(VALUE self) {
1640
+ static VALUE doc_not_implemented(VALUE self) {
1672
1641
  rb_raise(rb_eNotImpError, "Not implemented.");
1673
1642
  return Qnil;
1674
1643
  }
@@ -1709,27 +1678,28 @@ doc_not_implemented(VALUE self) {
1709
1678
  * # move and get value
1710
1679
  * Oj::Doc.open(json) do |doc|
1711
1680
  * doc.move('/1/two')
1712
- * # doc location is now at the 'two' element of the hash that is the first element of the array.
1713
- * doc.fetch()
1714
- * end
1681
+ * # doc location is now at the 'two' element of the hash that is the first
1682
+ * element of the array. doc.fetch() end
1715
1683
  * #=> 2
1716
1684
  *
1717
- * # Now try again using a path to Oj::Doc.fetch() directly and not using a block.
1718
- * doc = Oj::Doc.open(json)
1719
- * doc.fetch('/2/three') #=> 3
1720
- * doc.close()
1685
+ * # Now try again using a path to Oj::Doc.fetch() directly and not using a
1686
+ * block. doc = Oj::Doc.open(json) doc.fetch('/2/three') #=> 3 doc.close()
1721
1687
  */
1722
- void
1723
- oj_init_doc() {
1688
+ void oj_init_doc(void) {
1724
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);
1725
1692
  rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
1726
1693
  rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
1727
1694
  rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
1728
- 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);
1729
1698
  rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
1730
1699
  rb_define_method(oj_doc_class, "home", doc_home, 0);
1731
1700
  rb_define_method(oj_doc_class, "type", doc_type, -1);
1732
1701
  rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
1702
+ rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
1733
1703
  rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
1734
1704
  rb_define_method(oj_doc_class, "move", doc_move, 1);
1735
1705
  rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);