oj 2.18.3 → 3.13.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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);