oj 3.7.4 → 3.13.21

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