oj 3.9.1 → 3.16.11

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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1452 -0
  3. data/README.md +21 -6
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +50 -68
  6. data/ext/oj/cache.c +329 -0
  7. data/ext/oj/cache.h +22 -0
  8. data/ext/oj/cache8.c +60 -62
  9. data/ext/oj/cache8.h +9 -36
  10. data/ext/oj/circarray.c +38 -42
  11. data/ext/oj/circarray.h +12 -13
  12. data/ext/oj/code.c +158 -179
  13. data/ext/oj/code.h +20 -22
  14. data/ext/oj/compat.c +145 -205
  15. data/ext/oj/custom.c +740 -880
  16. data/ext/oj/debug.c +126 -0
  17. data/ext/oj/dump.c +1145 -844
  18. data/ext/oj/dump.h +71 -57
  19. data/ext/oj/dump_compat.c +575 -655
  20. data/ext/oj/dump_leaf.c +96 -186
  21. data/ext/oj/dump_object.c +533 -660
  22. data/ext/oj/dump_strict.c +306 -340
  23. data/ext/oj/encode.h +4 -33
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +28 -28
  26. data/ext/oj/err.h +39 -42
  27. data/ext/oj/extconf.rb +28 -7
  28. data/ext/oj/fast.c +1052 -1113
  29. data/ext/oj/intern.c +313 -0
  30. data/ext/oj/intern.h +22 -0
  31. data/ext/oj/mem.c +318 -0
  32. data/ext/oj/mem.h +53 -0
  33. data/ext/oj/mimic_json.c +471 -430
  34. data/ext/oj/object.c +532 -580
  35. data/ext/oj/odd.c +156 -142
  36. data/ext/oj/odd.h +25 -26
  37. data/ext/oj/oj.c +1346 -961
  38. data/ext/oj/oj.h +307 -290
  39. data/ext/oj/parse.c +954 -858
  40. data/ext/oj/parse.h +74 -72
  41. data/ext/oj/parser.c +1600 -0
  42. data/ext/oj/parser.h +103 -0
  43. data/ext/oj/rails.c +819 -836
  44. data/ext/oj/rails.h +8 -11
  45. data/ext/oj/reader.c +136 -147
  46. data/ext/oj/reader.h +69 -83
  47. data/ext/oj/resolve.c +41 -63
  48. data/ext/oj/resolve.h +4 -6
  49. data/ext/oj/rxclass.c +69 -72
  50. data/ext/oj/rxclass.h +12 -13
  51. data/ext/oj/saj.c +440 -485
  52. data/ext/oj/saj2.c +584 -0
  53. data/ext/oj/saj2.h +23 -0
  54. data/ext/oj/scp.c +79 -118
  55. data/ext/oj/simd.h +10 -0
  56. data/ext/oj/sparse.c +739 -709
  57. data/ext/oj/stream_writer.c +141 -175
  58. data/ext/oj/strict.c +103 -128
  59. data/ext/oj/string_writer.c +244 -261
  60. data/ext/oj/trace.c +34 -41
  61. data/ext/oj/trace.h +42 -15
  62. data/ext/oj/usual.c +1218 -0
  63. data/ext/oj/usual.h +69 -0
  64. data/ext/oj/util.c +107 -107
  65. data/ext/oj/util.h +4 -3
  66. data/ext/oj/val_stack.c +61 -78
  67. data/ext/oj/val_stack.h +80 -114
  68. data/ext/oj/validate.c +46 -0
  69. data/ext/oj/wab.c +316 -361
  70. data/lib/oj/active_support_helper.rb +1 -3
  71. data/lib/oj/bag.rb +8 -1
  72. data/lib/oj/easy_hash.rb +9 -9
  73. data/lib/oj/error.rb +1 -2
  74. data/lib/oj/json.rb +162 -150
  75. data/lib/oj/mimic.rb +54 -20
  76. data/lib/oj/saj.rb +20 -6
  77. data/lib/oj/schandler.rb +5 -4
  78. data/lib/oj/state.rb +12 -8
  79. data/lib/oj/version.rb +1 -2
  80. data/lib/oj.rb +2 -8
  81. data/pages/Compatibility.md +1 -1
  82. data/pages/Encoding.md +1 -1
  83. data/pages/InstallOptions.md +20 -0
  84. data/pages/JsonGem.md +15 -0
  85. data/pages/Modes.md +9 -3
  86. data/pages/Options.md +62 -12
  87. data/pages/Parser.md +309 -0
  88. data/pages/Rails.md +73 -22
  89. metadata +68 -192
  90. data/ext/oj/hash.c +0 -163
  91. data/ext/oj/hash.h +0 -46
  92. data/ext/oj/hash_test.c +0 -512
  93. data/test/_test_active.rb +0 -76
  94. data/test/_test_active_mimic.rb +0 -96
  95. data/test/_test_mimic_rails.rb +0 -126
  96. data/test/activerecord/result_test.rb +0 -27
  97. data/test/activesupport4/decoding_test.rb +0 -108
  98. data/test/activesupport4/encoding_test.rb +0 -531
  99. data/test/activesupport4/test_helper.rb +0 -41
  100. data/test/activesupport5/decoding_test.rb +0 -125
  101. data/test/activesupport5/encoding_test.rb +0 -485
  102. data/test/activesupport5/encoding_test_cases.rb +0 -90
  103. data/test/activesupport5/test_helper.rb +0 -50
  104. data/test/activesupport5/time_zone_test_helpers.rb +0 -24
  105. data/test/bar.rb +0 -25
  106. data/test/files.rb +0 -29
  107. data/test/foo.rb +0 -21
  108. data/test/helper.rb +0 -26
  109. data/test/isolated/shared.rb +0 -308
  110. data/test/isolated/test_mimic_after.rb +0 -13
  111. data/test/isolated/test_mimic_alone.rb +0 -12
  112. data/test/isolated/test_mimic_as_json.rb +0 -45
  113. data/test/isolated/test_mimic_before.rb +0 -13
  114. data/test/isolated/test_mimic_define.rb +0 -28
  115. data/test/isolated/test_mimic_rails_after.rb +0 -22
  116. data/test/isolated/test_mimic_rails_before.rb +0 -21
  117. data/test/isolated/test_mimic_redefine.rb +0 -15
  118. data/test/json_gem/json_addition_test.rb +0 -216
  119. data/test/json_gem/json_common_interface_test.rb +0 -148
  120. data/test/json_gem/json_encoding_test.rb +0 -107
  121. data/test/json_gem/json_ext_parser_test.rb +0 -20
  122. data/test/json_gem/json_fixtures_test.rb +0 -35
  123. data/test/json_gem/json_generator_test.rb +0 -383
  124. data/test/json_gem/json_generic_object_test.rb +0 -90
  125. data/test/json_gem/json_parser_test.rb +0 -470
  126. data/test/json_gem/json_string_matching_test.rb +0 -42
  127. data/test/json_gem/test_helper.rb +0 -18
  128. data/test/perf.rb +0 -107
  129. data/test/perf_compat.rb +0 -130
  130. data/test/perf_fast.rb +0 -164
  131. data/test/perf_file.rb +0 -64
  132. data/test/perf_object.rb +0 -138
  133. data/test/perf_saj.rb +0 -109
  134. data/test/perf_scp.rb +0 -151
  135. data/test/perf_simple.rb +0 -287
  136. data/test/perf_strict.rb +0 -145
  137. data/test/perf_wab.rb +0 -131
  138. data/test/sample/change.rb +0 -14
  139. data/test/sample/dir.rb +0 -19
  140. data/test/sample/doc.rb +0 -36
  141. data/test/sample/file.rb +0 -48
  142. data/test/sample/group.rb +0 -16
  143. data/test/sample/hasprops.rb +0 -16
  144. data/test/sample/layer.rb +0 -12
  145. data/test/sample/line.rb +0 -20
  146. data/test/sample/oval.rb +0 -10
  147. data/test/sample/rect.rb +0 -10
  148. data/test/sample/shape.rb +0 -35
  149. data/test/sample/text.rb +0 -20
  150. data/test/sample.rb +0 -54
  151. data/test/sample_json.rb +0 -37
  152. data/test/test_compat.rb +0 -509
  153. data/test/test_custom.rb +0 -503
  154. data/test/test_debian.rb +0 -53
  155. data/test/test_fast.rb +0 -470
  156. data/test/test_file.rb +0 -239
  157. data/test/test_gc.rb +0 -49
  158. data/test/test_hash.rb +0 -29
  159. data/test/test_integer_range.rb +0 -73
  160. data/test/test_null.rb +0 -376
  161. data/test/test_object.rb +0 -1018
  162. data/test/test_saj.rb +0 -186
  163. data/test/test_scp.rb +0 -433
  164. data/test/test_strict.rb +0 -410
  165. data/test/test_various.rb +0 -741
  166. data/test/test_wab.rb +0 -307
  167. data/test/test_writer.rb +0 -380
  168. data/test/tests.rb +0 -24
  169. data/test/tests_mimic.rb +0 -14
  170. data/test/tests_mimic_addition.rb +0 -7
  171. data/test/zoo.rb +0 -13
data/ext/oj/fast.c CHANGED
@@ -1,216 +1,170 @@
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"
14
+ #include "dump.h"
41
15
  #include "encode.h"
16
+ #include "mem.h"
17
+ #include "oj.h"
42
18
 
43
19
  // 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
20
+ #define SMALL_JSON 65536
21
+ #define MAX_STACK 100
22
+ // #define BATCH_SIZE (4096 / sizeof(struct _leaf) - 1)
23
+ #define BATCH_SIZE 100
24
+
25
+ // Support for compaction
26
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
27
+ #define mark rb_gc_mark_movable
28
+ #else
29
+ #define mark rb_gc_mark
30
+ #endif
48
31
 
49
32
  typedef struct _batch {
50
- struct _batch *next;
51
- int next_avail;
52
- struct _leaf leaves[BATCH_SIZE];
33
+ struct _batch *next;
34
+ int next_avail;
35
+ struct _leaf leaves[BATCH_SIZE];
53
36
  } *Batch;
54
37
 
55
38
  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;
39
+ Leaf data;
40
+ Leaf *where; // points to current location
41
+ Leaf where_path[MAX_STACK]; // points to head of path
42
+ char *json;
43
+ unsigned long size; // number of leaves/branches in the doc
44
+ VALUE self;
45
+ Batch batches;
46
+ struct _batch batch0;
64
47
  } *Doc;
65
48
 
66
49
  typedef struct _parseInfo {
67
- char *str; /* buffer being read from */
68
- char *s; /* current position in buffer */
69
- Doc doc;
70
- void *stack_min;
50
+ char *str; // buffer being read from
51
+ char *s; // current position in buffer
52
+ Doc doc;
53
+ void *stack_min;
71
54
  } *ParseInfo;
72
55
 
73
- static void leaf_init(Leaf leaf, int type);
74
- static Leaf leaf_new(Doc doc, int type);
75
- static void leaf_append_element(Leaf parent, Leaf element);
76
- static VALUE leaf_value(Doc doc, Leaf leaf);
77
- static void leaf_fixnum_value(Leaf leaf);
78
- static void leaf_float_value(Leaf leaf);
79
- static VALUE leaf_array_value(Doc doc, Leaf leaf);
80
- static VALUE leaf_hash_value(Doc doc, Leaf leaf);
81
-
82
- static Leaf read_next(ParseInfo pi);
83
- static Leaf read_obj(ParseInfo pi);
84
- static Leaf read_array(ParseInfo pi);
85
- static Leaf read_str(ParseInfo pi);
86
- static Leaf read_num(ParseInfo pi);
87
- static Leaf read_true(ParseInfo pi);
88
- static Leaf read_false(ParseInfo pi);
89
- static Leaf read_nil(ParseInfo pi);
90
- static void next_non_white(ParseInfo pi);
91
- static char* read_quoted_value(ParseInfo pi);
92
- static void skip_comment(ParseInfo pi);
93
-
94
- static VALUE protect_open_proc(VALUE x);
95
- static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated);
96
- static void each_leaf(Doc doc, VALUE self);
97
- static int move_step(Doc doc, const char *path, int loc);
98
- static Leaf get_doc_leaf(Doc doc, const char *path);
99
- static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
100
- static void each_value(Doc doc, Leaf leaf);
101
-
102
- static void doc_init(Doc doc);
103
- static void doc_free(Doc doc);
104
- static VALUE doc_open(VALUE clas, VALUE str);
105
- static VALUE doc_open_file(VALUE clas, VALUE filename);
106
- static VALUE doc_where(VALUE self);
107
- static VALUE doc_local_key(VALUE self);
108
- static VALUE doc_home(VALUE self);
109
- static VALUE doc_type(int argc, VALUE *argv, VALUE self);
110
- static VALUE doc_fetch(int argc, VALUE *argv, VALUE self);
111
- static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self);
112
- static VALUE doc_move(VALUE self, VALUE str);
113
- static VALUE doc_each_child(int argc, VALUE *argv, VALUE self);
114
- static VALUE doc_each_value(int argc, VALUE *argv, VALUE self);
115
- static VALUE doc_dump(int argc, VALUE *argv, VALUE self);
116
- static VALUE doc_size(VALUE self);
117
-
118
- VALUE oj_doc_class = Qundef;
56
+ static void leaf_init(Leaf leaf, int type);
57
+ static Leaf leaf_new(Doc doc, int type);
58
+ static void leaf_append_element(Leaf parent, Leaf element);
59
+ static VALUE leaf_value(Doc doc, Leaf leaf);
60
+ static void leaf_fixnum_value(Leaf leaf);
61
+ static void leaf_float_value(Leaf leaf);
62
+ static VALUE leaf_array_value(Doc doc, Leaf leaf);
63
+ static VALUE leaf_hash_value(Doc doc, Leaf leaf);
64
+
65
+ static Leaf read_next(ParseInfo pi);
66
+ static Leaf read_obj(ParseInfo pi);
67
+ static Leaf read_array(ParseInfo pi);
68
+ static Leaf read_str(ParseInfo pi);
69
+ static Leaf read_num(ParseInfo pi);
70
+ static Leaf read_true(ParseInfo pi);
71
+ static Leaf read_false(ParseInfo pi);
72
+ static Leaf read_nil(ParseInfo pi);
73
+ static void next_non_white(ParseInfo pi);
74
+ static char *read_quoted_value(ParseInfo pi);
75
+ static void skip_comment(ParseInfo pi);
76
+
77
+ static VALUE protect_open_proc(VALUE x);
78
+ static VALUE parse_json(VALUE clas, char *json, bool given);
79
+ static void each_leaf(Doc doc, VALUE self);
80
+ static int move_step(Doc doc, const char *path, int loc);
81
+ static Leaf get_doc_leaf(Doc doc, const char *path);
82
+ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
83
+ static void each_value(Doc doc, Leaf leaf);
84
+
85
+ VALUE oj_doc_class = Qundef;
119
86
 
120
87
  // This is only for CentOS 5.4 with Ruby 1.9.3-p0.
121
88
  #ifndef HAVE_STPCPY
122
89
  char *stpcpy(char *dest, const char *src) {
123
- size_t cnt = strlen(src);
124
-
90
+ size_t cnt = strlen(src);
91
+
125
92
  strcpy(dest, src);
126
93
 
127
94
  return dest + cnt;
128
95
  }
129
96
  #endif
130
97
 
131
- inline static void
132
- next_non_white(ParseInfo pi) {
98
+ inline static void next_non_white(ParseInfo pi) {
133
99
  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
- }
100
+ switch (*pi->s) {
101
+ case ' ':
102
+ case '\t':
103
+ case '\f':
104
+ case '\n':
105
+ case '\r': break;
106
+ case '/': skip_comment(pi); break;
107
+ default: return;
108
+ }
147
109
  }
148
110
  }
149
111
 
150
- inline static char*
151
- ulong_fill(char *s, size_t num) {
152
- char buf[32];
153
- char *b = buf + sizeof(buf) - 1;
112
+ inline static char *ulong_fill(char *s, size_t num) {
113
+ char buf[32];
114
+ char *b = buf + sizeof(buf) - 1;
154
115
 
155
116
  *b-- = '\0';
156
- for (; 0 < num; num /= 10, b--) {
157
- *b = (num % 10) + '0';
158
- }
159
- b++;
117
+ b = oj_longlong_to_string((long long)num, false, b);
160
118
  if ('\0' == *b) {
161
- b--;
162
- *b = '0';
119
+ b--;
120
+ *b = '0';
163
121
  }
164
122
  for (; '\0' != *b; b++, s++) {
165
- *s = *b;
123
+ *s = *b;
166
124
  }
167
125
  return s;
168
126
  }
169
127
 
170
- inline static void
171
- leaf_init(Leaf leaf, int type) {
172
- leaf->next = 0;
173
- leaf->rtype = type;
128
+ inline static void leaf_init(Leaf leaf, int type) {
129
+ leaf->next = 0;
130
+ leaf->rtype = type;
174
131
  leaf->parent_type = T_NONE;
175
132
  switch (type) {
176
133
  case T_ARRAY:
177
134
  case T_HASH:
178
- leaf->elements = 0;
179
- leaf->value_type = COL_VAL;
180
- break;
135
+ leaf->elements = 0;
136
+ leaf->value_type = COL_VAL;
137
+ break;
181
138
  case T_NIL:
182
- leaf->value = Qnil;
183
- leaf->value_type = RUBY_VAL;
184
- break;
139
+ leaf->value = Qnil;
140
+ leaf->value_type = RUBY_VAL;
141
+ break;
185
142
  case T_TRUE:
186
- leaf->value = Qtrue;
187
- leaf->value_type = RUBY_VAL;
188
- break;
143
+ leaf->value = Qtrue;
144
+ leaf->value_type = RUBY_VAL;
145
+ break;
189
146
  case T_FALSE:
190
- leaf->value = Qfalse;
191
- leaf->value_type = RUBY_VAL;
192
- break;
147
+ leaf->value = Qfalse;
148
+ leaf->value_type = RUBY_VAL;
149
+ break;
193
150
  case T_FIXNUM:
194
151
  case T_FLOAT:
195
152
  case T_STRING:
196
- default:
197
- leaf->value_type = STR_VAL;
198
- break;
153
+ default: leaf->value_type = STR_VAL; break;
199
154
  }
200
155
  }
201
156
 
202
- inline static Leaf
203
- leaf_new(Doc doc, int type) {
204
- Leaf leaf;
157
+ inline static Leaf leaf_new(Doc doc, int type) {
158
+ Leaf leaf;
205
159
 
206
160
  if (0 == doc->batches || BATCH_SIZE == doc->batches->next_avail) {
207
- Batch b = ALLOC(struct _batch);
161
+ Batch b = OJ_R_ALLOC(struct _batch);
208
162
 
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;
163
+ // Initializes all leaves with a NO_VAL value_type
164
+ memset(b, 0, sizeof(struct _batch));
165
+ b->next = doc->batches;
166
+ doc->batches = b;
167
+ b->next_avail = 0;
214
168
  }
215
169
  leaf = &doc->batches->leaves[doc->batches->next_avail];
216
170
  doc->batches->next_avail++;
@@ -219,93 +173,70 @@ leaf_new(Doc doc, int type) {
219
173
  return leaf;
220
174
  }
221
175
 
222
- inline static void
223
- leaf_append_element(Leaf parent, Leaf element) {
176
+ inline static void leaf_append_element(Leaf parent, Leaf element) {
224
177
  if (0 == parent->elements) {
225
- parent->elements = element;
226
- element->next = element;
178
+ parent->elements = element;
179
+ element->next = element;
227
180
  } else {
228
- element->next = parent->elements->next;
229
- parent->elements->next = element;
230
- parent->elements = element;
181
+ element->next = parent->elements->next;
182
+ parent->elements->next = element;
183
+ parent->elements = element;
231
184
  }
232
185
  }
233
186
 
234
- static VALUE
235
- leaf_value(Doc doc, Leaf leaf) {
187
+ static VALUE leaf_value(Doc doc, Leaf leaf) {
236
188
  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
- }
189
+ switch (leaf->rtype) {
190
+ case T_NIL: leaf->value = Qnil; break;
191
+ case T_TRUE: leaf->value = Qtrue; break;
192
+ case T_FALSE: leaf->value = Qfalse; break;
193
+ case T_FIXNUM: leaf_fixnum_value(leaf); break;
194
+ case T_FLOAT: leaf_float_value(leaf); break;
195
+ case T_STRING:
196
+ leaf->value = rb_utf8_str_new_cstr(leaf->str);
197
+ leaf->value_type = RUBY_VAL;
198
+ break;
199
+ case T_ARRAY: return leaf_array_value(doc, leaf); break;
200
+ case T_HASH: return leaf_hash_value(doc, leaf); break;
201
+ default: rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype); break;
202
+ }
268
203
  }
269
204
  return leaf->value;
270
205
  }
271
206
 
272
- inline static Doc
273
- self_doc(VALUE self) {
274
- Doc doc = DATA_PTR(self);
207
+ inline static Doc self_doc(VALUE self) {
208
+ Doc doc = DATA_PTR(self);
275
209
 
276
210
  if (0 == doc) {
277
- rb_raise(rb_eIOError, "Document already closed or not open.");
211
+ rb_raise(rb_eIOError, "Document already closed or not open.");
278
212
  }
279
213
  return doc;
280
214
  }
281
215
 
282
- static void
283
- skip_comment(ParseInfo pi) {
284
- pi->s++; // skip first /
216
+ static void skip_comment(ParseInfo pi) {
217
+ pi->s++; // skip first /
285
218
  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
- }
219
+ pi->s++;
220
+ for (; '\0' != *pi->s; pi->s++) {
221
+ if ('*' == *pi->s && '/' == *(pi->s + 1)) {
222
+ pi->s++;
223
+ return;
224
+ } else if ('\0' == *pi->s) {
225
+ raise_error("comment not terminated", pi->str, pi->s);
226
+ }
227
+ }
295
228
  } 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
- }
229
+ for (; 1; pi->s++) {
230
+ switch (*pi->s) {
231
+ case '\n':
232
+ case '\r':
233
+ case '\f':
234
+ case '\0': return;
235
+ default: break;
236
+ }
237
+ }
307
238
  } else {
308
- raise_error("invalid comment", pi->str, pi->s);
239
+ raise_error("invalid comment", pi->str, pi->s);
309
240
  }
310
241
  }
311
242
 
@@ -315,100 +246,87 @@ skip_comment(ParseInfo pi) {
315
246
  #define NUM_MAX (FIXNUM_MAX >> 8)
316
247
  #endif
317
248
 
249
+ static void leaf_fixnum_value(Leaf leaf) {
250
+ char *s = leaf->str;
251
+ int64_t n = 0;
252
+ int neg = 0;
253
+ int big = 0;
318
254
 
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
255
  if ('-' == *s) {
327
- s++;
328
- neg = 1;
256
+ s++;
257
+ neg = 1;
329
258
  } else if ('+' == *s) {
330
- s++;
259
+ s++;
331
260
  }
332
261
  for (; '0' <= *s && *s <= '9'; s++) {
333
- n = n * 10 + (*s - '0');
334
- if (NUM_MAX <= n) {
335
- big = 1;
336
- }
262
+ n = n * 10 + (*s - '0');
263
+ if (NUM_MAX <= n) {
264
+ big = 1;
265
+ }
337
266
  }
338
267
  if (big) {
339
- char c = *s;
340
-
341
- *s = '\0';
342
- leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
343
- *s = c;
268
+ char c = *s;
269
+
270
+ *s = '\0';
271
+ leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
272
+ *s = c;
344
273
  } else {
345
- if (neg) {
346
- n = -n;
347
- }
348
- leaf->value = rb_ll2inum(n);
274
+ if (neg) {
275
+ n = -n;
276
+ }
277
+ leaf->value = rb_ll2inum(n);
349
278
  }
350
279
  leaf->value_type = RUBY_VAL;
351
280
  }
352
281
 
353
- static void
354
- leaf_float_value(Leaf leaf) {
355
- leaf->value = rb_float_new(rb_cstr_to_dbl(leaf->str, 1));
282
+ static void leaf_float_value(Leaf leaf) {
283
+ leaf->value = rb_float_new(rb_cstr_to_dbl(leaf->str, 1));
356
284
  leaf->value_type = RUBY_VAL;
357
285
  }
358
286
 
359
- static VALUE
360
- leaf_array_value(Doc doc, Leaf leaf) {
361
- volatile VALUE a = rb_ary_new();
287
+ static VALUE leaf_array_value(Doc doc, Leaf leaf) {
288
+ volatile VALUE a = rb_ary_new();
362
289
 
363
290
  if (0 != leaf->elements) {
364
- Leaf first = leaf->elements->next;
365
- Leaf e = first;
291
+ Leaf first = leaf->elements->next;
292
+ Leaf e = first;
366
293
 
367
- do {
368
- rb_ary_push(a, leaf_value(doc, e));
369
- e = e->next;
370
- } while (e != first);
294
+ do {
295
+ rb_ary_push(a, leaf_value(doc, e));
296
+ e = e->next;
297
+ } while (e != first);
371
298
  }
372
299
  return a;
373
300
  }
374
301
 
375
- static VALUE
376
- leaf_hash_value(Doc doc, Leaf leaf) {
377
- volatile VALUE h = rb_hash_new();
302
+ static VALUE leaf_hash_value(Doc doc, Leaf leaf) {
303
+ volatile VALUE h = rb_hash_new();
378
304
 
379
305
  if (0 != leaf->elements) {
380
- Leaf first = leaf->elements->next;
381
- Leaf e = first;
382
- volatile VALUE key;
306
+ Leaf first = leaf->elements->next;
307
+ Leaf e = first;
308
+ volatile VALUE key;
383
309
 
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);
310
+ do {
311
+ key = rb_utf8_str_new_cstr(e->key);
312
+ rb_hash_aset(h, key, leaf_value(doc, e));
313
+ e = e->next;
314
+ } while (e != first);
390
315
  }
391
316
  return h;
392
317
  }
393
318
 
394
- static Leaf
395
- read_next(ParseInfo pi) {
396
- Leaf leaf = 0;
319
+ static Leaf read_next(ParseInfo pi) {
320
+ Leaf leaf = 0;
397
321
 
398
- if ((void*)&leaf < pi->stack_min) {
399
- rb_raise(rb_eSysStackError, "JSON is too deeply nested");
322
+ if ((void *)&leaf < pi->stack_min) {
323
+ rb_raise(rb_eSysStackError, "JSON is too deeply nested");
400
324
  }
401
- next_non_white(pi); // skip white space
325
+ next_non_white(pi); // skip white space
402
326
  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;
327
+ case '{': leaf = read_obj(pi); break;
328
+ case '[': leaf = read_array(pi); break;
329
+ case '"': leaf = read_str(pi); break;
412
330
  case '+':
413
331
  case '-':
414
332
  case '0':
@@ -420,716 +338,724 @@ read_next(ParseInfo pi) {
420
338
  case '6':
421
339
  case '7':
422
340
  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;
341
+ case '9': leaf = read_num(pi); break;
342
+ case 't': leaf = read_true(pi); break;
343
+ case 'f': leaf = read_false(pi); break;
344
+ case 'n': leaf = read_nil(pi); break;
435
345
  case '\0':
436
- default:
437
- break; // returns 0
346
+ default: break; // returns 0
438
347
  }
439
348
  pi->doc->size++;
440
349
 
441
350
  return leaf;
442
351
  }
443
352
 
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;
353
+ static Leaf read_obj(ParseInfo pi) {
354
+ Leaf h = leaf_new(pi->doc, T_HASH);
355
+ char *end;
356
+ const char *key = 0;
357
+ Leaf val = 0;
450
358
 
451
359
  pi->s++;
452
360
  next_non_white(pi);
453
361
  if ('}' == *pi->s) {
454
- pi->s++;
455
- return h;
362
+ pi->s++;
363
+ return h;
456
364
  }
457
365
  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';
366
+ next_non_white(pi);
367
+ key = 0;
368
+ val = 0;
369
+ if ('"' != *pi->s || 0 == (key = read_quoted_value(pi))) {
370
+ raise_error("unexpected character", pi->str, pi->s);
371
+ }
372
+ next_non_white(pi);
373
+ if (':' == *pi->s) {
374
+ pi->s++;
375
+ } else {
376
+ raise_error("invalid format, expected :", pi->str, pi->s);
377
+ }
378
+ if (0 == (val = read_next(pi))) {
379
+ // printf("*** '%s'\n", pi->s);
380
+ raise_error("unexpected character", pi->str, pi->s);
381
+ }
382
+ end = pi->s;
383
+ val->key = key;
384
+ val->parent_type = T_HASH;
385
+ leaf_append_element(h, val);
386
+ next_non_white(pi);
387
+ if ('}' == *pi->s) {
388
+ pi->s++;
389
+ *end = '\0';
390
+ break;
391
+ } else if (',' == *pi->s) {
392
+ pi->s++;
393
+ } else {
394
+ // printf("*** '%s'\n", pi->s);
395
+ raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
396
+ }
397
+ *end = '\0';
490
398
  }
491
399
  return h;
492
400
  }
493
401
 
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;
402
+ static Leaf read_array(ParseInfo pi) {
403
+ Leaf a = leaf_new(pi->doc, T_ARRAY);
404
+ Leaf e;
405
+ char *end;
406
+ int cnt = 0;
500
407
 
501
408
  pi->s++;
502
409
  next_non_white(pi);
503
410
  if (']' == *pi->s) {
504
- pi->s++;
505
- return a;
411
+ pi->s++;
412
+ return a;
506
413
  }
507
414
  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';
415
+ next_non_white(pi);
416
+ if (0 == (e = read_next(pi))) {
417
+ raise_error("unexpected character", pi->str, pi->s);
418
+ }
419
+ cnt++;
420
+ e->index = cnt;
421
+ e->parent_type = T_ARRAY;
422
+ leaf_append_element(a, e);
423
+ end = pi->s;
424
+ next_non_white(pi);
425
+ if (',' == *pi->s) {
426
+ pi->s++;
427
+ } else if (']' == *pi->s) {
428
+ pi->s++;
429
+ *end = '\0';
430
+ break;
431
+ } else {
432
+ raise_error("invalid format, expected , or ] while in an array", pi->str, pi->s);
433
+ }
434
+ *end = '\0';
528
435
  }
529
436
  return a;
530
437
  }
531
438
 
532
- static Leaf
533
- read_str(ParseInfo pi) {
534
- Leaf leaf = leaf_new(pi->doc, T_STRING);
439
+ static Leaf read_str(ParseInfo pi) {
440
+ Leaf leaf = leaf_new(pi->doc, T_STRING);
535
441
 
536
442
  leaf->str = read_quoted_value(pi);
537
443
 
538
444
  return leaf;
539
445
  }
540
446
 
541
- static Leaf
542
- read_num(ParseInfo pi) {
543
- char *start = pi->s;
544
- int type = T_FIXNUM;
545
- Leaf leaf;
447
+ static Leaf read_num(ParseInfo pi) {
448
+ char *start = pi->s;
449
+ int type = T_FIXNUM;
450
+ Leaf leaf;
546
451
 
547
452
  if ('-' == *pi->s) {
548
- pi->s++;
453
+ pi->s++;
549
454
  }
550
455
  // digits
551
456
  for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
552
457
  }
553
458
  if ('.' == *pi->s) {
554
- type = T_FLOAT;
555
- pi->s++;
556
- for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
557
- }
459
+ type = T_FLOAT;
460
+ pi->s++;
461
+ for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
462
+ }
558
463
  }
559
464
  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);
465
+ pi->s++;
466
+ if ('-' == *pi->s || '+' == *pi->s) {
467
+ pi->s++;
468
+ }
469
+ for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
470
+ }
471
+ }
472
+ leaf = leaf_new(pi->doc, type);
568
473
  leaf->str = start;
569
474
 
570
475
  return leaf;
571
476
  }
572
477
 
573
- static Leaf
574
- read_true(ParseInfo pi) {
575
- Leaf leaf = leaf_new(pi->doc, T_TRUE);
478
+ static Leaf read_true(ParseInfo pi) {
479
+ Leaf leaf = leaf_new(pi->doc, T_TRUE);
576
480
 
577
481
  pi->s++;
578
482
  if ('r' != *pi->s || 'u' != *(pi->s + 1) || 'e' != *(pi->s + 2)) {
579
- raise_error("invalid format, expected 'true'", pi->str, pi->s);
483
+ raise_error("invalid format, expected 'true'", pi->str, pi->s);
580
484
  }
581
485
  pi->s += 3;
582
486
 
583
487
  return leaf;
584
488
  }
585
489
 
586
- static Leaf
587
- read_false(ParseInfo pi) {
588
- Leaf leaf = leaf_new(pi->doc, T_FALSE);
490
+ static Leaf read_false(ParseInfo pi) {
491
+ Leaf leaf = leaf_new(pi->doc, T_FALSE);
589
492
 
590
493
  pi->s++;
591
494
  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);
495
+ raise_error("invalid format, expected 'false'", pi->str, pi->s);
593
496
  }
594
497
  pi->s += 4;
595
498
 
596
499
  return leaf;
597
500
  }
598
501
 
599
- static Leaf
600
- read_nil(ParseInfo pi) {
601
- Leaf leaf = leaf_new(pi->doc, T_NIL);
502
+ static Leaf read_nil(ParseInfo pi) {
503
+ Leaf leaf = leaf_new(pi->doc, T_NIL);
602
504
 
603
505
  pi->s++;
604
506
  if ('u' != *pi->s || 'l' != *(pi->s + 1) || 'l' != *(pi->s + 2)) {
605
- raise_error("invalid format, expected 'nil'", pi->str, pi->s);
507
+ raise_error("invalid format, expected 'nil'", pi->str, pi->s);
606
508
  }
607
509
  pi->s += 3;
608
510
 
609
511
  return leaf;
610
512
  }
611
513
 
612
- static uint32_t
613
- read_4hex(ParseInfo pi, const char *h) {
614
- uint32_t b = 0;
615
- int i;
514
+ static uint32_t read_4hex(ParseInfo pi, const char *h) {
515
+ uint32_t b = 0;
516
+ int i;
616
517
 
617
518
  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
- }
519
+ b = b << 4;
520
+ if ('0' <= *h && *h <= '9') {
521
+ b += *h - '0';
522
+ } else if ('A' <= *h && *h <= 'F') {
523
+ b += *h - 'A' + 10;
524
+ } else if ('a' <= *h && *h <= 'f') {
525
+ b += *h - 'a' + 10;
526
+ } else {
527
+ raise_error("invalid hex character", pi->str, pi->s);
528
+ }
628
529
  }
629
530
  return b;
630
531
  }
631
532
 
632
- static char*
633
- unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
533
+ static char *unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
634
534
  if (0x0000007F >= code) {
635
- *t++ = (char)code;
535
+ *t++ = (char)code;
636
536
  } else if (0x000007FF >= code) {
637
- *t++ = 0xC0 | (code >> 6);
638
- *t++ = 0x80 | (0x3F & code);
537
+ *t++ = 0xC0 | (code >> 6);
538
+ *t++ = 0x80 | (0x3F & code);
639
539
  } else if (0x0000FFFF >= code) {
640
- *t++ = 0xE0 | (code >> 12);
641
- *t++ = 0x80 | ((code >> 6) & 0x3F);
642
- *t++ = 0x80 | (0x3F & code);
540
+ *t++ = 0xE0 | (code >> 12);
541
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
542
+ *t++ = 0x80 | (0x3F & code);
643
543
  } 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);
544
+ *t++ = 0xF0 | (code >> 18);
545
+ *t++ = 0x80 | ((code >> 12) & 0x3F);
546
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
547
+ *t++ = 0x80 | (0x3F & code);
648
548
  } 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);
549
+ *t++ = 0xF8 | (code >> 24);
550
+ *t++ = 0x80 | ((code >> 18) & 0x3F);
551
+ *t++ = 0x80 | ((code >> 12) & 0x3F);
552
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
553
+ *t++ = 0x80 | (0x3F & code);
654
554
  } 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);
555
+ *t++ = 0xFC | (code >> 30);
556
+ *t++ = 0x80 | ((code >> 24) & 0x3F);
557
+ *t++ = 0x80 | ((code >> 18) & 0x3F);
558
+ *t++ = 0x80 | ((code >> 12) & 0x3F);
559
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
560
+ *t++ = 0x80 | (0x3F & code);
661
561
  } else {
662
- raise_error("invalid Unicode character", pi->str, pi->s);
562
+ raise_error("invalid Unicode character", pi->str, pi->s);
663
563
  }
664
564
  return t;
665
565
  }
666
566
 
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
567
+ // Assume the value starts immediately and goes until the quote character is
568
+ // reached again. Do not read the character after the terminating quote.
569
+ static char *read_quoted_value(ParseInfo pi) {
570
+ char *value = 0;
571
+ char *h = pi->s; // head
572
+ char *t = h; // tail
573
+
574
+ h++; // skip quote character
677
575
  t++;
678
576
  value = h;
679
577
  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
578
+ if ('\0' == *h) {
579
+ pi->s = h;
580
+ raise_error("quoted string not terminated", pi->str, pi->s);
581
+ } else if ('\\' == *h) {
582
+ h++;
583
+ switch (*h) {
584
+ case 'n': *t = '\n'; break;
585
+ case 'r': *t = '\r'; break;
586
+ case 't': *t = '\t'; break;
587
+ case 'f': *t = '\f'; break;
588
+ case 'b': *t = '\b'; break;
589
+ case '"': *t = '"'; break;
590
+ case '/': *t = '/'; break;
591
+ case '\\': *t = '\\'; break;
592
+ case 'u': {
593
+ uint32_t code;
594
+
595
+ h++;
596
+ code = read_4hex(pi, h);
597
+ h += 3;
598
+ if (0x0000D800 <= code && code <= 0x0000DFFF) {
599
+ uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
600
+ uint32_t c2;
601
+
602
+ h++;
603
+ if ('\\' != *h || 'u' != *(h + 1)) {
604
+ pi->s = h;
605
+ raise_error("invalid escaped character", pi->str, pi->s);
606
+ }
607
+ h += 2;
608
+ c2 = read_4hex(pi, h);
609
+ h += 3;
610
+ c2 = (c2 - 0x0000DC00) & 0x000003FF;
611
+ code = ((c1 << 10) | c2) + 0x00010000;
612
+ }
613
+ t = unicode_to_chars(pi, t, code);
614
+ t--;
615
+ break;
616
+ }
617
+ default:
618
+ pi->s = h;
619
+ raise_error("invalid escaped character", pi->str, pi->s);
620
+ break;
621
+ }
622
+ } else if (t != h) {
623
+ *t = *h;
624
+ }
625
+ }
626
+ *t = '\0'; // terminate value
729
627
  pi->s = h + 1;
730
628
 
731
629
  return value;
732
630
  }
733
631
 
734
632
  // doc support functions
735
- inline static void
736
- doc_init(Doc doc) {
633
+ inline static void doc_init(Doc doc) {
737
634
  memset(doc, 0, sizeof(struct _doc));
738
- doc->where = doc->where_path;
739
- doc->self = Qundef;
635
+ doc->where = doc->where_path;
636
+ doc->self = Qundef;
740
637
  doc->batches = &doc->batch0;
741
638
  }
742
639
 
743
- static void
744
- doc_free(Doc doc) {
640
+ static void doc_free(Doc doc) {
745
641
  if (0 != doc) {
746
- Batch b;
642
+ Batch b;
747
643
 
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);
644
+ while (0 != (b = doc->batches)) {
645
+ doc->batches = doc->batches->next;
646
+ if (&doc->batch0 != b) {
647
+ OJ_R_FREE(b);
648
+ }
649
+ }
650
+ OJ_R_FREE(doc->json);
651
+ OJ_R_FREE(doc);
755
652
  }
756
653
  }
757
654
 
758
- static VALUE
759
- protect_open_proc(VALUE x) {
760
- ParseInfo pi = (ParseInfo)x;
655
+ static VALUE protect_open_proc(VALUE x) {
656
+ ParseInfo pi = (ParseInfo)x;
761
657
 
762
- pi->doc->data = read_next(pi); // parse
658
+ pi->doc->data = read_next(pi); // parse
763
659
  *pi->doc->where = pi->doc->data;
764
- pi->doc->where = pi->doc->where_path;
660
+ pi->doc->where = pi->doc->where_path;
765
661
  if (rb_block_given_p()) {
766
- return rb_yield(pi->doc->self); // caller processing
662
+ return rb_yield(pi->doc->self); // caller processing
767
663
  }
768
664
  return Qnil;
769
665
  }
770
666
 
771
- static void
772
- free_doc_cb(void *x) {
773
- Doc doc = (Doc)x;
667
+ static void free_doc_cb(void *x) {
668
+ Doc doc = (Doc)x;
774
669
 
775
670
  if (0 != doc) {
776
- xfree(doc->json);
777
- doc_free(doc);
671
+ doc_free(doc);
778
672
  }
779
673
  }
780
674
 
781
- static void
782
- mark_leaf(Leaf leaf) {
783
- switch (leaf->value_type) {
784
- case COL_VAL:
785
- if (NULL != leaf->elements) {
786
- Leaf first = leaf->elements->next;
787
- Leaf e = first;
788
-
789
- do {
790
- mark_leaf(e);
791
- e = e->next;
792
- } while (e != first);
793
- }
794
- break;
795
- case RUBY_VAL:
796
- rb_gc_mark(leaf->value);
797
- break;
798
-
799
- default:
800
- break;
675
+ static void mark_leaf(Leaf leaf) {
676
+ if (NULL != leaf) {
677
+ switch (leaf->value_type) {
678
+ case COL_VAL:
679
+ if (NULL != leaf->elements) {
680
+ Leaf first = leaf->elements->next;
681
+ Leaf e = first;
682
+
683
+ do {
684
+ mark_leaf(e);
685
+ e = e->next;
686
+ } while (e != first);
687
+ }
688
+ break;
689
+ case RUBY_VAL: mark(leaf->value); break;
690
+
691
+ default: break;
692
+ }
801
693
  }
802
694
  }
803
695
 
804
- static void
805
- mark_doc(void *ptr) {
696
+ static void mark_doc(void *ptr) {
806
697
  if (NULL != ptr) {
807
- Doc doc = (Doc)ptr;
808
-
809
- rb_gc_mark(doc->self);
810
- mark_leaf(doc->data);
698
+ Doc doc = (Doc)ptr;
699
+
700
+ mark(doc->self);
701
+ mark_leaf(doc->data);
702
+ }
703
+ }
704
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
705
+ static void compact_leaf(Leaf leaf) {
706
+ switch (leaf->value_type) {
707
+ case COL_VAL:
708
+ if (NULL != leaf->elements) {
709
+ Leaf first = leaf->elements->next;
710
+ Leaf e = first;
711
+
712
+ do {
713
+ compact_leaf(e);
714
+ e = e->next;
715
+ } while (e != first);
716
+ }
717
+ break;
718
+ case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
719
+
720
+ default: break;
811
721
  }
812
722
  }
813
723
 
814
- static VALUE
815
- parse_json(VALUE clas, char *json, bool given, bool allocated) {
816
- struct _parseInfo pi;
817
- volatile VALUE result = Qnil;
818
- Doc doc;
819
- int ex = 0;
820
- volatile VALUE self;
724
+ static void compact_doc(void *ptr) {
725
+ Doc doc = (Doc)ptr;
821
726
 
822
- // TBD are both needed? is stack allocation ever needed?
823
-
824
- if (given) {
825
- doc = ALLOCA_N(struct _doc, 1);
826
- } else {
827
- doc = ALLOC(struct _doc);
727
+ if (doc) {
728
+ doc->self = rb_gc_location(doc->self);
729
+ compact_leaf(doc->data);
828
730
  }
829
- /* skip UTF-8 BOM if present */
731
+ }
732
+ #endif
733
+
734
+ static const rb_data_type_t oj_doc_type = {
735
+ "Oj/doc",
736
+ {
737
+ mark_doc,
738
+ free_doc_cb,
739
+ NULL,
740
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
741
+ compact_doc,
742
+ #endif
743
+ },
744
+ 0,
745
+ 0,
746
+ };
747
+
748
+ static VALUE parse_json(VALUE clas, char *json, bool given) {
749
+ struct _parseInfo pi;
750
+ volatile VALUE result = Qnil;
751
+ Doc doc;
752
+ int ex = 0;
753
+ volatile VALUE self;
754
+
755
+ doc = OJ_R_ALLOC_N(struct _doc, 1);
756
+
757
+ // skip UTF-8 BOM if present
830
758
  if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
831
- pi.str = json + 3;
759
+ pi.str = json + 3;
832
760
  } else {
833
- pi.str = json;
761
+ pi.str = json;
834
762
  }
835
763
  pi.s = pi.str;
836
764
  doc_init(doc);
837
765
  pi.doc = doc;
838
- #if IS_WINDOWS
839
- pi.stack_min = (void*)((char*)&pi - (512 * 1024)); // assume a 1M stack and give half to ruby
766
+ #if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
767
+ // assume a 1M stack and give half to ruby
768
+ pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
840
769
  #else
841
770
  {
842
- struct rlimit lim;
771
+ struct rlimit lim;
843
772
 
844
- if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
845
- pi.stack_min = (void*)((char*)&lim - (lim.rlim_cur / 4 * 3)); // let 3/4ths of the stack be used only
846
- } else {
847
- pi.stack_min = 0; // indicates not to check stack limit
848
- }
773
+ if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
774
+ // let 3/4ths of the stack be used only
775
+ pi.stack_min = (void *)((char *)&lim - (lim.rlim_cur / 4 * 3));
776
+ } else {
777
+ pi.stack_min = 0; // indicates not to check stack limit
778
+ }
849
779
  }
850
780
  #endif
851
- // last arg is free func void* func(void*)
852
- #ifdef HAVE_RB_DATA_OBJECT_WRAP
853
- self = rb_data_object_wrap(clas, doc, mark_doc, free_doc_cb);
854
- #else
855
- self = rb_data_object_alloc(clas, doc, mark_doc, free_doc_cb);
856
- #endif
857
- doc->self = self;
858
781
  doc->json = json;
859
- DATA_PTR(doc->self) = doc;
860
- result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
782
+ self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
783
+ doc->self = self;
784
+ result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
861
785
  if (given || 0 != ex) {
862
- DATA_PTR(doc->self) = NULL;
863
- doc_free(pi.doc);
864
- if (allocated && 0 != ex) { // will jump so caller will not free
865
- xfree(json);
866
- }
867
- rb_gc_enable();
786
+ DATA_PTR(doc->self) = NULL;
787
+ // TBD is this needed?
788
+ /*
789
+ doc_free(pi.doc);
790
+ if (0 != ex) { // will jump so caller will not free
791
+ OJ_R_FREE(json);
792
+ }
793
+ */
868
794
  } else {
869
- result = doc->self;
795
+ result = doc->self;
870
796
  }
871
797
  if (0 != ex) {
872
- rb_jump_tag(ex);
798
+ rb_jump_tag(ex);
873
799
  }
874
800
  return result;
875
801
  }
876
802
 
877
- static Leaf
878
- get_doc_leaf(Doc doc, const char *path) {
879
- Leaf leaf = *doc->where;
803
+ static Leaf get_doc_leaf(Doc doc, const char *path) {
804
+ Leaf leaf = *doc->where;
880
805
 
881
806
  if (0 != doc->data && 0 != path) {
882
- Leaf stack[MAX_STACK];
883
- Leaf *lp;
884
-
885
- if ('/' == *path) {
886
- path++;
887
- *stack = doc->data;
888
- lp = stack;
889
- } else if (doc->where == doc->where_path) {
890
- *stack = doc->data;
891
- lp = stack;
892
- } else {
893
- size_t cnt = doc->where - doc->where_path;
894
-
895
- if (MAX_STACK <= cnt) {
896
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
897
- }
898
- memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
899
- lp = stack + cnt;
900
- }
901
- return get_leaf(stack, lp, path);
807
+ Leaf stack[MAX_STACK];
808
+ Leaf *lp;
809
+
810
+ if ('/' == *path) {
811
+ path++;
812
+ *stack = doc->data;
813
+ lp = stack;
814
+ } else if (doc->where == doc->where_path) {
815
+ *stack = doc->data;
816
+ lp = stack;
817
+ } else {
818
+ size_t cnt = doc->where - doc->where_path;
819
+
820
+ if (MAX_STACK <= cnt) {
821
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
822
+ }
823
+ memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
824
+ lp = stack + cnt;
825
+ }
826
+ return get_leaf(stack, lp, path);
902
827
  }
903
828
  return leaf;
904
829
  }
905
830
 
906
- static const char*
907
- next_slash(const char *s) {
831
+ static const char *next_slash(const char *s) {
908
832
  for (; '\0' != *s; s++) {
909
- if ('\\' == *s) {
910
- s++;
911
- if ('\0' == *s) {
912
- break;
913
- }
914
- } else if ('/' == *s) {
915
- return s;
916
- }
833
+ if ('\\' == *s) {
834
+ s++;
835
+ if ('\0' == *s) {
836
+ break;
837
+ }
838
+ } else if ('/' == *s) {
839
+ return s;
840
+ }
917
841
  }
918
842
  return NULL;
919
843
  }
920
844
 
921
- static bool
922
- key_match(const char *pat, const char *key, int plen) {
845
+ static bool key_match(const char *pat, const char *key, int plen) {
923
846
  for (; 0 < plen; plen--, pat++, key++) {
924
- if ('\\' == *pat) {
925
- plen--;
926
- pat++;
927
- }
928
- if (*pat != *key) {
929
- return false;
930
- }
847
+ if ('\\' == *pat) {
848
+ plen--;
849
+ pat++;
850
+ }
851
+ if (*pat != *key) {
852
+ return false;
853
+ }
931
854
  }
932
855
  return '\0' == *key;
933
856
  }
934
857
 
935
- static Leaf
936
- get_leaf(Leaf *stack, Leaf *lp, const char *path) {
937
- Leaf leaf = *lp;
858
+ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
859
+ Leaf leaf = *lp;
938
860
 
939
861
  if (MAX_STACK <= lp - stack) {
940
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
862
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
941
863
  }
942
864
  if ('\0' != *path) {
943
- if ('.' == *path && '.' == *(path + 1)) {
944
- path += 2;
945
- if ('/' == *path) {
946
- path++;
947
- }
948
- if (stack < lp) {
949
- leaf = get_leaf(stack, lp - 1, path);
950
- } else {
951
- return 0;
952
- }
953
- } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
954
- Leaf first = leaf->elements->next;
955
- Leaf e = first;
956
- int type = leaf->rtype;
957
-
958
- leaf = 0;
959
- if (T_ARRAY == type) {
960
- int cnt = 0;
961
-
962
- for (; '0' <= *path && *path <= '9'; path++) {
963
- cnt = cnt * 10 + (*path - '0');
964
- }
965
- if ('/' == *path) {
966
- path++;
967
- }
968
- do {
969
- if (1 >= cnt) {
970
- lp++;
971
- *lp = e;
972
- leaf = get_leaf(stack, lp, path);
973
- break;
974
- }
975
- cnt--;
976
- e = e->next;
977
- } while (e != first);
978
- } else if (T_HASH == type) {
979
- const char *key = path;
980
- const char *slash = next_slash(path);
981
- int klen;
982
-
983
- if (0 == slash) {
984
- klen = (int)strlen(key);
985
- path += klen;
986
- } else {
987
- klen = (int)(slash - key);
988
- path += klen + 1;
989
- }
990
- do {
991
- if (key_match(key, e->key, klen)) {
992
- lp++;
993
- *lp = e;
994
- leaf = get_leaf(stack, lp, path);
995
- break;
996
- }
997
- e = e->next;
998
- } while (e != first);
999
- }
1000
- }
865
+ if ('.' == *path && '.' == *(path + 1)) {
866
+ path += 2;
867
+ if ('/' == *path) {
868
+ path++;
869
+ }
870
+ if (stack < lp) {
871
+ leaf = get_leaf(stack, lp - 1, path);
872
+ } else {
873
+ return 0;
874
+ }
875
+ } else if (NULL == leaf->elements) {
876
+ leaf = NULL;
877
+ } else if (STR_VAL == leaf->value_type || RUBY_VAL == leaf->value_type) {
878
+ // We are trying to get a children of a leaf, which
879
+ // doesn't exist.
880
+ leaf = NULL;
881
+ } else if (COL_VAL == leaf->value_type) {
882
+ Leaf first = leaf->elements->next;
883
+ Leaf e = first;
884
+ int type = leaf->rtype;
885
+
886
+ leaf = NULL;
887
+ if (T_ARRAY == type) {
888
+ int cnt = 0;
889
+
890
+ for (; '0' <= *path && *path <= '9'; path++) {
891
+ cnt = cnt * 10 + (*path - '0');
892
+ }
893
+ if ('/' == *path) {
894
+ path++;
895
+ }
896
+ do {
897
+ if (1 >= cnt) {
898
+ lp++;
899
+ *lp = e;
900
+ leaf = get_leaf(stack, lp, path);
901
+ break;
902
+ }
903
+ cnt--;
904
+ e = e->next;
905
+ } while (e != first);
906
+ } else if (T_HASH == type) {
907
+ const char *key = path;
908
+ const char *slash = next_slash(path);
909
+ int klen;
910
+
911
+ leaf = NULL;
912
+ if (0 == slash) {
913
+ klen = (int)strlen(key);
914
+ path += klen;
915
+ } else {
916
+ klen = (int)(slash - key);
917
+ path += klen + 1;
918
+ }
919
+ do {
920
+ if (key_match(key, e->key, klen)) {
921
+ lp++;
922
+ *lp = e;
923
+ leaf = get_leaf(stack, lp, path);
924
+ break;
925
+ }
926
+ e = e->next;
927
+ } while (e != first);
928
+ }
929
+ }
1001
930
  }
1002
931
  return leaf;
1003
932
  }
1004
933
 
1005
- static void
1006
- each_leaf(Doc doc, VALUE self) {
934
+ static void each_leaf(Doc doc, VALUE self) {
1007
935
  if (COL_VAL == (*doc->where)->value_type) {
1008
- if (0 != (*doc->where)->elements) {
1009
- Leaf first = (*doc->where)->elements->next;
1010
- Leaf e = first;
1011
-
1012
- doc->where++;
1013
- if (MAX_STACK <= doc->where - doc->where_path) {
1014
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
1015
- }
1016
- do {
1017
- *doc->where = e;
1018
- each_leaf(doc, self);
1019
- e = e->next;
1020
- } while (e != first);
1021
- doc->where--;
1022
- }
936
+ if (0 != (*doc->where)->elements) {
937
+ Leaf first = (*doc->where)->elements->next;
938
+ Leaf e = first;
939
+
940
+ doc->where++;
941
+ if (MAX_STACK <= doc->where - doc->where_path) {
942
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
943
+ }
944
+ do {
945
+ *doc->where = e;
946
+ each_leaf(doc, self);
947
+ e = e->next;
948
+ } while (e != first);
949
+ doc->where--;
950
+ }
1023
951
  } else {
1024
- rb_yield(self);
952
+ rb_yield(self);
1025
953
  }
1026
954
  }
1027
955
 
1028
- static int
1029
- move_step(Doc doc, const char *path, int loc) {
956
+ static int move_step(Doc doc, const char *path, int loc) {
1030
957
  if (MAX_STACK <= doc->where - doc->where_path) {
1031
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
958
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
1032
959
  }
1033
960
  if ('\0' == *path) {
1034
- loc = 0;
961
+ loc = 0;
1035
962
  } else {
1036
- Leaf leaf;
1037
-
1038
- if (0 == doc->where || 0 == (leaf = *doc->where)) {
1039
- printf("*** Internal error at %s\n", path);
1040
- return loc;
1041
- }
1042
- if ('.' == *path && '.' == *(path + 1)) {
1043
- Leaf init = *doc->where;
1044
-
1045
- path += 2;
1046
- if (doc->where == doc->where_path) {
1047
- return loc;
1048
- }
1049
- if ('/' == *path) {
1050
- path++;
1051
- }
1052
- *doc->where = 0;
1053
- doc->where--;
1054
- loc = move_step(doc, path, loc + 1);
1055
- if (0 != loc) {
1056
- *doc->where = init;
1057
- doc->where++;
1058
- }
1059
- } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
1060
- Leaf first = leaf->elements->next;
1061
- Leaf e = first;
1062
-
1063
- if (T_ARRAY == leaf->rtype) {
1064
- int cnt = 0;
1065
-
1066
- for (; '0' <= *path && *path <= '9'; path++) {
1067
- cnt = cnt * 10 + (*path - '0');
1068
- }
1069
- if ('/' == *path) {
1070
- path++;
1071
- } else if ('\0' != *path) {
1072
- return loc;
1073
- }
1074
- do {
1075
- if (1 >= cnt) {
1076
- doc->where++;
1077
- *doc->where = e;
1078
- loc = move_step(doc, path, loc + 1);
1079
- if (0 != loc) {
1080
- *doc->where = 0;
1081
- doc->where--;
1082
- }
1083
- break;
1084
- }
1085
- cnt--;
1086
- e = e->next;
1087
- } while (e != first);
1088
- } else if (T_HASH == leaf->rtype) {
1089
- const char *key = path;
1090
- const char *slash = next_slash(path);
1091
- int klen;
1092
-
1093
- if (0 == slash) {
1094
- klen = (int)strlen(key);
1095
- path += klen;
1096
- } else {
1097
- klen = (int)(slash - key);
1098
- path += klen + 1;
1099
- }
1100
- do {
1101
- if (key_match(key, e->key, klen)) {
1102
- doc->where++;
1103
- *doc->where = e;
1104
- loc = move_step(doc, path, loc + 1);
1105
- if (0 != loc) {
1106
- *doc->where = 0;
1107
- doc->where--;
1108
- }
1109
- break;
1110
- }
1111
- e = e->next;
1112
- } while (e != first);
1113
- }
1114
- }
963
+ Leaf leaf;
964
+
965
+ if (0 == doc->where || 0 == (leaf = *doc->where)) {
966
+ printf("*** Internal error at %s\n", path);
967
+ return loc;
968
+ }
969
+ if ('.' == *path && '.' == *(path + 1)) {
970
+ Leaf init = *doc->where;
971
+
972
+ path += 2;
973
+ if (doc->where == doc->where_path) {
974
+ return loc;
975
+ }
976
+ if ('/' == *path) {
977
+ path++;
978
+ }
979
+ *doc->where = 0;
980
+ doc->where--;
981
+ loc = move_step(doc, path, loc + 1);
982
+ if (0 != loc) {
983
+ *doc->where = init;
984
+ doc->where++;
985
+ }
986
+ } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
987
+ Leaf first = leaf->elements->next;
988
+ Leaf e = first;
989
+
990
+ if (T_ARRAY == leaf->rtype) {
991
+ int cnt = 0;
992
+
993
+ for (; '0' <= *path && *path <= '9'; path++) {
994
+ cnt = cnt * 10 + (*path - '0');
995
+ }
996
+ if ('/' == *path) {
997
+ path++;
998
+ } else if ('\0' != *path) {
999
+ return loc;
1000
+ }
1001
+ do {
1002
+ if (1 >= cnt) {
1003
+ doc->where++;
1004
+ *doc->where = e;
1005
+ loc = move_step(doc, path, loc + 1);
1006
+ if (0 != loc) {
1007
+ *doc->where = 0;
1008
+ doc->where--;
1009
+ }
1010
+ break;
1011
+ }
1012
+ cnt--;
1013
+ e = e->next;
1014
+ } while (e != first);
1015
+ } else if (T_HASH == leaf->rtype) {
1016
+ const char *key = path;
1017
+ const char *slash = next_slash(path);
1018
+ int klen;
1019
+
1020
+ if (0 == slash) {
1021
+ klen = (int)strlen(key);
1022
+ path += klen;
1023
+ } else {
1024
+ klen = (int)(slash - key);
1025
+ path += klen + 1;
1026
+ }
1027
+ do {
1028
+ if (key_match(key, e->key, klen)) {
1029
+ doc->where++;
1030
+ *doc->where = e;
1031
+ loc = move_step(doc, path, loc + 1);
1032
+ if (0 != loc) {
1033
+ *doc->where = 0;
1034
+ doc->where--;
1035
+ }
1036
+ break;
1037
+ }
1038
+ e = e->next;
1039
+ } while (e != first);
1040
+ }
1041
+ }
1115
1042
  }
1116
1043
  return loc;
1117
1044
  }
1118
1045
 
1119
- static void
1120
- each_value(Doc doc, Leaf leaf) {
1046
+ static void each_value(Doc doc, Leaf leaf) {
1121
1047
  if (COL_VAL == leaf->value_type) {
1122
- if (0 != leaf->elements) {
1123
- Leaf first = leaf->elements->next;
1124
- Leaf e = first;
1125
-
1126
- do {
1127
- each_value(doc, e);
1128
- e = e->next;
1129
- } while (e != first);
1130
- }
1048
+ if (0 != leaf->elements) {
1049
+ Leaf first = leaf->elements->next;
1050
+ Leaf e = first;
1051
+
1052
+ do {
1053
+ each_value(doc, e);
1054
+ e = e->next;
1055
+ } while (e != first);
1056
+ }
1131
1057
  } else {
1132
- rb_yield(leaf_value(doc, leaf));
1058
+ rb_yield(leaf_value(doc, leaf));
1133
1059
  }
1134
1060
  }
1135
1061
 
@@ -1144,7 +1070,8 @@ each_value(Doc doc, Leaf leaf) {
1144
1070
  *
1145
1071
  * @param [String] json JSON document string
1146
1072
  * @yieldparam [Oj::Doc] doc parsed JSON document
1147
- * @yieldreturn [Object] returns the result of the yield as the result of the method call
1073
+ * @yieldreturn [Object] returns the result of the yield as the result of the
1074
+ * method call
1148
1075
  * @example
1149
1076
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1150
1077
  * # or as an alternative
@@ -1152,33 +1079,24 @@ each_value(Doc doc, Leaf leaf) {
1152
1079
  * doc.size() #=> 4
1153
1080
  * doc.close()
1154
1081
  */
1155
- static VALUE
1156
- doc_open(VALUE clas, VALUE str) {
1157
- char *json;
1158
- size_t len;
1159
- volatile VALUE obj;
1160
- int given = rb_block_given_p();
1161
- int allocate;
1082
+ static VALUE doc_open(VALUE clas, VALUE str) {
1083
+ char *json;
1084
+ size_t len;
1085
+ volatile VALUE obj;
1086
+ int given = rb_block_given_p();
1162
1087
 
1163
1088
  Check_Type(str, T_STRING);
1164
- len = (int)RSTRING_LEN(str) + 1;
1165
- allocate = (SMALL_XML < len || !given);
1166
- if (allocate) {
1167
- json = ALLOC_N(char, len);
1168
- } else {
1169
- json = ALLOCA_N(char, len);
1170
- }
1171
- // It should not be necessaary to stop GC but if it is not stopped and a
1172
- // large string is parsed that string is corrupted or freed during
1173
- // parsing. I'm not sure what is going on exactly but disabling GC avoids
1174
- // the issue.
1175
- rb_gc_disable();
1089
+ len = RSTRING_LEN(str) + 1;
1090
+ json = OJ_R_ALLOC_N(char, len);
1091
+
1176
1092
  memcpy(json, StringValuePtr(str), len);
1177
- obj = parse_json(clas, json, given, allocate);
1178
- rb_gc_enable();
1179
- if (given && allocate) {
1180
- xfree(json);
1093
+ obj = parse_json(clas, json, given);
1094
+ // TBD is this needed
1095
+ /*
1096
+ if (given) {
1097
+ OJ_R_FREE(json);
1181
1098
  }
1099
+ */
1182
1100
  return obj;
1183
1101
  }
1184
1102
 
@@ -1191,7 +1109,8 @@ doc_open(VALUE clas, VALUE str) {
1191
1109
  *
1192
1110
  * @param [String] filename name of file that contains a JSON document
1193
1111
  * @yieldparam [Oj::Doc] doc parsed JSON document
1194
- * @yieldreturn [Object] returns the result of the yield as the result of the method call
1112
+ * @yieldreturn [Object] returns the result of the yield as the result of the
1113
+ * method call
1195
1114
  * @example
1196
1115
  * File.open('array.json', 'w') { |f| f.write('[1,2,3]') }
1197
1116
  * Oj::Doc.open_file(filename) { |doc| doc.size() } #=> 4
@@ -1200,65 +1119,59 @@ doc_open(VALUE clas, VALUE str) {
1200
1119
  * doc.size() #=> 4
1201
1120
  * doc.close()
1202
1121
  */
1203
- static VALUE
1204
- doc_open_file(VALUE clas, VALUE filename) {
1205
- char *path;
1206
- char *json;
1207
- FILE *f;
1208
- size_t len;
1209
- volatile VALUE obj;
1210
- int given = rb_block_given_p();
1211
- int allocate;
1212
-
1213
- Check_Type(filename, T_STRING);
1122
+ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1123
+ char *path;
1124
+ char *json;
1125
+ FILE *f;
1126
+ size_t len;
1127
+ volatile VALUE obj;
1128
+ int given = rb_block_given_p();
1129
+
1214
1130
  path = StringValuePtr(filename);
1215
1131
  if (0 == (f = fopen(path, "r"))) {
1216
- rb_raise(rb_eIOError, "%s", strerror(errno));
1132
+ rb_raise(rb_eIOError, "%s", strerror(errno));
1217
1133
  }
1218
1134
  fseek(f, 0, SEEK_END);
1219
- len = ftell(f);
1220
- allocate = (SMALL_XML < len || !given);
1221
- if (allocate) {
1222
- json = ALLOC_N(char, len + 1);
1223
- } else {
1224
- json = ALLOCA_N(char, len + 1);
1225
- }
1135
+ len = ftell(f);
1136
+ json = OJ_R_ALLOC_N(char, len + 1);
1137
+
1226
1138
  fseek(f, 0, SEEK_SET);
1227
1139
  if (len != fread(json, 1, len, f)) {
1228
- fclose(f);
1229
- rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")),
1230
- "Failed to read %lu bytes from %s.", (unsigned long)len, path);
1140
+ fclose(f);
1141
+ rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")),
1142
+ "Failed to read %lu bytes from %s.",
1143
+ (unsigned long)len,
1144
+ path);
1231
1145
  }
1232
1146
  fclose(f);
1233
1147
  json[len] = '\0';
1234
- rb_gc_disable();
1235
- obj = parse_json(clas, json, given, allocate);
1236
- rb_gc_enable();
1237
- if (given && allocate) {
1238
- xfree(json);
1148
+ obj = parse_json(clas, json, given);
1149
+ // TBD is this needed
1150
+ /*
1151
+ if (given) {
1152
+ OJ_R_FREE(json);
1239
1153
  }
1154
+ */
1240
1155
  return obj;
1241
1156
  }
1242
1157
 
1243
- static int
1244
- esc_strlen(const char *s) {
1245
- int cnt = 0;
1158
+ static int esc_strlen(const char *s) {
1159
+ int cnt = 0;
1246
1160
 
1247
1161
  for (; '\0' != *s; s++, cnt++) {
1248
- if ('/' == *s) {
1249
- cnt++;
1250
- }
1162
+ if ('/' == *s) {
1163
+ cnt++;
1164
+ }
1251
1165
  }
1252
1166
  return cnt;
1253
1167
  }
1254
1168
 
1255
- static char*
1256
- append_key(char *p, const char *key) {
1169
+ static char *append_key(char *p, const char *key) {
1257
1170
  for (; '\0' != *key; p++, key++) {
1258
- if ('/' == *key) {
1259
- *p++ = '\\';
1260
- }
1261
- *p = *key;
1171
+ if ('/' == *key) {
1172
+ *p++ = '\\';
1173
+ }
1174
+ *p = *key;
1262
1175
  }
1263
1176
  return p;
1264
1177
  }
@@ -1267,68 +1180,84 @@ append_key(char *p, const char *key) {
1267
1180
  * @see Oj::Doc.open
1268
1181
  */
1269
1182
 
1270
- /* @overload where?() => String
1183
+ /* @overload where() => String
1271
1184
  *
1272
1185
  * Returns a String that describes the absolute path to the current location
1273
1186
  * in the JSON document.
1274
1187
  */
1275
- static VALUE
1276
- doc_where(VALUE self) {
1277
- Doc doc = self_doc(self);
1188
+ static VALUE doc_where(VALUE self) {
1189
+ Doc doc = self_doc(self);
1278
1190
 
1279
1191
  if (0 == *doc->where_path || doc->where == doc->where_path) {
1280
- return oj_slash_string;
1192
+ return oj_slash_string;
1281
1193
  } else {
1282
- Leaf *lp;
1283
- Leaf leaf;
1284
- size_t size = 3; // leading / and terminating \0
1285
- char *path;
1286
- char *p;
1287
-
1288
- for (lp = doc->where_path; lp <= doc->where; lp++) {
1289
- leaf = *lp;
1290
- if (T_HASH == leaf->parent_type) {
1291
- size += esc_strlen((*lp)->key) + 1;
1292
- } else if (T_ARRAY == leaf->parent_type) {
1293
- size += ((*lp)->index < 100) ? 3 : 11;
1294
- }
1295
- }
1296
- path = ALLOCA_N(char, size);
1297
- p = path;
1298
- for (lp = doc->where_path; lp <= doc->where; lp++) {
1299
- leaf = *lp;
1300
- if (T_HASH == leaf->parent_type) {
1301
- p = append_key(p, (*lp)->key);
1302
- } else if (T_ARRAY == leaf->parent_type) {
1303
- p = ulong_fill(p, (*lp)->index);
1304
- }
1305
- *p++ = '/';
1306
- }
1307
- *--p = '\0';
1308
-
1309
- return rb_str_new(path, p - path);
1194
+ Leaf *lp;
1195
+ Leaf leaf;
1196
+ size_t size = 3; // leading / and terminating \0
1197
+ char *path;
1198
+ char *p;
1199
+
1200
+ for (lp = doc->where_path; lp <= doc->where; lp++) {
1201
+ leaf = *lp;
1202
+ if (T_HASH == leaf->parent_type) {
1203
+ size += esc_strlen((*lp)->key) + 1;
1204
+ } else if (T_ARRAY == leaf->parent_type) {
1205
+ size += ((*lp)->index < 100) ? 3 : 11;
1206
+ }
1207
+ }
1208
+ path = ALLOCA_N(char, size);
1209
+ p = path;
1210
+ for (lp = doc->where_path; lp <= doc->where; lp++) {
1211
+ leaf = *lp;
1212
+ if (T_HASH == leaf->parent_type) {
1213
+ p = append_key(p, (*lp)->key);
1214
+ } else if (T_ARRAY == leaf->parent_type) {
1215
+ p = ulong_fill(p, (*lp)->index);
1216
+ }
1217
+ *p++ = '/';
1218
+ }
1219
+ *--p = '\0';
1220
+
1221
+ return rb_str_new(path, p - path);
1310
1222
  }
1311
1223
  }
1312
1224
 
1225
+ /* @overload where?() => String
1226
+ * @deprecated
1227
+ * Returns a String that describes the absolute path to the current location
1228
+ * in the JSON document.
1229
+ */
1230
+ static VALUE doc_where_q(VALUE self) {
1231
+ return doc_where(self);
1232
+ }
1233
+
1234
+ /* @overload path() => String
1235
+ *
1236
+ * Returns a String that describes the absolute path to the current location
1237
+ * in the JSON document.
1238
+ */
1239
+ static VALUE doc_path(VALUE self) {
1240
+ return doc_where(self);
1241
+ }
1242
+
1313
1243
  /* @overload local_key() => String, Fixnum, nil
1314
1244
  *
1315
1245
  * Returns the final key to the current location.
1316
1246
  * @example
1317
- * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() } #=> 2
1318
- * Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=> "one"
1319
- * Oj::Doc.open('[1,2,3]') { |doc| doc.local_key() } #=> nil
1247
+ * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() } #=> 2
1248
+ * Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=>
1249
+ * "one" Oj::Doc.open('[1,2,3]') { |doc| doc.local_key() }
1250
+ * #=> nil
1320
1251
  */
1321
- static VALUE
1322
- doc_local_key(VALUE self) {
1323
- Doc doc = self_doc(self);
1324
- Leaf leaf = *doc->where;
1325
- volatile VALUE key = Qnil;
1252
+ static VALUE doc_local_key(VALUE self) {
1253
+ Doc doc = self_doc(self);
1254
+ Leaf leaf = *doc->where;
1255
+ volatile VALUE key = Qnil;
1326
1256
 
1327
1257
  if (T_HASH == leaf->parent_type) {
1328
- key = rb_str_new2(leaf->key);
1329
- key = oj_encode(key);
1258
+ key = rb_utf8_str_new_cstr(leaf->key);
1330
1259
  } else if (T_ARRAY == leaf->parent_type) {
1331
- key = LONG2NUM(leaf->index);
1260
+ key = LONG2NUM(leaf->index);
1332
1261
  }
1333
1262
  return key;
1334
1263
  }
@@ -1338,14 +1267,14 @@ doc_local_key(VALUE self) {
1338
1267
  * Moves the document marker or location to the hoot or home position. The
1339
1268
  * same operation can be performed with a Oj::Doc.move('/').
1340
1269
  * @example
1341
- * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? } #=> '/'
1270
+ * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? }
1271
+ * #=> '/'
1342
1272
  */
1343
- static VALUE
1344
- doc_home(VALUE self) {
1345
- Doc doc = self_doc(self);
1273
+ static VALUE doc_home(VALUE self) {
1274
+ Doc doc = self_doc(self);
1346
1275
 
1347
1276
  *doc->where_path = doc->data;
1348
- doc->where = doc->where_path;
1277
+ doc->where = doc->where_path;
1349
1278
 
1350
1279
  return oj_slash_string;
1351
1280
  }
@@ -1361,76 +1290,91 @@ doc_home(VALUE self) {
1361
1290
  * Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
1362
1291
  * Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
1363
1292
  */
1364
- static VALUE
1365
- doc_type(int argc, VALUE *argv, VALUE self) {
1366
- Doc doc = self_doc(self);
1367
- Leaf leaf;
1368
- const char *path = 0;
1369
- VALUE type = Qnil;
1293
+ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
1294
+ Doc doc = self_doc(self);
1295
+ Leaf leaf;
1296
+ const char *path = 0;
1297
+ VALUE type = Qnil;
1370
1298
 
1371
1299
  if (1 <= argc) {
1372
- Check_Type(*argv, T_STRING);
1373
- path = StringValuePtr(*argv);
1300
+ path = StringValuePtr(*argv);
1374
1301
  }
1375
1302
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1376
- switch (leaf->rtype) {
1377
- case T_NIL: type = rb_cNilClass; break;
1378
- case T_TRUE: type = rb_cTrueClass; break;
1379
- case T_FALSE: type = rb_cFalseClass; break;
1380
- case T_STRING: type = rb_cString; break;
1381
- #ifdef RUBY_INTEGER_UNIFICATION
1382
- case T_FIXNUM: type = rb_cInteger; break;
1383
- #else
1384
- case T_FIXNUM: type = rb_cFixnum; break;
1385
- #endif
1386
- case T_FLOAT: type = rb_cFloat; break;
1387
- case T_ARRAY: type = rb_cArray; break;
1388
- case T_HASH: type = rb_cHash; break;
1389
- default: break;
1390
- }
1303
+ switch (leaf->rtype) {
1304
+ case T_NIL: type = rb_cNilClass; break;
1305
+ case T_TRUE: type = rb_cTrueClass; break;
1306
+ case T_FALSE: type = rb_cFalseClass; break;
1307
+ case T_STRING: type = rb_cString; break;
1308
+ case T_FIXNUM: type = rb_cInteger; break;
1309
+ case T_FLOAT: type = rb_cFloat; break;
1310
+ case T_ARRAY: type = rb_cArray; break;
1311
+ case T_HASH: type = rb_cHash; break;
1312
+ default: break;
1313
+ }
1391
1314
  }
1392
1315
  return type;
1393
1316
  }
1394
1317
 
1395
- /* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array, Hash
1318
+ /* @overload fetch(path=nil,default=nil) => nil, true, false, Fixnum, Float, String, Array,
1319
+ * Hash
1396
1320
  *
1397
1321
  * Returns the value at the location identified by the path or the current
1398
1322
  * location if the path is nil or not provided. This method will create and
1399
1323
  * return an Array or Hash if that is the type of Object at the location
1400
1324
  * specified. This is more expensive than navigating to the leaves of the JSON
1401
- * document.
1325
+ * document. If a default is provided that is used if no value if found.
1402
1326
  * @param [String] path path to the location to get the type of if provided
1403
1327
  * @example
1404
1328
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
1405
1329
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
1406
1330
  */
1407
- static VALUE
1408
- doc_fetch(int argc, VALUE *argv, VALUE self) {
1409
- Doc doc;
1410
- Leaf leaf;
1411
- volatile VALUE val = Qnil;
1412
- const char *path = 0;
1331
+ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
1332
+ Doc doc;
1333
+ Leaf leaf;
1334
+ volatile VALUE val = Qnil;
1335
+ const char *path = 0;
1413
1336
 
1414
1337
  doc = self_doc(self);
1415
1338
  if (1 <= argc) {
1416
- Check_Type(*argv, T_STRING);
1417
- path = StringValuePtr(*argv);
1418
- if (2 == argc) {
1419
- val = argv[1];
1420
- }
1339
+ path = StringValuePtr(*argv);
1340
+ if (2 == argc) {
1341
+ val = argv[1];
1342
+ }
1421
1343
  }
1422
1344
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1423
- val = leaf_value(doc, leaf);
1345
+ val = leaf_value(doc, leaf);
1424
1346
  }
1425
1347
  return val;
1426
1348
  }
1427
1349
 
1350
+ /* @overload exists?(path) => true, false
1351
+ *
1352
+ * Returns true if the value at the location identified by the path exists.
1353
+ * @param [String] path path to the location
1354
+ * @example
1355
+ * Oj::Doc.open('[1,2]') { |doc| doc.exists?('/1') } #=> true
1356
+ * Oj::Doc.open('[1,2]') { |doc| doc.exists?('/3') } #=> false
1357
+ */
1358
+ static VALUE doc_exists(VALUE self, VALUE str) {
1359
+ Doc doc;
1360
+ Leaf leaf;
1361
+
1362
+ doc = self_doc(self);
1363
+ if (0 != (leaf = get_doc_leaf(doc, StringValuePtr(str)))) {
1364
+ if (NULL != leaf) {
1365
+ return Qtrue;
1366
+ }
1367
+ }
1368
+ return Qfalse;
1369
+ }
1370
+
1428
1371
  /* @overload each_leaf(path=nil) => nil
1429
1372
  *
1430
1373
  * Yields to the provided block for each leaf node with the identified
1431
1374
  * location of the JSON document as the root. The parameter passed to the
1432
1375
  * block on yield is the Doc instance after moving to the child location.
1433
- * @param [String] path if provided it identified the top of the branch to process the leaves of
1376
+ * @param [String] path if provided it identified the top of the branch to
1377
+ * process the leaves of
1434
1378
  * @yieldparam [Doc] Doc at the child location
1435
1379
  * @example
1436
1380
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1440,36 +1384,34 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
1440
1384
  * }
1441
1385
  * #=> ["/1" => 3, "/2/1" => 2, "/2/2" => 1]
1442
1386
  */
1443
- static VALUE
1444
- doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1387
+ static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1445
1388
  if (rb_block_given_p()) {
1446
- Leaf save_path[MAX_STACK];
1447
- Doc doc = self_doc(self);
1448
- const char *path = 0;
1449
- size_t wlen;
1450
-
1451
- wlen = doc->where - doc->where_path;
1452
- if (0 < wlen) {
1453
- memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1454
- }
1455
- if (1 <= argc) {
1456
- Check_Type(*argv, T_STRING);
1457
- path = StringValuePtr(*argv);
1458
- if ('/' == *path) {
1459
- doc->where = doc->where_path;
1460
- path++;
1461
- }
1462
- if (0 != move_step(doc, path, 1)) {
1463
- if (0 < wlen) {
1464
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1465
- }
1466
- return Qnil;
1467
- }
1468
- }
1469
- each_leaf(doc, self);
1470
- if (0 < wlen) {
1471
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1472
- }
1389
+ Leaf save_path[MAX_STACK];
1390
+ Doc doc = self_doc(self);
1391
+ const char *path = 0;
1392
+ size_t wlen;
1393
+
1394
+ wlen = doc->where - doc->where_path;
1395
+ if (0 < wlen) {
1396
+ memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1397
+ }
1398
+ if (1 <= argc) {
1399
+ path = StringValuePtr(*argv);
1400
+ if ('/' == *path) {
1401
+ doc->where = doc->where_path;
1402
+ path++;
1403
+ }
1404
+ if (0 != move_step(doc, path, 1)) {
1405
+ if (0 < wlen) {
1406
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1407
+ }
1408
+ return Qnil;
1409
+ }
1410
+ }
1411
+ each_leaf(doc, self);
1412
+ if (0 < wlen) {
1413
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1414
+ }
1473
1415
  }
1474
1416
  return Qnil;
1475
1417
  }
@@ -1480,22 +1422,21 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1480
1422
  * path or a relative path.
1481
1423
  * @param [String] path path to the location to move to
1482
1424
  * @example
1483
- * Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=> "/one/2"
1425
+ * Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=>
1426
+ * "/one/2"
1484
1427
  */
1485
- static VALUE
1486
- doc_move(VALUE self, VALUE str) {
1487
- Doc doc = self_doc(self);
1488
- const char *path;
1489
- int loc;
1428
+ static VALUE doc_move(VALUE self, VALUE str) {
1429
+ Doc doc = self_doc(self);
1430
+ const char *path;
1431
+ int loc;
1490
1432
 
1491
- Check_Type(str, T_STRING);
1492
1433
  path = StringValuePtr(str);
1493
1434
  if ('/' == *path) {
1494
- doc->where = doc->where_path;
1495
- path++;
1435
+ doc->where = doc->where_path;
1436
+ path++;
1496
1437
  }
1497
1438
  if (0 != (loc = move_step(doc, path, 1))) {
1498
- rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
1439
+ rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
1499
1440
  }
1500
1441
  return Qnil;
1501
1442
  }
@@ -1506,7 +1447,8 @@ doc_move(VALUE self, VALUE str) {
1506
1447
  * identified location of the JSON document as the root. The parameter passed
1507
1448
  * to the block on yield is the Doc instance after moving to the child
1508
1449
  * location.
1509
- * @param [String] path if provided it identified the top of the branch to process the chilren of
1450
+ * @param [String] path if provided it identified the top of the branch to
1451
+ * process the children of
1510
1452
  * @yieldparam [Doc] Doc at the child location
1511
1453
  * @example
1512
1454
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1516,46 +1458,50 @@ doc_move(VALUE self, VALUE str) {
1516
1458
  * }
1517
1459
  * #=> ["/2/1", "/2/2"]
1518
1460
  */
1519
- static VALUE
1520
- doc_each_child(int argc, VALUE *argv, VALUE self) {
1461
+ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1521
1462
  if (rb_block_given_p()) {
1522
- Leaf save_path[MAX_STACK];
1523
- Doc doc = self_doc(self);
1524
- const char *path = 0;
1525
- size_t wlen;
1526
-
1527
- wlen = doc->where - doc->where_path;
1528
- if (0 < wlen) {
1529
- memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1530
- }
1531
- if (1 <= argc) {
1532
- Check_Type(*argv, T_STRING);
1533
- path = StringValuePtr(*argv);
1534
- if ('/' == *path) {
1535
- doc->where = doc->where_path;
1536
- path++;
1537
- }
1538
- if (0 != move_step(doc, path, 1)) {
1539
- if (0 < wlen) {
1540
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1541
- }
1542
- return Qnil;
1543
- }
1544
- }
1545
- if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
1546
- Leaf first = (*doc->where)->elements->next;
1547
- Leaf e = first;
1548
-
1549
- doc->where++;
1550
- do {
1551
- *doc->where = e;
1552
- rb_yield(self);
1553
- e = e->next;
1554
- } while (e != first);
1555
- }
1556
- if (0 < wlen) {
1557
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1558
- }
1463
+ Leaf save_path[MAX_STACK];
1464
+ Doc doc = self_doc(self);
1465
+ const char *path = 0;
1466
+ size_t wlen;
1467
+ Leaf *where_orig = doc->where;
1468
+
1469
+ wlen = doc->where - doc->where_path;
1470
+ if (0 < wlen) {
1471
+ memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1472
+ }
1473
+ if (1 <= argc) {
1474
+ path = StringValuePtr(*argv);
1475
+ if ('/' == *path) {
1476
+ doc->where = doc->where_path;
1477
+ path++;
1478
+ }
1479
+ if (0 != move_step(doc, path, 1)) {
1480
+ if (0 < wlen) {
1481
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1482
+ }
1483
+ doc->where = where_orig;
1484
+ return Qnil;
1485
+ }
1486
+ }
1487
+ if (NULL == doc->where || NULL == *doc->where) {
1488
+ return Qnil;
1489
+ }
1490
+ if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
1491
+ Leaf first = (*doc->where)->elements->next;
1492
+ Leaf e = first;
1493
+
1494
+ doc->where++;
1495
+ do {
1496
+ *doc->where = e;
1497
+ rb_yield(self);
1498
+ e = e->next;
1499
+ } while (e != first);
1500
+ }
1501
+ if (0 < wlen) {
1502
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1503
+ }
1504
+ doc->where = where_orig;
1559
1505
  }
1560
1506
  return Qnil;
1561
1507
  }
@@ -1566,7 +1512,8 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1566
1512
  * of the JSON document. The parameter passed to the block on yield is the
1567
1513
  * value of the leaf. Only those leaves below the element specified by the
1568
1514
  * path parameter are processed.
1569
- * @param [String] path if provided it identified the top of the branch to process the leaf values of
1515
+ * @param [String] path if provided it identified the top of the branch to
1516
+ * process the leaf values of
1570
1517
  * @yieldparam [Object] val each leaf value
1571
1518
  * @example
1572
1519
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1575,7 +1522,7 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1575
1522
  * result
1576
1523
  * }
1577
1524
  * #=> [3, 2, 1]
1578
- *
1525
+ *
1579
1526
  * Oj::Doc.open('[3,[2,1]]') { |doc|
1580
1527
  * result = []
1581
1528
  * doc.each_value('/2') { |v| result << v }
@@ -1583,20 +1530,18 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1583
1530
  * }
1584
1531
  * #=> [2, 1]
1585
1532
  */
1586
- static VALUE
1587
- doc_each_value(int argc, VALUE *argv, VALUE self) {
1533
+ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
1588
1534
  if (rb_block_given_p()) {
1589
- Doc doc = self_doc(self);
1590
- const char *path = 0;
1591
- Leaf leaf;
1592
-
1593
- if (1 <= argc) {
1594
- Check_Type(*argv, T_STRING);
1595
- path = StringValuePtr(*argv);
1596
- }
1597
- if (0 != (leaf = get_doc_leaf(doc, path))) {
1598
- each_value(doc, leaf);
1599
- }
1535
+ Doc doc = self_doc(self);
1536
+ const char *path = 0;
1537
+ Leaf leaf;
1538
+
1539
+ if (1 <= argc) {
1540
+ path = StringValuePtr(*argv);
1541
+ }
1542
+ if (0 != (leaf = get_doc_leaf(doc, path))) {
1543
+ each_value(doc, leaf);
1544
+ }
1600
1545
  }
1601
1546
  return Qnil;
1602
1547
  }
@@ -1605,52 +1550,48 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
1605
1550
  *
1606
1551
  * Dumps the document or nodes to a new JSON document. It uses the default
1607
1552
  * options for generating the JSON.
1608
- * @param path [String] if provided it identified the top of the branch to dump to JSON
1609
- * @param filename [String] if provided it is the filename to write the output to
1553
+ * @param path [String] if provided it identified the top of the branch to
1554
+ * dump to JSON
1555
+ * @param filename [String] if provided it is the filename to write the output
1556
+ * to
1610
1557
  * @example
1611
1558
  * Oj::Doc.open('[3,[2,1]]') { |doc|
1612
1559
  * doc.dump('/2')
1613
1560
  * }
1614
1561
  * #=> "[2,1]"
1615
1562
  */
1616
- static VALUE
1617
- doc_dump(int argc, VALUE *argv, VALUE self) {
1618
- Doc doc = self_doc(self);
1619
- Leaf leaf;
1620
- const char *path = 0;
1621
- const char *filename = 0;
1563
+ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
1564
+ Doc doc = self_doc(self);
1565
+ Leaf leaf;
1566
+ const char *path = 0;
1567
+ const char *filename = 0;
1622
1568
 
1623
1569
  if (1 <= argc) {
1624
- if (Qnil != *argv) {
1625
- Check_Type(*argv, T_STRING);
1626
- path = StringValuePtr(*argv);
1627
- }
1628
- if (2 <= argc) {
1629
- Check_Type(argv[1], T_STRING);
1630
- filename = StringValuePtr(argv[1]);
1631
- }
1570
+ if (Qnil != *argv) {
1571
+ path = StringValuePtr(*argv);
1572
+ }
1573
+ if (2 <= argc) {
1574
+ filename = StringValuePtr(argv[1]);
1575
+ }
1632
1576
  }
1633
1577
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1634
- volatile VALUE rjson;
1635
-
1636
- if (0 == filename) {
1637
- char buf[4096];
1638
- struct _out out;
1639
-
1640
- out.buf = buf;
1641
- out.end = buf + sizeof(buf) - 10;
1642
- out.allocated = false;
1643
- out.omit_nil = oj_default_options.dump_opts.omit_nil;
1644
- oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
1645
- rjson = rb_str_new2(out.buf);
1646
- if (out.allocated) {
1647
- xfree(out.buf);
1648
- }
1649
- } else {
1650
- oj_write_leaf_to_file(leaf, filename, &oj_default_options);
1651
- rjson = Qnil;
1652
- }
1653
- return rjson;
1578
+ volatile VALUE rjson;
1579
+
1580
+ if (0 == filename) {
1581
+ struct _out out;
1582
+
1583
+ oj_out_init(&out);
1584
+
1585
+ out.omit_nil = oj_default_options.dump_opts.omit_nil;
1586
+ oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
1587
+ rjson = rb_str_new2(out.buf);
1588
+
1589
+ oj_out_free(&out);
1590
+ } else {
1591
+ oj_write_leaf_to_file(leaf, filename, &oj_default_options);
1592
+ rjson = Qnil;
1593
+ }
1594
+ return rjson;
1654
1595
  }
1655
1596
  return Qnil;
1656
1597
  }
@@ -1663,9 +1604,10 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
1663
1604
  * @example
1664
1605
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1665
1606
  */
1666
- static VALUE
1667
- doc_size(VALUE self) {
1668
- return ULONG2NUM(((Doc)DATA_PTR(self))->size);
1607
+ static VALUE doc_size(VALUE self) {
1608
+ Doc d;
1609
+ TypedData_Get_Struct(self, struct _doc, &oj_doc_type, d);
1610
+ return ULONG2NUM(d->size);
1669
1611
  }
1670
1612
 
1671
1613
  /* @overload close() => nil
@@ -1677,16 +1619,13 @@ doc_size(VALUE self) {
1677
1619
  * doc.size() #=> 4
1678
1620
  * doc.close()
1679
1621
  */
1680
- static VALUE
1681
- doc_close(VALUE self) {
1682
- Doc doc = self_doc(self);
1622
+ static VALUE doc_close(VALUE self) {
1623
+ Doc doc = self_doc(self);
1683
1624
 
1684
1625
  rb_gc_unregister_address(&doc->self);
1685
- DATA_PTR(doc->self) = 0;
1626
+ DATA_PTR(doc->self) = NULL;
1686
1627
  if (0 != doc) {
1687
- xfree(doc->json);
1688
- doc_free(doc);
1689
- xfree(doc);
1628
+ doc_free(doc);
1690
1629
  }
1691
1630
  return Qnil;
1692
1631
  }
@@ -1695,8 +1634,7 @@ doc_close(VALUE self) {
1695
1634
  Oj = rb_define_module("Oj");
1696
1635
  #endif
1697
1636
 
1698
- static VALUE
1699
- doc_not_implemented(VALUE self) {
1637
+ static VALUE doc_not_implemented(VALUE self) {
1700
1638
  rb_raise(rb_eNotImpError, "Not implemented.");
1701
1639
  return Qnil;
1702
1640
  }
@@ -1708,21 +1646,21 @@ doc_not_implemented(VALUE self) {
1708
1646
  * extracted. Once the document is closed the document can not longer be
1709
1647
  * accessed. This allows the parsing and data extraction to be extremely fast
1710
1648
  * compared to other JSON parses.
1711
- *
1649
+ *
1712
1650
  * An Oj::Doc class is not created directly but the _open()_ class method is
1713
1651
  * used to open a document and the yield parameter to the block of the #open()
1714
1652
  * call is the Doc instance. The Doc instance can be moved across, up, and
1715
1653
  * down the JSON document. At each element the data associated with the
1716
1654
  * element can be extracted. It is also possible to just provide a path to the
1717
1655
  * data to be extracted and retrieve the data in that manner.
1718
- *
1656
+ *
1719
1657
  * For many of the methods a path is used to describe the location of an
1720
1658
  * element. Paths follow a subset of the XPath syntax. The slash ('/')
1721
1659
  * character is the separator. Each step in the path identifies the next
1722
1660
  * branch to take through the document. A JSON object will expect a key string
1723
1661
  * while an array will expect a positive index. A .. step indicates a move up
1724
1662
  * the JSON document.
1725
- *
1663
+ *
1726
1664
  * @example
1727
1665
  * json = %{[
1728
1666
  * {
@@ -1736,28 +1674,29 @@ doc_not_implemented(VALUE self) {
1736
1674
  * ]}
1737
1675
  * # move and get value
1738
1676
  * Oj::Doc.open(json) do |doc|
1739
- * doc.move('/1/two')
1740
- * # doc location is now at the 'two' element of the hash that is the first element of the array.
1741
- * doc.fetch()
1742
- * end
1677
+ * doc.move('/1/two')
1678
+ * # doc location is now at the 'two' element of the hash that is the first
1679
+ * element of the array. doc.fetch() end
1743
1680
  * #=> 2
1744
- *
1745
- * # Now try again using a path to Oj::Doc.fetch() directly and not using a block.
1746
- * doc = Oj::Doc.open(json)
1747
- * doc.fetch('/2/three') #=> 3
1748
- * doc.close()
1681
+ *
1682
+ * # Now try again using a path to Oj::Doc.fetch() directly and not using a
1683
+ * block. doc = Oj::Doc.open(json) doc.fetch('/2/three') #=> 3 doc.close()
1749
1684
  */
1750
- void
1751
- oj_init_doc() {
1685
+ void oj_init_doc(void) {
1752
1686
  oj_doc_class = rb_define_class_under(Oj, "Doc", rb_cObject);
1687
+ rb_gc_register_address(&oj_doc_class);
1688
+ rb_undef_alloc_func(oj_doc_class);
1753
1689
  rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
1754
1690
  rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
1755
1691
  rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
1756
- rb_define_method(oj_doc_class, "where?", doc_where, 0);
1692
+ rb_define_method(oj_doc_class, "where?", doc_where_q, 0);
1693
+ rb_define_method(oj_doc_class, "where", doc_where, 0);
1694
+ rb_define_method(oj_doc_class, "path", doc_path, 0);
1757
1695
  rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
1758
1696
  rb_define_method(oj_doc_class, "home", doc_home, 0);
1759
1697
  rb_define_method(oj_doc_class, "type", doc_type, -1);
1760
1698
  rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
1699
+ rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
1761
1700
  rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
1762
1701
  rb_define_method(oj_doc_class, "move", doc_move, 1);
1763
1702
  rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);