oj 3.7.4 → 3.13.23

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