oj 2.18.3 → 3.13.14

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