oj 3.10.6 → 3.12.1

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