oj 3.7.4 → 3.13.23

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