oj 3.7.4 → 3.13.21

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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1352 -0
  3. data/README.md +29 -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 +809 -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 +1070 -1087
  29. data/ext/oj/intern.c +301 -0
  30. data/ext/oj/intern.h +26 -0
  31. data/ext/oj/mimic_json.c +469 -436
  32. data/ext/oj/object.c +525 -593
  33. data/ext/oj/odd.c +154 -138
  34. data/ext/oj/odd.h +37 -38
  35. data/ext/oj/oj.c +1325 -986
  36. data/ext/oj/oj.h +333 -316
  37. data/ext/oj/parse.c +1002 -846
  38. data/ext/oj/parse.h +92 -87
  39. data/ext/oj/parser.c +1557 -0
  40. data/ext/oj/parser.h +91 -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 +602 -0
  51. data/ext/oj/scp.c +88 -113
  52. data/ext/oj/sparse.c +787 -709
  53. data/ext/oj/stream_writer.c +133 -159
  54. data/ext/oj/strict.c +127 -118
  55. data/ext/oj/string_writer.c +230 -249
  56. data/ext/oj/trace.c +34 -41
  57. data/ext/oj/trace.h +19 -19
  58. data/ext/oj/usual.c +1254 -0
  59. data/ext/oj/util.c +136 -0
  60. data/ext/oj/util.h +20 -0
  61. data/ext/oj/val_stack.c +59 -67
  62. data/ext/oj/val_stack.h +91 -129
  63. data/ext/oj/validate.c +46 -0
  64. data/ext/oj/wab.c +342 -353
  65. data/lib/oj/bag.rb +1 -0
  66. data/lib/oj/easy_hash.rb +5 -4
  67. data/lib/oj/error.rb +1 -1
  68. data/lib/oj/json.rb +1 -1
  69. data/lib/oj/mimic.rb +48 -14
  70. data/lib/oj/saj.rb +20 -6
  71. data/lib/oj/state.rb +8 -7
  72. data/lib/oj/version.rb +2 -2
  73. data/lib/oj.rb +0 -8
  74. data/pages/Compatibility.md +1 -1
  75. data/pages/JsonGem.md +15 -0
  76. data/pages/Modes.md +53 -46
  77. data/pages/Options.md +72 -11
  78. data/pages/Parser.md +309 -0
  79. data/pages/Rails.md +73 -22
  80. data/pages/Security.md +1 -1
  81. data/test/activerecord/result_test.rb +7 -2
  82. data/test/activesupport5/abstract_unit.rb +45 -0
  83. data/test/activesupport5/decoding_test.rb +68 -60
  84. data/test/activesupport5/encoding_test.rb +111 -96
  85. data/test/activesupport5/encoding_test_cases.rb +33 -25
  86. data/test/activesupport5/test_helper.rb +43 -21
  87. data/test/activesupport5/time_zone_test_helpers.rb +18 -3
  88. data/test/activesupport6/abstract_unit.rb +44 -0
  89. data/test/activesupport6/decoding_test.rb +133 -0
  90. data/test/activesupport6/encoding_test.rb +507 -0
  91. data/test/activesupport6/encoding_test_cases.rb +98 -0
  92. data/test/activesupport6/test_common.rb +17 -0
  93. data/test/activesupport6/test_helper.rb +163 -0
  94. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  95. data/test/activesupport7/abstract_unit.rb +49 -0
  96. data/test/activesupport7/decoding_test.rb +125 -0
  97. data/test/activesupport7/encoding_test.rb +486 -0
  98. data/test/activesupport7/encoding_test_cases.rb +104 -0
  99. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  100. data/test/bar.rb +6 -12
  101. data/test/baz.rb +16 -0
  102. data/test/bug.rb +16 -0
  103. data/test/foo.rb +69 -75
  104. data/test/helper.rb +16 -0
  105. data/test/json_gem/json_common_interface_test.rb +8 -3
  106. data/test/json_gem/json_generator_test.rb +18 -4
  107. data/test/json_gem/json_parser_test.rb +9 -0
  108. data/test/json_gem/test_helper.rb +12 -0
  109. data/test/mem.rb +33 -0
  110. data/test/perf.rb +1 -1
  111. data/test/perf_dump.rb +50 -0
  112. data/test/perf_once.rb +58 -0
  113. data/test/perf_parser.rb +189 -0
  114. data/test/perf_scp.rb +11 -10
  115. data/test/perf_strict.rb +17 -23
  116. data/test/prec.rb +23 -0
  117. data/test/sample_json.rb +1 -1
  118. data/test/test_compat.rb +46 -10
  119. data/test/test_custom.rb +147 -8
  120. data/test/test_fast.rb +62 -2
  121. data/test/test_file.rb +25 -2
  122. data/test/test_gc.rb +13 -0
  123. data/test/test_generate.rb +21 -0
  124. data/test/test_hash.rb +11 -1
  125. data/test/test_integer_range.rb +7 -2
  126. data/test/test_object.rb +85 -9
  127. data/test/test_parser.rb +27 -0
  128. data/test/test_parser_saj.rb +335 -0
  129. data/test/test_parser_usual.rb +217 -0
  130. data/test/test_rails.rb +35 -0
  131. data/test/test_saj.rb +1 -1
  132. data/test/test_scp.rb +5 -5
  133. data/test/test_strict.rb +26 -1
  134. data/test/test_various.rb +87 -65
  135. data/test/test_wab.rb +2 -0
  136. data/test/test_writer.rb +19 -2
  137. data/test/tests.rb +1 -1
  138. data/test/zoo.rb +13 -0
  139. metadata +60 -110
  140. data/ext/oj/hash.c +0 -163
  141. data/ext/oj/hash.h +0 -46
  142. 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,726 @@ 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) {
783
680
  switch (leaf->value_type) {
784
681
  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;
682
+ if (NULL != leaf->elements) {
683
+ Leaf first = leaf->elements->next;
684
+ Leaf e = first;
685
+
686
+ do {
687
+ mark_leaf(e);
688
+ e = e->next;
689
+ } while (e != first);
690
+ }
691
+ break;
692
+ case RUBY_VAL: mark(leaf->value); break;
693
+
694
+ default: break;
801
695
  }
802
696
  }
803
697
 
804
- static void
805
- mark_doc(void *ptr) {
698
+ static void mark_doc(void *ptr) {
806
699
  if (NULL != ptr) {
807
- Doc doc = (Doc)ptr;
808
-
809
- rb_gc_mark(doc->self);
810
- mark_leaf(doc->data);
700
+ Doc doc = (Doc)ptr;
701
+
702
+ mark(doc->self);
703
+ mark_leaf(doc->data);
811
704
  }
812
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;
713
+
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;
721
+
722
+ default: break;
723
+ }
724
+ }
725
+
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);
732
+ }
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, bool allocated) {
751
+ struct _parseInfo pi;
752
+ volatile VALUE result = Qnil;
753
+ Doc doc;
754
+ int ex = 0;
755
+ volatile VALUE self;
813
756
 
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;
757
+ // TBD are both needed? is stack allocation ever needed?
821
758
 
822
759
  if (given) {
823
- doc = ALLOCA_N(struct _Doc, 1);
760
+ doc = ALLOCA_N(struct _doc, 1);
824
761
  } else {
825
- doc = ALLOC(struct _Doc);
762
+ doc = ALLOC(struct _doc);
826
763
  }
827
- /* skip UTF-8 BOM if present */
764
+ // skip UTF-8 BOM if present
828
765
  if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
829
- pi.str = json + 3;
766
+ pi.str = json + 3;
830
767
  } else {
831
- pi.str = json;
768
+ pi.str = json;
832
769
  }
833
770
  pi.s = pi.str;
834
771
  doc_init(doc);
835
772
  pi.doc = doc;
836
773
  #if IS_WINDOWS
837
- pi.stack_min = (void*)((char*)&pi - (512 * 1024)); // assume a 1M stack and give half to ruby
774
+ // assume a 1M stack and give half to ruby
775
+ pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
838
776
  #else
839
777
  {
840
- struct rlimit lim;
778
+ struct rlimit lim;
841
779
 
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
- }
780
+ if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
781
+ // let 3/4ths of the stack be used only
782
+ pi.stack_min = (void *)((char *)&lim - (lim.rlim_cur / 4 * 3));
783
+ } else {
784
+ pi.stack_min = 0; // indicates not to check stack limit
785
+ }
847
786
  }
848
787
  #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;
788
+ self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
789
+ doc->self = self;
790
+ doc->json = json;
857
791
  DATA_PTR(doc->self) = doc;
858
- result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
792
+ result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
859
793
  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
- }
794
+ DATA_PTR(doc->self) = NULL;
795
+ doc_free(pi.doc);
796
+ if (allocated && 0 != ex) { // will jump so caller will not free
797
+ xfree(json);
798
+ }
799
+ rb_gc_enable();
865
800
  } else {
866
- result = doc->self;
801
+ result = doc->self;
867
802
  }
868
803
  if (0 != ex) {
869
- rb_jump_tag(ex);
804
+ rb_jump_tag(ex);
870
805
  }
871
806
  return result;
872
807
  }
873
808
 
874
- static Leaf
875
- get_doc_leaf(Doc doc, const char *path) {
876
- Leaf leaf = *doc->where;
809
+ static Leaf get_doc_leaf(Doc doc, const char *path) {
810
+ Leaf leaf = *doc->where;
877
811
 
878
812
  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);
813
+ Leaf stack[MAX_STACK];
814
+ Leaf *lp;
815
+
816
+ if ('/' == *path) {
817
+ path++;
818
+ *stack = doc->data;
819
+ lp = stack;
820
+ } else if (doc->where == doc->where_path) {
821
+ *stack = doc->data;
822
+ lp = stack;
823
+ } else {
824
+ size_t cnt = doc->where - doc->where_path;
825
+
826
+ if (MAX_STACK <= cnt) {
827
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
828
+ }
829
+ memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
830
+ lp = stack + cnt;
831
+ }
832
+ return get_leaf(stack, lp, path);
899
833
  }
900
834
  return leaf;
901
835
  }
902
836
 
903
- static const char*
904
- next_slash(const char *s) {
837
+ static const char *next_slash(const char *s) {
905
838
  for (; '\0' != *s; s++) {
906
- if ('\\' == *s) {
907
- s++;
908
- if ('\0' == *s) {
909
- break;
910
- }
911
- } else if ('/' == *s) {
912
- return s;
913
- }
839
+ if ('\\' == *s) {
840
+ s++;
841
+ if ('\0' == *s) {
842
+ break;
843
+ }
844
+ } else if ('/' == *s) {
845
+ return s;
846
+ }
914
847
  }
915
848
  return NULL;
916
849
  }
917
850
 
918
- static bool
919
- key_match(const char *pat, const char *key, int plen) {
851
+ static bool key_match(const char *pat, const char *key, int plen) {
920
852
  for (; 0 < plen; plen--, pat++, key++) {
921
- if ('\\' == *pat) {
922
- plen--;
923
- pat++;
924
- }
925
- if (*pat != *key) {
926
- return false;
927
- }
853
+ if ('\\' == *pat) {
854
+ plen--;
855
+ pat++;
856
+ }
857
+ if (*pat != *key) {
858
+ return false;
859
+ }
928
860
  }
929
861
  return '\0' == *key;
930
862
  }
931
863
 
932
- static Leaf
933
- get_leaf(Leaf *stack, Leaf *lp, const char *path) {
934
- Leaf leaf = *lp;
864
+ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
865
+ Leaf leaf = *lp;
935
866
 
936
867
  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);
868
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
938
869
  }
939
870
  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
- }
871
+ if ('.' == *path && '.' == *(path + 1)) {
872
+ path += 2;
873
+ if ('/' == *path) {
874
+ path++;
875
+ }
876
+ if (stack < lp) {
877
+ leaf = get_leaf(stack, lp - 1, path);
878
+ } else {
879
+ return 0;
880
+ }
881
+ } else if (NULL == leaf->elements) {
882
+ leaf = NULL;
883
+ } else if (STR_VAL == leaf->value_type || RUBY_VAL == leaf->value_type) {
884
+ // We are trying to get a children of a leaf, which
885
+ // doesn't exist.
886
+ leaf = NULL;
887
+ } else if (COL_VAL == leaf->value_type) {
888
+ Leaf first = leaf->elements->next;
889
+ Leaf e = first;
890
+ int type = leaf->rtype;
891
+
892
+ leaf = NULL;
893
+ if (T_ARRAY == type) {
894
+ int cnt = 0;
895
+
896
+ for (; '0' <= *path && *path <= '9'; path++) {
897
+ cnt = cnt * 10 + (*path - '0');
898
+ }
899
+ if ('/' == *path) {
900
+ path++;
901
+ }
902
+ do {
903
+ if (1 >= cnt) {
904
+ lp++;
905
+ *lp = e;
906
+ leaf = get_leaf(stack, lp, path);
907
+ break;
908
+ }
909
+ cnt--;
910
+ e = e->next;
911
+ } while (e != first);
912
+ } else if (T_HASH == type) {
913
+ const char *key = path;
914
+ const char *slash = next_slash(path);
915
+ int klen;
916
+
917
+ leaf = NULL;
918
+ if (0 == slash) {
919
+ klen = (int)strlen(key);
920
+ path += klen;
921
+ } else {
922
+ klen = (int)(slash - key);
923
+ path += klen + 1;
924
+ }
925
+ do {
926
+ if (key_match(key, e->key, klen)) {
927
+ lp++;
928
+ *lp = e;
929
+ leaf = get_leaf(stack, lp, path);
930
+ break;
931
+ }
932
+ e = e->next;
933
+ } while (e != first);
934
+ }
935
+ }
998
936
  }
999
937
  return leaf;
1000
938
  }
1001
939
 
1002
- static void
1003
- each_leaf(Doc doc, VALUE self) {
940
+ static void each_leaf(Doc doc, VALUE self) {
1004
941
  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
- }
942
+ if (0 != (*doc->where)->elements) {
943
+ Leaf first = (*doc->where)->elements->next;
944
+ Leaf e = first;
945
+
946
+ doc->where++;
947
+ if (MAX_STACK <= doc->where - doc->where_path) {
948
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
949
+ }
950
+ do {
951
+ *doc->where = e;
952
+ each_leaf(doc, self);
953
+ e = e->next;
954
+ } while (e != first);
955
+ doc->where--;
956
+ }
1020
957
  } else {
1021
- rb_yield(self);
958
+ rb_yield(self);
1022
959
  }
1023
960
  }
1024
961
 
1025
- static int
1026
- move_step(Doc doc, const char *path, int loc) {
962
+ static int move_step(Doc doc, const char *path, int loc) {
1027
963
  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);
964
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
1029
965
  }
1030
966
  if ('\0' == *path) {
1031
- loc = 0;
967
+ loc = 0;
1032
968
  } 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
- }
969
+ Leaf leaf;
970
+
971
+ if (0 == doc->where || 0 == (leaf = *doc->where)) {
972
+ printf("*** Internal error at %s\n", path);
973
+ return loc;
974
+ }
975
+ if ('.' == *path && '.' == *(path + 1)) {
976
+ Leaf init = *doc->where;
977
+
978
+ path += 2;
979
+ if (doc->where == doc->where_path) {
980
+ return loc;
981
+ }
982
+ if ('/' == *path) {
983
+ path++;
984
+ }
985
+ *doc->where = 0;
986
+ doc->where--;
987
+ loc = move_step(doc, path, loc + 1);
988
+ if (0 != loc) {
989
+ *doc->where = init;
990
+ doc->where++;
991
+ }
992
+ } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
993
+ Leaf first = leaf->elements->next;
994
+ Leaf e = first;
995
+
996
+ if (T_ARRAY == leaf->rtype) {
997
+ int cnt = 0;
998
+
999
+ for (; '0' <= *path && *path <= '9'; path++) {
1000
+ cnt = cnt * 10 + (*path - '0');
1001
+ }
1002
+ if ('/' == *path) {
1003
+ path++;
1004
+ } else if ('\0' != *path) {
1005
+ return loc;
1006
+ }
1007
+ do {
1008
+ if (1 >= cnt) {
1009
+ doc->where++;
1010
+ *doc->where = e;
1011
+ loc = move_step(doc, path, loc + 1);
1012
+ if (0 != loc) {
1013
+ *doc->where = 0;
1014
+ doc->where--;
1015
+ }
1016
+ break;
1017
+ }
1018
+ cnt--;
1019
+ e = e->next;
1020
+ } while (e != first);
1021
+ } else if (T_HASH == leaf->rtype) {
1022
+ const char *key = path;
1023
+ const char *slash = next_slash(path);
1024
+ int klen;
1025
+
1026
+ if (0 == slash) {
1027
+ klen = (int)strlen(key);
1028
+ path += klen;
1029
+ } else {
1030
+ klen = (int)(slash - key);
1031
+ path += klen + 1;
1032
+ }
1033
+ do {
1034
+ if (key_match(key, e->key, klen)) {
1035
+ doc->where++;
1036
+ *doc->where = e;
1037
+ loc = move_step(doc, path, loc + 1);
1038
+ if (0 != loc) {
1039
+ *doc->where = 0;
1040
+ doc->where--;
1041
+ }
1042
+ break;
1043
+ }
1044
+ e = e->next;
1045
+ } while (e != first);
1046
+ }
1047
+ }
1112
1048
  }
1113
1049
  return loc;
1114
1050
  }
1115
1051
 
1116
- static void
1117
- each_value(Doc doc, Leaf leaf) {
1052
+ static void each_value(Doc doc, Leaf leaf) {
1118
1053
  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
- }
1054
+ if (0 != leaf->elements) {
1055
+ Leaf first = leaf->elements->next;
1056
+ Leaf e = first;
1057
+
1058
+ do {
1059
+ each_value(doc, e);
1060
+ e = e->next;
1061
+ } while (e != first);
1062
+ }
1128
1063
  } else {
1129
- rb_yield(leaf_value(doc, leaf));
1064
+ rb_yield(leaf_value(doc, leaf));
1130
1065
  }
1131
1066
  }
1132
1067
 
@@ -1141,7 +1076,8 @@ each_value(Doc doc, Leaf leaf) {
1141
1076
  *
1142
1077
  * @param [String] json JSON document string
1143
1078
  * @yieldparam [Oj::Doc] doc parsed JSON document
1144
- * @yieldreturn [Object] returns the result of the yield as the result of the method call
1079
+ * @yieldreturn [Object] returns the result of the yield as the result of the
1080
+ * method call
1145
1081
  * @example
1146
1082
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1147
1083
  * # or as an alternative
@@ -1149,26 +1085,31 @@ each_value(Doc doc, Leaf leaf) {
1149
1085
  * doc.size() #=> 4
1150
1086
  * doc.close()
1151
1087
  */
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;
1088
+ static VALUE doc_open(VALUE clas, VALUE str) {
1089
+ char * json;
1090
+ size_t len;
1091
+ volatile VALUE obj;
1092
+ int given = rb_block_given_p();
1093
+ int allocate;
1159
1094
 
1160
1095
  Check_Type(str, T_STRING);
1161
- len = (int)RSTRING_LEN(str) + 1;
1162
- allocate = (SMALL_XML < len || !given);
1096
+ len = (int)RSTRING_LEN(str) + 1;
1097
+ allocate = (SMALL_JSON < len || !given);
1163
1098
  if (allocate) {
1164
- json = ALLOC_N(char, len);
1099
+ json = ALLOC_N(char, len);
1165
1100
  } else {
1166
- json = ALLOCA_N(char, len);
1101
+ json = ALLOCA_N(char, len);
1167
1102
  }
1103
+ // It should not be necessaary to stop GC but if it is not stopped and a
1104
+ // large string is parsed that string is corrupted or freed during
1105
+ // parsing. I'm not sure what is going on exactly but disabling GC avoids
1106
+ // the issue.
1107
+ rb_gc_disable();
1168
1108
  memcpy(json, StringValuePtr(str), len);
1169
1109
  obj = parse_json(clas, json, given, allocate);
1110
+ rb_gc_enable();
1170
1111
  if (given && allocate) {
1171
- xfree(json);
1112
+ xfree(json);
1172
1113
  }
1173
1114
  return obj;
1174
1115
  }
@@ -1182,7 +1123,8 @@ doc_open(VALUE clas, VALUE str) {
1182
1123
  *
1183
1124
  * @param [String] filename name of file that contains a JSON document
1184
1125
  * @yieldparam [Oj::Doc] doc parsed JSON document
1185
- * @yieldreturn [Object] returns the result of the yield as the result of the method call
1126
+ * @yieldreturn [Object] returns the result of the yield as the result of the
1127
+ * method call
1186
1128
  * @example
1187
1129
  * File.open('array.json', 'w') { |f| f.write('[1,2,3]') }
1188
1130
  * Oj::Doc.open_file(filename) { |doc| doc.size() } #=> 4
@@ -1191,63 +1133,64 @@ doc_open(VALUE clas, VALUE str) {
1191
1133
  * doc.size() #=> 4
1192
1134
  * doc.close()
1193
1135
  */
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;
1136
+ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1137
+ char * path;
1138
+ char * json;
1139
+ FILE * f;
1140
+ size_t len;
1141
+ volatile VALUE obj;
1142
+ int given = rb_block_given_p();
1143
+ int allocate;
1203
1144
 
1204
1145
  Check_Type(filename, T_STRING);
1205
1146
  path = StringValuePtr(filename);
1206
1147
  if (0 == (f = fopen(path, "r"))) {
1207
- rb_raise(rb_eIOError, "%s", strerror(errno));
1148
+ rb_raise(rb_eIOError, "%s", strerror(errno));
1208
1149
  }
1209
1150
  fseek(f, 0, SEEK_END);
1210
- len = ftell(f);
1211
- allocate = (SMALL_XML < len || !given);
1151
+ len = ftell(f);
1152
+ allocate = (SMALL_JSON < len || !given);
1212
1153
  if (allocate) {
1213
- json = ALLOC_N(char, len + 1);
1154
+ json = ALLOC_N(char, len + 1);
1214
1155
  } else {
1215
- json = ALLOCA_N(char, len + 1);
1156
+ json = ALLOCA_N(char, len + 1);
1216
1157
  }
1217
1158
  fseek(f, 0, SEEK_SET);
1218
1159
  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);
1160
+ fclose(f);
1161
+ rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")),
1162
+ "Failed to read %lu bytes from %s.",
1163
+ (unsigned long)len,
1164
+ path);
1222
1165
  }
1223
1166
  fclose(f);
1224
1167
  json[len] = '\0';
1168
+ rb_gc_disable();
1225
1169
  obj = parse_json(clas, json, given, allocate);
1170
+ rb_gc_enable();
1226
1171
  if (given && allocate) {
1227
- xfree(json);
1172
+ xfree(json);
1228
1173
  }
1229
1174
  return obj;
1230
1175
  }
1231
1176
 
1232
- static int
1233
- esc_strlen(const char *s) {
1234
- int cnt = 0;
1177
+ static int esc_strlen(const char *s) {
1178
+ int cnt = 0;
1235
1179
 
1236
1180
  for (; '\0' != *s; s++, cnt++) {
1237
- if ('/' == *s) {
1238
- cnt++;
1239
- }
1181
+ if ('/' == *s) {
1182
+ cnt++;
1183
+ }
1240
1184
  }
1241
1185
  return cnt;
1242
1186
  }
1243
1187
 
1244
- static char*
1245
- append_key(char *p, const char *key) {
1188
+ static char *append_key(char *p, const char *key) {
1246
1189
  for (; '\0' != *key; p++, key++) {
1247
- if ('/' == *key) {
1248
- *p++ = '\\';
1249
- }
1250
- *p = *key;
1190
+ if ('/' == *key) {
1191
+ *p++ = '\\';
1192
+ }
1193
+ *p = *key;
1251
1194
  }
1252
1195
  return p;
1253
1196
  }
@@ -1256,68 +1199,85 @@ append_key(char *p, const char *key) {
1256
1199
  * @see Oj::Doc.open
1257
1200
  */
1258
1201
 
1259
- /* @overload where?() => String
1202
+ /* @overload where() => String
1260
1203
  *
1261
1204
  * Returns a String that describes the absolute path to the current location
1262
1205
  * in the JSON document.
1263
1206
  */
1264
- static VALUE
1265
- doc_where(VALUE self) {
1266
- Doc doc = self_doc(self);
1207
+ static VALUE doc_where(VALUE self) {
1208
+ Doc doc = self_doc(self);
1267
1209
 
1268
1210
  if (0 == *doc->where_path || doc->where == doc->where_path) {
1269
- return oj_slash_string;
1211
+ return oj_slash_string;
1270
1212
  } 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);
1213
+ Leaf * lp;
1214
+ Leaf leaf;
1215
+ size_t size = 3; // leading / and terminating \0
1216
+ char * path;
1217
+ char * p;
1218
+
1219
+ for (lp = doc->where_path; lp <= doc->where; lp++) {
1220
+ leaf = *lp;
1221
+ if (T_HASH == leaf->parent_type) {
1222
+ size += esc_strlen((*lp)->key) + 1;
1223
+ } else if (T_ARRAY == leaf->parent_type) {
1224
+ size += ((*lp)->index < 100) ? 3 : 11;
1225
+ }
1226
+ }
1227
+ path = ALLOCA_N(char, size);
1228
+ p = path;
1229
+ for (lp = doc->where_path; lp <= doc->where; lp++) {
1230
+ leaf = *lp;
1231
+ if (T_HASH == leaf->parent_type) {
1232
+ p = append_key(p, (*lp)->key);
1233
+ } else if (T_ARRAY == leaf->parent_type) {
1234
+ p = ulong_fill(p, (*lp)->index);
1235
+ }
1236
+ *p++ = '/';
1237
+ }
1238
+ *--p = '\0';
1239
+
1240
+ return rb_str_new(path, p - path);
1299
1241
  }
1300
1242
  }
1301
1243
 
1244
+ /* @overload where?() => String
1245
+ * @deprecated
1246
+ * Returns a String that describes the absolute path to the current location
1247
+ * in the JSON document.
1248
+ */
1249
+ static VALUE doc_where_q(VALUE self) {
1250
+ return doc_where(self);
1251
+ }
1252
+
1253
+ /* @overload path() => String
1254
+ *
1255
+ * Returns a String that describes the absolute path to the current location
1256
+ * in the JSON document.
1257
+ */
1258
+ static VALUE doc_path(VALUE self) {
1259
+ return doc_where(self);
1260
+ }
1261
+
1302
1262
  /* @overload local_key() => String, Fixnum, nil
1303
1263
  *
1304
1264
  * Returns the final key to the current location.
1305
1265
  * @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
1266
+ * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() } #=> 2
1267
+ * Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=>
1268
+ * "one" Oj::Doc.open('[1,2,3]') { |doc| doc.local_key() }
1269
+ * #=> nil
1309
1270
  */
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;
1271
+ static VALUE doc_local_key(VALUE self) {
1272
+ Doc doc = self_doc(self);
1273
+ Leaf leaf = *doc->where;
1274
+ volatile VALUE key = Qnil;
1315
1275
 
1316
1276
  if (T_HASH == leaf->parent_type) {
1317
- key = rb_str_new2(leaf->key);
1318
- key = oj_encode(key);
1277
+ key = rb_str_new2(leaf->key);
1278
+ key = oj_encode(key);
1319
1279
  } else if (T_ARRAY == leaf->parent_type) {
1320
- key = LONG2NUM(leaf->index);
1280
+ key = LONG2NUM(leaf->index);
1321
1281
  }
1322
1282
  return key;
1323
1283
  }
@@ -1327,14 +1287,14 @@ doc_local_key(VALUE self) {
1327
1287
  * Moves the document marker or location to the hoot or home position. The
1328
1288
  * same operation can be performed with a Oj::Doc.move('/').
1329
1289
  * @example
1330
- * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? } #=> '/'
1290
+ * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? }
1291
+ * #=> '/'
1331
1292
  */
1332
- static VALUE
1333
- doc_home(VALUE self) {
1334
- Doc doc = self_doc(self);
1293
+ static VALUE doc_home(VALUE self) {
1294
+ Doc doc = self_doc(self);
1335
1295
 
1336
1296
  *doc->where_path = doc->data;
1337
- doc->where = doc->where_path;
1297
+ doc->where = doc->where_path;
1338
1298
 
1339
1299
  return oj_slash_string;
1340
1300
  }
@@ -1350,76 +1310,98 @@ doc_home(VALUE self) {
1350
1310
  * Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
1351
1311
  * Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
1352
1312
  */
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;
1313
+ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
1314
+ Doc doc = self_doc(self);
1315
+ Leaf leaf;
1316
+ const char *path = 0;
1317
+ VALUE type = Qnil;
1359
1318
 
1360
1319
  if (1 <= argc) {
1361
- Check_Type(*argv, T_STRING);
1362
- path = StringValuePtr(*argv);
1320
+ Check_Type(*argv, T_STRING);
1321
+ path = StringValuePtr(*argv);
1363
1322
  }
1364
1323
  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;
1324
+ switch (leaf->rtype) {
1325
+ case T_NIL: type = rb_cNilClass; break;
1326
+ case T_TRUE: type = rb_cTrueClass; break;
1327
+ case T_FALSE: type = rb_cFalseClass; break;
1328
+ case T_STRING: type = rb_cString; break;
1370
1329
  #ifdef RUBY_INTEGER_UNIFICATION
1371
- case T_FIXNUM: type = rb_cInteger; break;
1330
+ case T_FIXNUM: type = rb_cInteger; break;
1372
1331
  #else
1373
- case T_FIXNUM: type = rb_cFixnum; break;
1332
+ case T_FIXNUM: type = rb_cFixnum; break;
1374
1333
  #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
- }
1334
+ case T_FLOAT: type = rb_cFloat; break;
1335
+ case T_ARRAY: type = rb_cArray; break;
1336
+ case T_HASH: type = rb_cHash; break;
1337
+ default: break;
1338
+ }
1380
1339
  }
1381
1340
  return type;
1382
1341
  }
1383
1342
 
1384
- /* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array, Hash
1343
+ /* @overload fetch(path=nil,default=nil) => nil, true, false, Fixnum, Float, String, Array,
1344
+ * Hash
1385
1345
  *
1386
1346
  * Returns the value at the location identified by the path or the current
1387
1347
  * location if the path is nil or not provided. This method will create and
1388
1348
  * return an Array or Hash if that is the type of Object at the location
1389
1349
  * specified. This is more expensive than navigating to the leaves of the JSON
1390
- * document.
1350
+ * document. If a default is provided that is used if no value if found.
1391
1351
  * @param [String] path path to the location to get the type of if provided
1392
1352
  * @example
1393
1353
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
1394
1354
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
1395
1355
  */
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;
1356
+ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
1357
+ Doc doc;
1358
+ Leaf leaf;
1359
+ volatile VALUE val = Qnil;
1360
+ const char * path = 0;
1402
1361
 
1403
1362
  doc = self_doc(self);
1404
1363
  if (1 <= argc) {
1405
- Check_Type(*argv, T_STRING);
1406
- path = StringValuePtr(*argv);
1407
- if (2 == argc) {
1408
- val = argv[1];
1409
- }
1364
+ Check_Type(*argv, T_STRING);
1365
+ path = StringValuePtr(*argv);
1366
+ if (2 == argc) {
1367
+ val = argv[1];
1368
+ }
1410
1369
  }
1411
1370
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1412
- val = leaf_value(doc, leaf);
1371
+ val = leaf_value(doc, leaf);
1413
1372
  }
1414
1373
  return val;
1415
1374
  }
1416
1375
 
1376
+ /* @overload exists?(path) => true, false
1377
+ *
1378
+ * Returns true if the value at the location identified by the path exists.
1379
+ * @param [String] path path to the location
1380
+ * @example
1381
+ * Oj::Doc.open('[1,2]') { |doc| doc.exists?('/1') } #=> true
1382
+ * Oj::Doc.open('[1,2]') { |doc| doc.exists?('/3') } #=> false
1383
+ */
1384
+ static VALUE doc_exists(VALUE self, VALUE str) {
1385
+ Doc doc;
1386
+ Leaf leaf;
1387
+
1388
+ doc = self_doc(self);
1389
+ Check_Type(str, T_STRING);
1390
+ if (0 != (leaf = get_doc_leaf(doc, StringValuePtr(str)))) {
1391
+ if (NULL != leaf) {
1392
+ return Qtrue;
1393
+ }
1394
+ }
1395
+ return Qfalse;
1396
+ }
1397
+
1417
1398
  /* @overload each_leaf(path=nil) => nil
1418
1399
  *
1419
1400
  * Yields to the provided block for each leaf node with the identified
1420
1401
  * location of the JSON document as the root. The parameter passed to the
1421
1402
  * 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
1403
+ * @param [String] path if provided it identified the top of the branch to
1404
+ * process the leaves of
1423
1405
  * @yieldparam [Doc] Doc at the child location
1424
1406
  * @example
1425
1407
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1429,36 +1411,35 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
1429
1411
  * }
1430
1412
  * #=> ["/1" => 3, "/2/1" => 2, "/2/2" => 1]
1431
1413
  */
1432
- static VALUE
1433
- doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1414
+ static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1434
1415
  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
- }
1416
+ Leaf save_path[MAX_STACK];
1417
+ Doc doc = self_doc(self);
1418
+ const char *path = 0;
1419
+ size_t wlen;
1420
+
1421
+ wlen = doc->where - doc->where_path;
1422
+ if (0 < wlen) {
1423
+ memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1424
+ }
1425
+ if (1 <= argc) {
1426
+ Check_Type(*argv, T_STRING);
1427
+ path = StringValuePtr(*argv);
1428
+ if ('/' == *path) {
1429
+ doc->where = doc->where_path;
1430
+ path++;
1431
+ }
1432
+ if (0 != move_step(doc, path, 1)) {
1433
+ if (0 < wlen) {
1434
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1435
+ }
1436
+ return Qnil;
1437
+ }
1438
+ }
1439
+ each_leaf(doc, self);
1440
+ if (0 < wlen) {
1441
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1442
+ }
1462
1443
  }
1463
1444
  return Qnil;
1464
1445
  }
@@ -1469,22 +1450,22 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1469
1450
  * path or a relative path.
1470
1451
  * @param [String] path path to the location to move to
1471
1452
  * @example
1472
- * Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=> "/one/2"
1453
+ * Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=>
1454
+ * "/one/2"
1473
1455
  */
1474
- static VALUE
1475
- doc_move(VALUE self, VALUE str) {
1476
- Doc doc = self_doc(self);
1477
- const char *path;
1478
- int loc;
1456
+ static VALUE doc_move(VALUE self, VALUE str) {
1457
+ Doc doc = self_doc(self);
1458
+ const char *path;
1459
+ int loc;
1479
1460
 
1480
1461
  Check_Type(str, T_STRING);
1481
1462
  path = StringValuePtr(str);
1482
1463
  if ('/' == *path) {
1483
- doc->where = doc->where_path;
1484
- path++;
1464
+ doc->where = doc->where_path;
1465
+ path++;
1485
1466
  }
1486
1467
  if (0 != (loc = move_step(doc, path, 1))) {
1487
- rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
1468
+ rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
1488
1469
  }
1489
1470
  return Qnil;
1490
1471
  }
@@ -1495,7 +1476,8 @@ doc_move(VALUE self, VALUE str) {
1495
1476
  * identified location of the JSON document as the root. The parameter passed
1496
1477
  * to the block on yield is the Doc instance after moving to the child
1497
1478
  * location.
1498
- * @param [String] path if provided it identified the top of the branch to process the chilren of
1479
+ * @param [String] path if provided it identified the top of the branch to
1480
+ * process the children of
1499
1481
  * @yieldparam [Doc] Doc at the child location
1500
1482
  * @example
1501
1483
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1505,46 +1487,51 @@ doc_move(VALUE self, VALUE str) {
1505
1487
  * }
1506
1488
  * #=> ["/2/1", "/2/2"]
1507
1489
  */
1508
- static VALUE
1509
- doc_each_child(int argc, VALUE *argv, VALUE self) {
1490
+ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1510
1491
  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
- }
1492
+ Leaf save_path[MAX_STACK];
1493
+ Doc doc = self_doc(self);
1494
+ const char *path = 0;
1495
+ size_t wlen;
1496
+ Leaf * where_orig = doc->where;
1497
+
1498
+ wlen = doc->where - doc->where_path;
1499
+ if (0 < wlen) {
1500
+ memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1501
+ }
1502
+ if (1 <= argc) {
1503
+ Check_Type(*argv, T_STRING);
1504
+ path = StringValuePtr(*argv);
1505
+ if ('/' == *path) {
1506
+ doc->where = doc->where_path;
1507
+ path++;
1508
+ }
1509
+ if (0 != move_step(doc, path, 1)) {
1510
+ if (0 < wlen) {
1511
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1512
+ }
1513
+ doc->where = where_orig;
1514
+ return Qnil;
1515
+ }
1516
+ }
1517
+ if (NULL == doc->where || NULL == *doc->where) {
1518
+ return Qnil;
1519
+ }
1520
+ if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
1521
+ Leaf first = (*doc->where)->elements->next;
1522
+ Leaf e = first;
1523
+
1524
+ doc->where++;
1525
+ do {
1526
+ *doc->where = e;
1527
+ rb_yield(self);
1528
+ e = e->next;
1529
+ } while (e != first);
1530
+ }
1531
+ if (0 < wlen) {
1532
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1533
+ }
1534
+ doc->where = where_orig;
1548
1535
  }
1549
1536
  return Qnil;
1550
1537
  }
@@ -1555,7 +1542,8 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1555
1542
  * of the JSON document. The parameter passed to the block on yield is the
1556
1543
  * value of the leaf. Only those leaves below the element specified by the
1557
1544
  * path parameter are processed.
1558
- * @param [String] path if provided it identified the top of the branch to process the leaf values of
1545
+ * @param [String] path if provided it identified the top of the branch to
1546
+ * process the leaf values of
1559
1547
  * @yieldparam [Object] val each leaf value
1560
1548
  * @example
1561
1549
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1564,7 +1552,7 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1564
1552
  * result
1565
1553
  * }
1566
1554
  * #=> [3, 2, 1]
1567
- *
1555
+ *
1568
1556
  * Oj::Doc.open('[3,[2,1]]') { |doc|
1569
1557
  * result = []
1570
1558
  * doc.each_value('/2') { |v| result << v }
@@ -1572,20 +1560,19 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1572
1560
  * }
1573
1561
  * #=> [2, 1]
1574
1562
  */
1575
- static VALUE
1576
- doc_each_value(int argc, VALUE *argv, VALUE self) {
1563
+ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
1577
1564
  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
- }
1565
+ Doc doc = self_doc(self);
1566
+ const char *path = 0;
1567
+ Leaf leaf;
1568
+
1569
+ if (1 <= argc) {
1570
+ Check_Type(*argv, T_STRING);
1571
+ path = StringValuePtr(*argv);
1572
+ }
1573
+ if (0 != (leaf = get_doc_leaf(doc, path))) {
1574
+ each_value(doc, leaf);
1575
+ }
1589
1576
  }
1590
1577
  return Qnil;
1591
1578
  }
@@ -1594,52 +1581,50 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
1594
1581
  *
1595
1582
  * Dumps the document or nodes to a new JSON document. It uses the default
1596
1583
  * 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
1584
+ * @param path [String] if provided it identified the top of the branch to
1585
+ * dump to JSON
1586
+ * @param filename [String] if provided it is the filename to write the output
1587
+ * to
1599
1588
  * @example
1600
1589
  * Oj::Doc.open('[3,[2,1]]') { |doc|
1601
1590
  * doc.dump('/2')
1602
1591
  * }
1603
1592
  * #=> "[2,1]"
1604
1593
  */
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;
1594
+ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
1595
+ Doc doc = self_doc(self);
1596
+ Leaf leaf;
1597
+ const char *path = 0;
1598
+ const char *filename = 0;
1611
1599
 
1612
1600
  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
- }
1601
+ if (Qnil != *argv) {
1602
+ Check_Type(*argv, T_STRING);
1603
+ path = StringValuePtr(*argv);
1604
+ }
1605
+ if (2 <= argc) {
1606
+ Check_Type(argv[1], T_STRING);
1607
+ filename = StringValuePtr(argv[1]);
1608
+ }
1621
1609
  }
1622
1610
  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;
1611
+ volatile VALUE rjson;
1612
+
1613
+ if (0 == filename) {
1614
+ struct _out out;
1615
+
1616
+ oj_out_init(&out);
1617
+
1618
+ out.omit_nil = oj_default_options.dump_opts.omit_nil;
1619
+ oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
1620
+ rjson = rb_str_new2(out.buf);
1621
+
1622
+ oj_out_free(&out);
1623
+ } else {
1624
+ oj_write_leaf_to_file(leaf, filename, &oj_default_options);
1625
+ rjson = Qnil;
1626
+ }
1627
+ return rjson;
1643
1628
  }
1644
1629
  return Qnil;
1645
1630
  }
@@ -1652,8 +1637,7 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
1652
1637
  * @example
1653
1638
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1654
1639
  */
1655
- static VALUE
1656
- doc_size(VALUE self) {
1640
+ static VALUE doc_size(VALUE self) {
1657
1641
  return ULONG2NUM(((Doc)DATA_PTR(self))->size);
1658
1642
  }
1659
1643
 
@@ -1666,16 +1650,15 @@ doc_size(VALUE self) {
1666
1650
  * doc.size() #=> 4
1667
1651
  * doc.close()
1668
1652
  */
1669
- static VALUE
1670
- doc_close(VALUE self) {
1671
- Doc doc = self_doc(self);
1653
+ static VALUE doc_close(VALUE self) {
1654
+ Doc doc = self_doc(self);
1672
1655
 
1673
1656
  rb_gc_unregister_address(&doc->self);
1674
1657
  DATA_PTR(doc->self) = 0;
1675
1658
  if (0 != doc) {
1676
- xfree(doc->json);
1677
- doc_free(doc);
1678
- xfree(doc);
1659
+ xfree(doc->json);
1660
+ doc_free(doc);
1661
+ xfree(doc);
1679
1662
  }
1680
1663
  return Qnil;
1681
1664
  }
@@ -1684,8 +1667,7 @@ doc_close(VALUE self) {
1684
1667
  Oj = rb_define_module("Oj");
1685
1668
  #endif
1686
1669
 
1687
- static VALUE
1688
- doc_not_implemented(VALUE self) {
1670
+ static VALUE doc_not_implemented(VALUE self) {
1689
1671
  rb_raise(rb_eNotImpError, "Not implemented.");
1690
1672
  return Qnil;
1691
1673
  }
@@ -1697,21 +1679,21 @@ doc_not_implemented(VALUE self) {
1697
1679
  * extracted. Once the document is closed the document can not longer be
1698
1680
  * accessed. This allows the parsing and data extraction to be extremely fast
1699
1681
  * compared to other JSON parses.
1700
- *
1682
+ *
1701
1683
  * An Oj::Doc class is not created directly but the _open()_ class method is
1702
1684
  * used to open a document and the yield parameter to the block of the #open()
1703
1685
  * call is the Doc instance. The Doc instance can be moved across, up, and
1704
1686
  * down the JSON document. At each element the data associated with the
1705
1687
  * element can be extracted. It is also possible to just provide a path to the
1706
1688
  * data to be extracted and retrieve the data in that manner.
1707
- *
1689
+ *
1708
1690
  * For many of the methods a path is used to describe the location of an
1709
1691
  * element. Paths follow a subset of the XPath syntax. The slash ('/')
1710
1692
  * character is the separator. Each step in the path identifies the next
1711
1693
  * branch to take through the document. A JSON object will expect a key string
1712
1694
  * while an array will expect a positive index. A .. step indicates a move up
1713
1695
  * the JSON document.
1714
- *
1696
+ *
1715
1697
  * @example
1716
1698
  * json = %{[
1717
1699
  * {
@@ -1725,28 +1707,29 @@ doc_not_implemented(VALUE self) {
1725
1707
  * ]}
1726
1708
  * # move and get value
1727
1709
  * 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
1710
+ * doc.move('/1/two')
1711
+ * # doc location is now at the 'two' element of the hash that is the first
1712
+ * element of the array. doc.fetch() end
1732
1713
  * #=> 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()
1714
+ *
1715
+ * # Now try again using a path to Oj::Doc.fetch() directly and not using a
1716
+ * block. doc = Oj::Doc.open(json) doc.fetch('/2/three') #=> 3 doc.close()
1738
1717
  */
1739
- void
1740
- oj_init_doc() {
1718
+ void oj_init_doc(void) {
1741
1719
  oj_doc_class = rb_define_class_under(Oj, "Doc", rb_cObject);
1720
+ rb_gc_register_address(&oj_doc_class);
1721
+ rb_undef_alloc_func(oj_doc_class);
1742
1722
  rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
1743
1723
  rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
1744
1724
  rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
1745
- rb_define_method(oj_doc_class, "where?", doc_where, 0);
1725
+ rb_define_method(oj_doc_class, "where?", doc_where_q, 0);
1726
+ rb_define_method(oj_doc_class, "where", doc_where, 0);
1727
+ rb_define_method(oj_doc_class, "path", doc_path, 0);
1746
1728
  rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
1747
1729
  rb_define_method(oj_doc_class, "home", doc_home, 0);
1748
1730
  rb_define_method(oj_doc_class, "type", doc_type, -1);
1749
1731
  rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
1732
+ rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
1750
1733
  rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
1751
1734
  rb_define_method(oj_doc_class, "move", doc_move, 1);
1752
1735
  rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);