oj 0.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of oj might be problematic. Click here for more details.
- data/LICENSE +27 -0
- data/README.md +58 -0
- data/ext/oj/dump.c +484 -0
- data/ext/oj/extconf.rb +7 -0
- data/ext/oj/load.c +426 -0
- data/ext/oj/oj.c +157 -0
- data/ext/oj/oj.h +98 -0
- data/lib/oj.rb +36 -0
- data/lib/oj/version.rb +5 -0
- metadata +61 -0
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2011, Peter Ohler
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
- Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
- Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
- Neither the name of Peter Ohler nor the names of its contributors may be
|
15
|
+
used to endorse or promote products derived from this software without
|
16
|
+
specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Oj gem
|
2
|
+
A fast JSON parser and Object marshaller as a Ruby gem.
|
3
|
+
|
4
|
+
## <a name="installation">Installation</a>
|
5
|
+
gem install oj
|
6
|
+
|
7
|
+
## <a name="source">Source</a>
|
8
|
+
|
9
|
+
*GitHub* *repo*: https://github.com/ohler55/oj
|
10
|
+
|
11
|
+
*RubyGems* *repo*: https://rubygems.org/gems/oj
|
12
|
+
|
13
|
+
## <a name="build_status">Build Status</a>
|
14
|
+
|
15
|
+
[![Build Status](http://travis-ci.org/ohler55/oj.png)](http://travis-ci.org/ohler55/oj)
|
16
|
+
|
17
|
+
## <a name="links">Links of Interest</a>
|
18
|
+
|
19
|
+
## <a name="release">Release Notes</a>
|
20
|
+
|
21
|
+
### Release 0.5
|
22
|
+
|
23
|
+
This is the first release sith a version of 0.5 indicating it is only half
|
24
|
+
done. Basic load() and dump() is supported for Hash, Array, NilClass,
|
25
|
+
TrueClass, FalseClass, Fixnum, Float, Symbol, and String Objects.
|
26
|
+
|
27
|
+
## <a name="description">Description</a>
|
28
|
+
|
29
|
+
Optimized JSON (Oj), as the name implies was written to provide speed
|
30
|
+
optimized JSON handling. It was designed as a faster alternative to Yajl and
|
31
|
+
other the common Ruby JSON parsers. So far is has achieved that at about 2
|
32
|
+
time faster than Yajl for parsing and 3 or more times faster writing JSON.
|
33
|
+
|
34
|
+
Coming soon: As an Object marshaller with support for circular references.
|
35
|
+
|
36
|
+
Coming soon: A SAX like JSON stream parser.
|
37
|
+
|
38
|
+
Oj is compatible with Ruby 1.8.7, 1.9.2, 1.9.3, JRuby, and RBX.
|
39
|
+
|
40
|
+
### Simple JSON Writing and Parsing:
|
41
|
+
|
42
|
+
require 'oj'
|
43
|
+
|
44
|
+
h = { 'one' => 1, 'array' => [ true, false ] }
|
45
|
+
json = Oj.dump(h)
|
46
|
+
|
47
|
+
# json =
|
48
|
+
# {
|
49
|
+
# "one":1,
|
50
|
+
# "array":[
|
51
|
+
# true,
|
52
|
+
# false
|
53
|
+
# ]
|
54
|
+
# }
|
55
|
+
|
56
|
+
h2 = Oj.parse(json)
|
57
|
+
puts "Same? #{h == h2}"
|
58
|
+
# true
|
data/ext/oj/dump.c
ADDED
@@ -0,0 +1,484 @@
|
|
1
|
+
/* dump.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
|
+
*/
|
30
|
+
|
31
|
+
#include <stdlib.h>
|
32
|
+
#include <errno.h>
|
33
|
+
#include <time.h>
|
34
|
+
#include <stdio.h>
|
35
|
+
#include <string.h>
|
36
|
+
|
37
|
+
#include "ruby.h"
|
38
|
+
#include "oj.h"
|
39
|
+
|
40
|
+
typedef unsigned long ulong;
|
41
|
+
|
42
|
+
typedef struct _Str {
|
43
|
+
const char *str;
|
44
|
+
size_t len;
|
45
|
+
} *Str;
|
46
|
+
|
47
|
+
typedef struct _Element {
|
48
|
+
struct _Str clas;
|
49
|
+
struct _Str attr;
|
50
|
+
unsigned long id;
|
51
|
+
int indent; // < 0 indicates no \n
|
52
|
+
int closed;
|
53
|
+
char type;
|
54
|
+
} *Element;
|
55
|
+
|
56
|
+
typedef struct _Out {
|
57
|
+
void (*w_start)(struct _Out *out, Element e);
|
58
|
+
void (*w_end)(struct _Out *out, Element e);
|
59
|
+
void (*w_time)(struct _Out *out, VALUE obj);
|
60
|
+
char *buf;
|
61
|
+
char *end;
|
62
|
+
char *cur;
|
63
|
+
// Cache8 circ_cache;
|
64
|
+
// unsigned long circ_cnt;
|
65
|
+
int indent;
|
66
|
+
int depth; // used by dumpHash
|
67
|
+
Options opts;
|
68
|
+
VALUE obj;
|
69
|
+
} *Out;
|
70
|
+
|
71
|
+
static void dump_obj_to_json(VALUE obj, Options copts, Out out);
|
72
|
+
static void dump_val(VALUE obj, int depth, Out out);
|
73
|
+
static void dump_nil(Out out);
|
74
|
+
static void dump_true(Out out);
|
75
|
+
static void dump_false(Out out);
|
76
|
+
static void dump_fixnum(VALUE obj, Out out);
|
77
|
+
static void dump_float(VALUE obj, Out out);
|
78
|
+
static void dump_cstr(const char *str, int cnt, Out out);
|
79
|
+
static void dump_hex(u_char c, Out out);
|
80
|
+
static void dump_str(VALUE obj, Out out);
|
81
|
+
static void dump_sym(VALUE obj, Out out);
|
82
|
+
static void dump_array(VALUE obj, int depth, Out out);
|
83
|
+
static void dump_hash(VALUE obj, int depth, Out out);
|
84
|
+
|
85
|
+
static void grow(Out out, size_t len);
|
86
|
+
static int is_json_friendly(const u_char *str, int len);
|
87
|
+
static int json_friendly_size(const u_char *str, int len);
|
88
|
+
|
89
|
+
|
90
|
+
static char json_friendly_chars[256] = "\
|
91
|
+
uuuuuuuuxxxuxxuuuuuuuuuuuuuuuuuu\
|
92
|
+
ooxooooooooooooxoooooooooooooooo\
|
93
|
+
ooooooooooooooooooooooooooooxooo\
|
94
|
+
ooooooooooooooooooooooooooooooou\
|
95
|
+
uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\
|
96
|
+
uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\
|
97
|
+
uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\
|
98
|
+
uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu";
|
99
|
+
|
100
|
+
inline static int
|
101
|
+
is_json_friendly(const u_char *str, int len) {
|
102
|
+
for (; 0 < len; str++, len--) {
|
103
|
+
if ('o' != json_friendly_chars[*str]) {
|
104
|
+
return 0;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
return 1;
|
108
|
+
}
|
109
|
+
|
110
|
+
inline static int
|
111
|
+
json_friendly_size(const u_char *str, int len) {
|
112
|
+
int cnt = 0;
|
113
|
+
|
114
|
+
for (; 0 < len; str++, len--) {
|
115
|
+
switch (json_friendly_chars[*str]) {
|
116
|
+
case 'o': cnt++; break;
|
117
|
+
case 'x': cnt += 2; break;
|
118
|
+
case 'u': cnt += 6; break;
|
119
|
+
default: break;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
return cnt;
|
123
|
+
}
|
124
|
+
|
125
|
+
inline static void
|
126
|
+
fill_indent(Out out, int cnt) {
|
127
|
+
cnt *= out->indent;
|
128
|
+
if (0 <= cnt) {
|
129
|
+
*out->cur++ = '\n';
|
130
|
+
for (; 0 < cnt; cnt--) {
|
131
|
+
*out->cur++ = ' ';
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
static void
|
137
|
+
grow(Out out, size_t len) {
|
138
|
+
size_t size = out->end - out->buf;
|
139
|
+
long pos = out->cur - out->buf;
|
140
|
+
char *buf;
|
141
|
+
|
142
|
+
size *= 2;
|
143
|
+
if (size <= len * 2 + pos) {
|
144
|
+
size += len;
|
145
|
+
}
|
146
|
+
if (0 == (buf = (char*)realloc(out->buf, size + 10))) { // 1 extra for terminator character plus extra (paranoid)
|
147
|
+
rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]\n", ENOSPC, strerror(ENOSPC));
|
148
|
+
}
|
149
|
+
out->buf = buf;
|
150
|
+
out->end = buf + size;
|
151
|
+
out->cur = out->buf + pos;
|
152
|
+
}
|
153
|
+
|
154
|
+
inline static void
|
155
|
+
dump_hex(u_char c, Out out) {
|
156
|
+
u_char d = c & 0xF0;
|
157
|
+
|
158
|
+
if (9 < d) {
|
159
|
+
*out->cur++ = (d - 10) + 'a';
|
160
|
+
} else {
|
161
|
+
*out->cur++ = d + '0';
|
162
|
+
}
|
163
|
+
d = c & 0x0F;
|
164
|
+
if (9 < d) {
|
165
|
+
*out->cur++ = (d - 10) + 'a';
|
166
|
+
} else {
|
167
|
+
*out->cur++ = d + '0';
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
static void
|
172
|
+
dump_nil(Out out) {
|
173
|
+
size_t size = 4;
|
174
|
+
|
175
|
+
if (out->end - out->cur <= (long)size) {
|
176
|
+
grow(out, size);
|
177
|
+
}
|
178
|
+
*out->cur++ = 'n';
|
179
|
+
*out->cur++ = 'u';
|
180
|
+
*out->cur++ = 'l';
|
181
|
+
*out->cur++ = 'l';
|
182
|
+
*out->cur = '\0';
|
183
|
+
}
|
184
|
+
|
185
|
+
static void
|
186
|
+
dump_true(Out out) {
|
187
|
+
size_t size = 4;
|
188
|
+
|
189
|
+
if (out->end - out->cur <= (long)size) {
|
190
|
+
grow(out, size);
|
191
|
+
}
|
192
|
+
*out->cur++ = 't';
|
193
|
+
*out->cur++ = 'r';
|
194
|
+
*out->cur++ = 'u';
|
195
|
+
*out->cur++ = 'e';
|
196
|
+
*out->cur = '\0';
|
197
|
+
}
|
198
|
+
|
199
|
+
static void
|
200
|
+
dump_false(Out out) {
|
201
|
+
size_t size = 5;
|
202
|
+
|
203
|
+
if (out->end - out->cur <= (long)size) {
|
204
|
+
grow(out, size);
|
205
|
+
}
|
206
|
+
*out->cur++ = 'f';
|
207
|
+
*out->cur++ = 'a';
|
208
|
+
*out->cur++ = 'l';
|
209
|
+
*out->cur++ = 's';
|
210
|
+
*out->cur++ = 'e';
|
211
|
+
*out->cur = '\0';
|
212
|
+
}
|
213
|
+
|
214
|
+
static void
|
215
|
+
dump_fixnum(VALUE obj, Out out) {
|
216
|
+
char buf[32];
|
217
|
+
char *b = buf + sizeof(buf) - 1;
|
218
|
+
long num = NUM2LONG(obj);
|
219
|
+
int neg = 0;
|
220
|
+
|
221
|
+
if (0 > num) {
|
222
|
+
neg = 1;
|
223
|
+
num = -num;
|
224
|
+
}
|
225
|
+
*b-- = '\0';
|
226
|
+
if (0 < num) {
|
227
|
+
for (; 0 < num; num /= 10, b--) {
|
228
|
+
*b = (num % 10) + '0';
|
229
|
+
}
|
230
|
+
if (neg) {
|
231
|
+
*b = '-';
|
232
|
+
} else {
|
233
|
+
b++;
|
234
|
+
}
|
235
|
+
} else {
|
236
|
+
*b = '0';
|
237
|
+
}
|
238
|
+
if (out->end - out->cur <= (long)(sizeof(buf) - (b - buf))) {
|
239
|
+
grow(out, sizeof(buf) - (b - buf));
|
240
|
+
}
|
241
|
+
for (; '\0' != *b; b++) {
|
242
|
+
*out->cur++ = *b;
|
243
|
+
}
|
244
|
+
*out->cur = '\0';
|
245
|
+
}
|
246
|
+
|
247
|
+
static void
|
248
|
+
dump_float(VALUE obj, Out out) {
|
249
|
+
char buf[64];
|
250
|
+
char *b;
|
251
|
+
int cnt = sprintf(buf, "%0.16g", RFLOAT_VALUE(obj)); // used sprintf due to bug in snprintf
|
252
|
+
|
253
|
+
if (out->end - out->cur <= (long)cnt) {
|
254
|
+
grow(out, cnt);
|
255
|
+
}
|
256
|
+
for (b = buf; '\0' != *b; b++) {
|
257
|
+
*out->cur++ = *b;
|
258
|
+
}
|
259
|
+
*out->cur = '\0';
|
260
|
+
}
|
261
|
+
|
262
|
+
static void
|
263
|
+
dump_cstr(const char *str, int cnt, Out out) {
|
264
|
+
int size = json_friendly_size((u_char*)str, cnt);
|
265
|
+
|
266
|
+
if (cnt == size) {
|
267
|
+
cnt += 2;
|
268
|
+
if (out->end - out->cur <= (long)cnt) {
|
269
|
+
grow(out, cnt);
|
270
|
+
}
|
271
|
+
*out->cur++ = '"';
|
272
|
+
for (; '\0' != *str; str++) {
|
273
|
+
*out->cur++ = *str;
|
274
|
+
}
|
275
|
+
*out->cur++ = '"';
|
276
|
+
} else {
|
277
|
+
size += 2;
|
278
|
+
if (out->end - out->cur <= (long)size) {
|
279
|
+
grow(out, size);
|
280
|
+
}
|
281
|
+
*out->cur++ = '"';
|
282
|
+
for (; 0 < cnt; cnt--, str++) {
|
283
|
+
switch (json_friendly_chars[(u_char)*str]) {
|
284
|
+
case 'o':
|
285
|
+
*out->cur++ = *str;
|
286
|
+
break;
|
287
|
+
case 'x':
|
288
|
+
*out->cur++ = '\\';
|
289
|
+
switch (*str) {
|
290
|
+
case '\b': *out->cur++ = 'b'; break;
|
291
|
+
case '\t': *out->cur++ = 't'; break;
|
292
|
+
case '\n': *out->cur++ = 'n'; break;
|
293
|
+
case '\f': *out->cur++ = 'f'; break;
|
294
|
+
case '\r': *out->cur++ = 'r'; break;
|
295
|
+
default: *out->cur++ = *str; break;
|
296
|
+
}
|
297
|
+
break;
|
298
|
+
case 'u':
|
299
|
+
*out->cur++ = '\\';
|
300
|
+
*out->cur++ = 'u';
|
301
|
+
if ((u_char)*str <= 0x7F) {
|
302
|
+
*out->cur++ = '0';
|
303
|
+
*out->cur++ = '0';
|
304
|
+
dump_hex((u_char)*str, out);
|
305
|
+
} else { // continuation?
|
306
|
+
// TBD lead with \u00 . grab next char?
|
307
|
+
*out->cur++ = '0';
|
308
|
+
*out->cur++ = '0';
|
309
|
+
dump_hex((u_char)*str, out);
|
310
|
+
}
|
311
|
+
break;
|
312
|
+
default:
|
313
|
+
break; // ignore, should never happen if the table is correct
|
314
|
+
}
|
315
|
+
}
|
316
|
+
*out->cur++ = '"';
|
317
|
+
}
|
318
|
+
*out->cur = '\0';
|
319
|
+
}
|
320
|
+
|
321
|
+
static void
|
322
|
+
dump_str(VALUE obj, Out out) {
|
323
|
+
dump_cstr(StringValuePtr(obj), (int)RSTRING_LEN(obj), out);
|
324
|
+
}
|
325
|
+
|
326
|
+
static void
|
327
|
+
dump_sym(VALUE obj, Out out) {
|
328
|
+
const char *sym = rb_id2name(SYM2ID(obj));
|
329
|
+
|
330
|
+
dump_cstr(sym, (int)strlen(sym), out);
|
331
|
+
}
|
332
|
+
|
333
|
+
static void
|
334
|
+
dump_array(VALUE a, int depth, Out out) {
|
335
|
+
VALUE *np = RARRAY_PTR(a);
|
336
|
+
size_t size = 2;
|
337
|
+
int cnt = (int)RARRAY_LEN(a);
|
338
|
+
int d2 = depth + 1;
|
339
|
+
|
340
|
+
if (out->end - out->cur <= (long)size) {
|
341
|
+
grow(out, size);
|
342
|
+
}
|
343
|
+
*out->cur++ = '[';
|
344
|
+
if (0 == cnt) {
|
345
|
+
*out->cur++ = ']';
|
346
|
+
} else {
|
347
|
+
size = d2 * out->indent + 2;
|
348
|
+
for (; 0 < cnt; cnt--, np++) {
|
349
|
+
if (out->end - out->cur <= (long)size) {
|
350
|
+
grow(out, size);
|
351
|
+
}
|
352
|
+
fill_indent(out, d2);
|
353
|
+
dump_val(*np, d2, out);
|
354
|
+
if (1 < cnt) {
|
355
|
+
// TBD check size?
|
356
|
+
*out->cur++ = ',';
|
357
|
+
}
|
358
|
+
}
|
359
|
+
size = depth * out->indent + 1;
|
360
|
+
if (out->end - out->cur <= (long)size) {
|
361
|
+
grow(out, size);
|
362
|
+
}
|
363
|
+
fill_indent(out, depth);
|
364
|
+
*out->cur++ = ']';
|
365
|
+
}
|
366
|
+
*out->cur = '\0';
|
367
|
+
}
|
368
|
+
|
369
|
+
static int
|
370
|
+
hash_cb(VALUE key, VALUE value, Out out) {
|
371
|
+
int depth = out->depth;
|
372
|
+
size_t size = depth * out->indent + 1;
|
373
|
+
|
374
|
+
if (out->end - out->cur <= (long)size) {
|
375
|
+
grow(out, size);
|
376
|
+
}
|
377
|
+
fill_indent(out, depth);
|
378
|
+
dump_str(key, out);
|
379
|
+
*out->cur++ = ':';
|
380
|
+
dump_val(value, depth, out);
|
381
|
+
out->depth = depth;
|
382
|
+
*out->cur++ = ',';
|
383
|
+
|
384
|
+
return ST_CONTINUE;
|
385
|
+
}
|
386
|
+
|
387
|
+
static void
|
388
|
+
dump_hash(VALUE obj, int depth, Out out) {
|
389
|
+
int cnt = (int)RHASH_SIZE(obj);
|
390
|
+
|
391
|
+
*out->cur++ = '{';
|
392
|
+
if (0 == cnt) {
|
393
|
+
*out->cur++ = '}';
|
394
|
+
} else {
|
395
|
+
size_t size = depth * out->indent + 2;
|
396
|
+
|
397
|
+
out->depth = depth + 1;
|
398
|
+
rb_hash_foreach(obj, hash_cb, (VALUE)out);
|
399
|
+
out->cur--; // backup to overwrite last comma
|
400
|
+
if (out->end - out->cur <= (long)size) {
|
401
|
+
grow(out, size);
|
402
|
+
}
|
403
|
+
fill_indent(out, depth);
|
404
|
+
*out->cur++ = '}';
|
405
|
+
}
|
406
|
+
*out->cur = '\0';
|
407
|
+
}
|
408
|
+
|
409
|
+
static void
|
410
|
+
dump_val(VALUE obj, int depth, Out out) {
|
411
|
+
switch (rb_type(obj)) {
|
412
|
+
case T_NIL: dump_nil(out); break;
|
413
|
+
case T_TRUE: dump_true(out); break;
|
414
|
+
case T_FALSE: dump_false(out); break;
|
415
|
+
case T_FIXNUM: dump_fixnum(obj, out); break;
|
416
|
+
case T_FLOAT: dump_float(obj, out); break;
|
417
|
+
case T_BIGNUM: break; // TBD
|
418
|
+
case T_STRING: dump_str(obj, out); break;
|
419
|
+
case T_SYMBOL: dump_sym(obj, out); break;
|
420
|
+
case T_ARRAY: dump_array(obj, depth, out); break;
|
421
|
+
case T_HASH: dump_hash(obj, depth, out); break;
|
422
|
+
case T_OBJECT:
|
423
|
+
case T_REGEXP:
|
424
|
+
case T_CLASS:
|
425
|
+
case T_DATA: // for Time
|
426
|
+
// TBD
|
427
|
+
rb_raise(rb_eNotImpError, "Failed to dump '%s' Object (%02x)\n",
|
428
|
+
rb_class2name(rb_obj_class(obj)), rb_type(obj));
|
429
|
+
break;
|
430
|
+
default:
|
431
|
+
rb_raise(rb_eNotImpError, "Failed to dump '%s' Object (%02x)\n",
|
432
|
+
rb_class2name(rb_obj_class(obj)), rb_type(obj));
|
433
|
+
break;
|
434
|
+
}
|
435
|
+
}
|
436
|
+
|
437
|
+
static void
|
438
|
+
dump_obj_to_json(VALUE obj, Options copts, Out out) {
|
439
|
+
out->buf = (char*)malloc(65336);
|
440
|
+
out->end = out->buf + 65325; // 1 less than end plus extra for possible errors
|
441
|
+
out->cur = out->buf;
|
442
|
+
// out->circ_cache = 0;
|
443
|
+
// out->circ_cnt = 0;
|
444
|
+
out->opts = copts;
|
445
|
+
out->obj = obj;
|
446
|
+
/* if (Yes == copts->circular) {
|
447
|
+
ox_cache8_new(&out->circ_cache);
|
448
|
+
}*/
|
449
|
+
out->indent = copts->indent;
|
450
|
+
out->indent = 2; // TBD
|
451
|
+
dump_val(obj, 0, out);
|
452
|
+
|
453
|
+
/* if (Yes == copts->circular) {
|
454
|
+
ox_cache8_delete(out->circ_cache);
|
455
|
+
}*/
|
456
|
+
}
|
457
|
+
|
458
|
+
char*
|
459
|
+
write_obj_to_str(VALUE obj, Options copts) {
|
460
|
+
struct _Out out;
|
461
|
+
|
462
|
+
dump_obj_to_json(obj, copts, &out);
|
463
|
+
|
464
|
+
return out.buf;
|
465
|
+
}
|
466
|
+
|
467
|
+
void
|
468
|
+
write_obj_to_file(VALUE obj, const char *path, Options copts) {
|
469
|
+
struct _Out out;
|
470
|
+
size_t size;
|
471
|
+
FILE *f;
|
472
|
+
|
473
|
+
dump_obj_to_json(obj, copts, &out);
|
474
|
+
size = out.cur - out.buf;
|
475
|
+
if (0 == (f = fopen(path, "w"))) {
|
476
|
+
rb_raise(rb_eIOError, "%s\n", strerror(errno));
|
477
|
+
}
|
478
|
+
if (size != fwrite(out.buf, 1, size, f)) {
|
479
|
+
int err = ferror(f);
|
480
|
+
rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
|
481
|
+
}
|
482
|
+
free(out.buf);
|
483
|
+
fclose(f);
|
484
|
+
}
|
data/ext/oj/extconf.rb
ADDED
data/ext/oj/load.c
ADDED
@@ -0,0 +1,426 @@
|
|
1
|
+
/* load.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
|
+
*/
|
30
|
+
|
31
|
+
#include <stdlib.h>
|
32
|
+
#include <stdio.h>
|
33
|
+
#include <string.h>
|
34
|
+
#include <math.h>
|
35
|
+
|
36
|
+
#include "ruby.h"
|
37
|
+
#include "oj.h"
|
38
|
+
|
39
|
+
typedef struct _ParseInfo {
|
40
|
+
char *str; /* buffer being read from */
|
41
|
+
char *s; /* current position in buffer */
|
42
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
43
|
+
rb_encoding *encoding;
|
44
|
+
#else
|
45
|
+
void *encoding;
|
46
|
+
#endif
|
47
|
+
int trace;
|
48
|
+
} *ParseInfo;
|
49
|
+
|
50
|
+
static VALUE read_next(ParseInfo pi);
|
51
|
+
static VALUE read_obj(ParseInfo pi);
|
52
|
+
static VALUE read_array(ParseInfo pi);
|
53
|
+
static VALUE read_str(ParseInfo pi);
|
54
|
+
static VALUE read_num(ParseInfo pi);
|
55
|
+
static VALUE read_true(ParseInfo pi);
|
56
|
+
static VALUE read_false(ParseInfo pi);
|
57
|
+
static VALUE read_nil(ParseInfo pi);
|
58
|
+
static void next_non_white(ParseInfo pi);
|
59
|
+
static char* read_quoted_value(ParseInfo pi);
|
60
|
+
|
61
|
+
|
62
|
+
/* This XML parser is a single pass, destructive, callback parser. It is a
|
63
|
+
* single pass parse since it only make one pass over the characters in the
|
64
|
+
* XML document string. It is destructive because it re-uses the content of
|
65
|
+
* the string for values in the callback and places \0 characters at various
|
66
|
+
* places to mark the end of tokens and strings. It is a callback parser like
|
67
|
+
* a SAX parser because it uses callback when document elements are
|
68
|
+
* encountered.
|
69
|
+
*
|
70
|
+
* Parsing is very tolerant. Lack of headers and even mispelled element
|
71
|
+
* endings are passed over without raising an error. A best attempt is made in
|
72
|
+
* all cases to parse the string.
|
73
|
+
*/
|
74
|
+
|
75
|
+
inline static void
|
76
|
+
next_non_white(ParseInfo pi) {
|
77
|
+
for (; 1; pi->s++) {
|
78
|
+
switch(*pi->s) {
|
79
|
+
case ' ':
|
80
|
+
case '\t':
|
81
|
+
case '\f':
|
82
|
+
case '\n':
|
83
|
+
case '\r':
|
84
|
+
break;
|
85
|
+
default:
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
inline static void
|
92
|
+
next_white(ParseInfo pi) {
|
93
|
+
for (; 1; pi->s++) {
|
94
|
+
switch(*pi->s) {
|
95
|
+
case ' ':
|
96
|
+
case '\t':
|
97
|
+
case '\f':
|
98
|
+
case '\n':
|
99
|
+
case '\r':
|
100
|
+
case '\0':
|
101
|
+
return;
|
102
|
+
default:
|
103
|
+
break;
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
VALUE
|
109
|
+
parse(char *json, int trace) {
|
110
|
+
VALUE obj;
|
111
|
+
struct _ParseInfo pi;
|
112
|
+
|
113
|
+
if (0 == json) {
|
114
|
+
raise_error("Invalid arg, xml string can not be null", json, 0);
|
115
|
+
}
|
116
|
+
if (trace) {
|
117
|
+
printf("Parsing JSON:\n%s\n", json);
|
118
|
+
}
|
119
|
+
/* initialize parse info */
|
120
|
+
pi.str = json;
|
121
|
+
pi.s = json;
|
122
|
+
pi.encoding = 0;
|
123
|
+
pi.trace = trace;
|
124
|
+
if (Qundef == (obj = read_next(&pi))) {
|
125
|
+
raise_error("no object read", pi.str, pi.s);
|
126
|
+
}
|
127
|
+
next_non_white(&pi); // skip white space
|
128
|
+
if ('\0' != *pi.s) {
|
129
|
+
raise_error("invalid format, extra characters", pi.str, pi.s);
|
130
|
+
}
|
131
|
+
return obj;
|
132
|
+
}
|
133
|
+
|
134
|
+
static VALUE
|
135
|
+
read_next(ParseInfo pi) {
|
136
|
+
VALUE obj;
|
137
|
+
|
138
|
+
next_non_white(pi); // skip white space
|
139
|
+
switch (*pi->s) {
|
140
|
+
case '{':
|
141
|
+
obj = read_obj(pi);
|
142
|
+
break;
|
143
|
+
case '[':
|
144
|
+
obj = read_array(pi);
|
145
|
+
break;
|
146
|
+
case '"':
|
147
|
+
obj = read_str(pi);
|
148
|
+
break;
|
149
|
+
case '+':
|
150
|
+
case '-':
|
151
|
+
case '0':
|
152
|
+
case '1':
|
153
|
+
case '2':
|
154
|
+
case '3':
|
155
|
+
case '4':
|
156
|
+
case '5':
|
157
|
+
case '6':
|
158
|
+
case '7':
|
159
|
+
case '8':
|
160
|
+
case '9':
|
161
|
+
obj = read_num(pi);
|
162
|
+
break;
|
163
|
+
case 't':
|
164
|
+
obj = read_true(pi);
|
165
|
+
break;
|
166
|
+
case 'f':
|
167
|
+
obj = read_false(pi);
|
168
|
+
break;
|
169
|
+
case 'n':
|
170
|
+
obj = read_nil(pi);
|
171
|
+
break;
|
172
|
+
case '\0':
|
173
|
+
obj = Qundef;
|
174
|
+
break;
|
175
|
+
default:
|
176
|
+
obj = Qundef;
|
177
|
+
break;
|
178
|
+
}
|
179
|
+
return obj;
|
180
|
+
}
|
181
|
+
|
182
|
+
static VALUE
|
183
|
+
read_obj(ParseInfo pi) {
|
184
|
+
VALUE obj = Qundef;
|
185
|
+
VALUE key = Qundef;
|
186
|
+
VALUE val = Qundef;
|
187
|
+
|
188
|
+
pi->s++;
|
189
|
+
while (1) {
|
190
|
+
next_non_white(pi);
|
191
|
+
if ('"' != *pi->s || Qundef == (key = read_str(pi))) {
|
192
|
+
raise_error("unexpected character", pi->str, pi->s);
|
193
|
+
}
|
194
|
+
next_non_white(pi);
|
195
|
+
if (':' == *pi->s) {
|
196
|
+
pi->s++;
|
197
|
+
} else {
|
198
|
+
raise_error("invalid format, expected :", pi->str, pi->s);
|
199
|
+
}
|
200
|
+
if (Qundef == (val = read_next(pi))) {
|
201
|
+
raise_error("unexpected character", pi->str, pi->s);
|
202
|
+
}
|
203
|
+
if (Qundef == obj) {
|
204
|
+
obj = rb_hash_new();
|
205
|
+
}
|
206
|
+
rb_hash_aset(obj, key, val);
|
207
|
+
next_non_white(pi);
|
208
|
+
if ('}' == *pi->s) {
|
209
|
+
pi->s++;
|
210
|
+
break;
|
211
|
+
} else if (',' == *pi->s) {
|
212
|
+
pi->s++;
|
213
|
+
} else {
|
214
|
+
raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
return obj;
|
218
|
+
}
|
219
|
+
|
220
|
+
static VALUE
|
221
|
+
read_array(ParseInfo pi) {
|
222
|
+
VALUE a = rb_ary_new();
|
223
|
+
VALUE e;
|
224
|
+
|
225
|
+
pi->s++;
|
226
|
+
while (1) {
|
227
|
+
if (Qundef == (e = read_next(pi))) {
|
228
|
+
raise_error("unexpected character", pi->str, pi->s);
|
229
|
+
}
|
230
|
+
rb_ary_push(a, e);
|
231
|
+
next_non_white(pi); // skip white space
|
232
|
+
if (',' == *pi->s) {
|
233
|
+
pi->s++;
|
234
|
+
} else if (']' == *pi->s) {
|
235
|
+
pi->s++;
|
236
|
+
break;
|
237
|
+
} else {
|
238
|
+
raise_error("invalid format, expected , or ] while in an array", pi->str, pi->s);
|
239
|
+
}
|
240
|
+
}
|
241
|
+
return a;
|
242
|
+
}
|
243
|
+
|
244
|
+
static VALUE
|
245
|
+
read_str(ParseInfo pi) {
|
246
|
+
char *text = read_quoted_value(pi);
|
247
|
+
VALUE s = rb_str_new2(text);
|
248
|
+
|
249
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
250
|
+
if (0 != pi->encoding) {
|
251
|
+
rb_enc_associate(s, pi->encoding);
|
252
|
+
}
|
253
|
+
#endif
|
254
|
+
return s;
|
255
|
+
}
|
256
|
+
|
257
|
+
static VALUE
|
258
|
+
read_num(ParseInfo pi) {
|
259
|
+
int64_t n = 0;
|
260
|
+
long a = 0;
|
261
|
+
long div = 1;
|
262
|
+
long e = 0;
|
263
|
+
int neg = 0;
|
264
|
+
int eneg = 0;
|
265
|
+
|
266
|
+
if ('-' == *pi->s) {
|
267
|
+
pi->s++;
|
268
|
+
neg = 1;
|
269
|
+
} else if ('+' == *pi->s) {
|
270
|
+
pi->s++;
|
271
|
+
}
|
272
|
+
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
273
|
+
n = n * 10 + (*pi->s - '0');
|
274
|
+
}
|
275
|
+
if ('.' == *pi->s) {
|
276
|
+
pi->s++;
|
277
|
+
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
278
|
+
a = a * 10 + (*pi->s - '0');
|
279
|
+
div *= 10;
|
280
|
+
}
|
281
|
+
}
|
282
|
+
if ('e' == *pi->s || 'E' == *pi->s) {
|
283
|
+
pi->s++;
|
284
|
+
if ('-' == *pi->s) {
|
285
|
+
pi->s++;
|
286
|
+
eneg = 1;
|
287
|
+
} else if ('+' == *pi->s) {
|
288
|
+
pi->s++;
|
289
|
+
}
|
290
|
+
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
291
|
+
e = e * 10 + (*pi->s - '0');
|
292
|
+
}
|
293
|
+
}
|
294
|
+
if (neg) {
|
295
|
+
n = -n;
|
296
|
+
}
|
297
|
+
if (0 == e && 0 == a && 1 == div) {
|
298
|
+
return LONG2NUM(n);
|
299
|
+
} else {
|
300
|
+
double d = (double)n + (double)a / (double)div;
|
301
|
+
|
302
|
+
if (0 != e) {
|
303
|
+
if (eneg) {
|
304
|
+
e = -e;
|
305
|
+
}
|
306
|
+
d *= pow(10.0, e);
|
307
|
+
}
|
308
|
+
return DBL2NUM(d);
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
static VALUE
|
313
|
+
read_true(ParseInfo pi) {
|
314
|
+
pi->s++;
|
315
|
+
if ('r' != *pi->s || 'u' != *(pi->s + 1) || 'e' != *(pi->s + 2)) {
|
316
|
+
raise_error("invalid format, expected 'true'", pi->str, pi->s);
|
317
|
+
}
|
318
|
+
pi->s += 3;
|
319
|
+
|
320
|
+
return Qtrue;
|
321
|
+
}
|
322
|
+
|
323
|
+
static VALUE
|
324
|
+
read_false(ParseInfo pi) {
|
325
|
+
pi->s++;
|
326
|
+
if ('a' != *pi->s || 'l' != *(pi->s + 1) || 's' != *(pi->s + 2) || 'e' != *(pi->s + 3)) {
|
327
|
+
raise_error("invalid format, expected 'false'", pi->str, pi->s);
|
328
|
+
}
|
329
|
+
pi->s += 4;
|
330
|
+
|
331
|
+
return Qfalse;
|
332
|
+
}
|
333
|
+
|
334
|
+
static VALUE
|
335
|
+
read_nil(ParseInfo pi) {
|
336
|
+
pi->s++;
|
337
|
+
if ('u' != *pi->s || 'l' != *(pi->s + 1) || 'l' != *(pi->s + 2)) {
|
338
|
+
raise_error("invalid format, expected 'nil'", pi->str, pi->s);
|
339
|
+
}
|
340
|
+
pi->s += 3;
|
341
|
+
|
342
|
+
return Qnil;
|
343
|
+
}
|
344
|
+
|
345
|
+
static char
|
346
|
+
read_hex(ParseInfo pi, char *h) {
|
347
|
+
uint8_t b = 0;
|
348
|
+
|
349
|
+
if ('0' <= *h && *h <= '9') {
|
350
|
+
b = *h - '0';
|
351
|
+
} else if ('A' <= *h && *h <= 'F') {
|
352
|
+
b = *h - 'A' + 10;
|
353
|
+
} else if ('a' <= *h && *h <= 'f') {
|
354
|
+
b = *h - 'a' + 10;
|
355
|
+
} else {
|
356
|
+
pi->s = h;
|
357
|
+
raise_error("invalid hex character", pi->str, pi->s);
|
358
|
+
}
|
359
|
+
h++;
|
360
|
+
b = b << 4;
|
361
|
+
if ('0' <= *h && *h <= '9') {
|
362
|
+
b += *h - '0';
|
363
|
+
} else if ('A' <= *h && *h <= 'F') {
|
364
|
+
b += *h - 'A' + 10;
|
365
|
+
} else if ('a' <= *h && *h <= 'f') {
|
366
|
+
b += *h - 'a' + 10;
|
367
|
+
} else {
|
368
|
+
pi->s = h;
|
369
|
+
raise_error("invalid hex character", pi->str, pi->s);
|
370
|
+
}
|
371
|
+
return (char)b;
|
372
|
+
}
|
373
|
+
|
374
|
+
/* Assume the value starts immediately and goes until the quote character is
|
375
|
+
* reached again. Do not read the character after the terminating quote.
|
376
|
+
*/
|
377
|
+
static char*
|
378
|
+
read_quoted_value(ParseInfo pi) {
|
379
|
+
char *value = 0;
|
380
|
+
char *h = pi->s; // head
|
381
|
+
char *t = h; // tail
|
382
|
+
|
383
|
+
h++; // skip quote character
|
384
|
+
t++;
|
385
|
+
value = h;
|
386
|
+
// TBD can whole string be read in and then eval-ed by ruby of there is a special character
|
387
|
+
for (; '"' != *h; h++, t++) {
|
388
|
+
if ('\0' == *h) {
|
389
|
+
pi->s = h;
|
390
|
+
raise_error("quoted string not terminated", pi->str, pi->s);
|
391
|
+
} else if ('\\' == *h) {
|
392
|
+
h++;
|
393
|
+
switch (*h) {
|
394
|
+
case 'n': *t = '\n'; break;
|
395
|
+
case 'r': *t = '\r'; break;
|
396
|
+
case 't': *t = '\t'; break;
|
397
|
+
case 'f': *t = '\f'; break;
|
398
|
+
case 'b': *t = '\b'; break;
|
399
|
+
case '"': *t = '"'; break;
|
400
|
+
case '/': *t = '/'; break;
|
401
|
+
case '\\': *t = '\\'; break;
|
402
|
+
case 'u':
|
403
|
+
// TBD if first character is 00 then skip it
|
404
|
+
h++;
|
405
|
+
*t = read_hex(pi, h);
|
406
|
+
h += 2;
|
407
|
+
if ('\0' != *t) {
|
408
|
+
t++;
|
409
|
+
}
|
410
|
+
*t = read_hex(pi, h);
|
411
|
+
h++;
|
412
|
+
break;
|
413
|
+
default:
|
414
|
+
pi->s = h;
|
415
|
+
raise_error("invalid escaped character", pi->str, pi->s);
|
416
|
+
break;
|
417
|
+
}
|
418
|
+
} else if (t != h) {
|
419
|
+
*t = *h;
|
420
|
+
}
|
421
|
+
}
|
422
|
+
*t = '\0'; // terminate value
|
423
|
+
pi->s = h + 1;
|
424
|
+
|
425
|
+
return value;
|
426
|
+
}
|
data/ext/oj/oj.c
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
/* oj.c
|
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
|
+
*/
|
30
|
+
|
31
|
+
#include <stdlib.h>
|
32
|
+
#include <errno.h>
|
33
|
+
#include <stdio.h>
|
34
|
+
#include <string.h>
|
35
|
+
|
36
|
+
#include "ruby.h"
|
37
|
+
#include "oj.h"
|
38
|
+
|
39
|
+
struct _Options default_options = {
|
40
|
+
{ '\0' }, // encoding
|
41
|
+
2, // indent
|
42
|
+
0, // trace
|
43
|
+
No, // circular
|
44
|
+
NoMode, // mode
|
45
|
+
// StrictEffort, // effort
|
46
|
+
};
|
47
|
+
|
48
|
+
void Init_oj();
|
49
|
+
|
50
|
+
VALUE Oj = Qnil;
|
51
|
+
|
52
|
+
static VALUE
|
53
|
+
load(char *json, int argc, VALUE *argv, VALUE self) {
|
54
|
+
VALUE obj;
|
55
|
+
|
56
|
+
// TBD other options like obj mode
|
57
|
+
obj = parse(json, 0);
|
58
|
+
free(json);
|
59
|
+
|
60
|
+
return obj;
|
61
|
+
}
|
62
|
+
|
63
|
+
/* call-seq: load(xml, options) => Hash, Array, String, Fixnum, Float, true, false, or nil
|
64
|
+
*
|
65
|
+
* Parses a JSON document String into a Hash, Array, String, Fixnum, Float,
|
66
|
+
* true, false, or nil Raises an exception if the JSON is * malformed or the
|
67
|
+
* classes specified are not valid.
|
68
|
+
* @param [String] json JSON String
|
69
|
+
* @param [Hash] options load options
|
70
|
+
*/
|
71
|
+
static VALUE
|
72
|
+
load_str(int argc, VALUE *argv, VALUE self) {
|
73
|
+
char *json;
|
74
|
+
|
75
|
+
Check_Type(*argv, T_STRING);
|
76
|
+
// the xml string gets modified so make a copy of it
|
77
|
+
json = strdup(StringValuePtr(*argv));
|
78
|
+
|
79
|
+
return load(json, argc - 1, argv + 1, self);
|
80
|
+
}
|
81
|
+
|
82
|
+
static VALUE
|
83
|
+
load_file(int argc, VALUE *argv, VALUE self) {
|
84
|
+
char *path;
|
85
|
+
char *json;
|
86
|
+
FILE *f;
|
87
|
+
unsigned long len;
|
88
|
+
|
89
|
+
Check_Type(*argv, T_STRING);
|
90
|
+
path = StringValuePtr(*argv);
|
91
|
+
if (0 == (f = fopen(path, "r"))) {
|
92
|
+
rb_raise(rb_eIOError, "%s\n", strerror(errno));
|
93
|
+
}
|
94
|
+
fseek(f, 0, SEEK_END);
|
95
|
+
len = ftell(f);
|
96
|
+
if (0 == (json = malloc(len + 1))) {
|
97
|
+
fclose(f);
|
98
|
+
rb_raise(rb_eNoMemError, "Could not allocate memory for %ld byte file.\n", len);
|
99
|
+
}
|
100
|
+
fseek(f, 0, SEEK_SET);
|
101
|
+
if (len != fread(json, 1, len, f)) {
|
102
|
+
fclose(f);
|
103
|
+
rb_raise(rb_eLoadError, "Failed to read %ld bytes from %s.\n", len, path);
|
104
|
+
}
|
105
|
+
fclose(f);
|
106
|
+
json[len] = '\0';
|
107
|
+
|
108
|
+
return load(json, argc - 1, argv + 1, self);
|
109
|
+
}
|
110
|
+
|
111
|
+
static VALUE
|
112
|
+
dump(int argc, VALUE *argv, VALUE self) {
|
113
|
+
char *json;
|
114
|
+
struct _Options copts = default_options;
|
115
|
+
VALUE rstr;
|
116
|
+
|
117
|
+
if (2 == argc) {
|
118
|
+
//parse_dump_options(argv[1], &copts);
|
119
|
+
}
|
120
|
+
if (0 == (json = write_obj_to_str(*argv, &copts))) {
|
121
|
+
rb_raise(rb_eNoMemError, "Not enough memory.\n");
|
122
|
+
}
|
123
|
+
rstr = rb_str_new2(json);
|
124
|
+
#ifdef ENCODING_INLINE_MAX
|
125
|
+
if ('\0' != *copts.encoding) {
|
126
|
+
rb_enc_associate(rstr, rb_enc_find(copts.encoding));
|
127
|
+
}
|
128
|
+
#endif
|
129
|
+
free(json);
|
130
|
+
|
131
|
+
return rstr;
|
132
|
+
}
|
133
|
+
|
134
|
+
void Init_oj() {
|
135
|
+
|
136
|
+
Oj = rb_define_module("Oj");
|
137
|
+
|
138
|
+
rb_define_module_function(Oj, "load", load_str, -1);
|
139
|
+
rb_define_module_function(Oj, "load_file", load_file, -1);
|
140
|
+
rb_define_module_function(Oj, "dump", dump, -1);
|
141
|
+
}
|
142
|
+
|
143
|
+
void
|
144
|
+
_raise_error(const char *msg, const char *xml, const char *current, const char* file, int line) {
|
145
|
+
int xline = 1;
|
146
|
+
int col = 1;
|
147
|
+
|
148
|
+
for (; xml < current && '\n' != *current; current--) {
|
149
|
+
col++;
|
150
|
+
}
|
151
|
+
for (; xml < current; current--) {
|
152
|
+
if ('\n' == *current) {
|
153
|
+
xline++;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
rb_raise(rb_eSyntaxError, "%s at line %d, column %d [%s:%d]\n", msg, xline, col, file, line);
|
157
|
+
}
|
data/ext/oj/oj.h
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
/* oj.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
|
+
*/
|
30
|
+
|
31
|
+
#ifndef __OJ_H__
|
32
|
+
#define __OJ_H__
|
33
|
+
|
34
|
+
#if defined(__cplusplus)
|
35
|
+
extern "C" {
|
36
|
+
#if 0
|
37
|
+
} /* satisfy cc-mode */
|
38
|
+
#endif
|
39
|
+
#endif
|
40
|
+
|
41
|
+
#include "ruby.h"
|
42
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
43
|
+
// HAVE_RUBY_ENCODING_H defined for Ruby 1.9
|
44
|
+
#include "ruby/encoding.h"
|
45
|
+
#endif
|
46
|
+
|
47
|
+
#ifdef JRUBY
|
48
|
+
#define NO_RSTRUCT 1
|
49
|
+
#endif
|
50
|
+
|
51
|
+
#if (defined RBX_Qnil && !defined RUBINIUS)
|
52
|
+
#define RUBINIUS
|
53
|
+
#endif
|
54
|
+
|
55
|
+
#ifdef RUBINIUS
|
56
|
+
#undef T_RATIONAL
|
57
|
+
#undef T_COMPLEX
|
58
|
+
#define NO_RSTRUCT 1
|
59
|
+
#endif
|
60
|
+
|
61
|
+
#define raise_error(msg, xml, current) _raise_error(msg, xml, current, __FILE__, __LINE__)
|
62
|
+
|
63
|
+
typedef enum {
|
64
|
+
Yes = 'y',
|
65
|
+
No = 'n',
|
66
|
+
NotSet = 0
|
67
|
+
} YesNo;
|
68
|
+
|
69
|
+
typedef enum {
|
70
|
+
ObjMode = 'o',
|
71
|
+
GenMode = 'g',
|
72
|
+
NoMode = 0
|
73
|
+
} LoadMode;
|
74
|
+
|
75
|
+
typedef struct _Options {
|
76
|
+
char encoding[64]; // encoding, stored in the option to avoid GC invalidation in default values
|
77
|
+
int indent; // indention for dump, default 2
|
78
|
+
int trace; // trace level
|
79
|
+
char circular; // YesNo
|
80
|
+
char mode; // LoadMode
|
81
|
+
char effort; // Effort
|
82
|
+
} *Options;
|
83
|
+
|
84
|
+
extern VALUE parse(char *json, int trace);
|
85
|
+
extern char* write_obj_to_str(VALUE obj, Options copts);
|
86
|
+
|
87
|
+
extern void _raise_error(const char *msg, const char *xml, const char *current, const char* file, int line);
|
88
|
+
|
89
|
+
|
90
|
+
extern VALUE Oj;
|
91
|
+
|
92
|
+
#if defined(__cplusplus)
|
93
|
+
#if 0
|
94
|
+
{ /* satisfy cc-mode */
|
95
|
+
#endif
|
96
|
+
} /* extern "C" { */
|
97
|
+
#endif
|
98
|
+
#endif /* __OJ_H__ */
|
data/lib/oj.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright (c) 2011, Peter Ohler<br>
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# - Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# - Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# - Neither the name of Peter Ohler nor the names of its contributors may be
|
15
|
+
# used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
22
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
29
|
+
module Oj
|
30
|
+
private
|
31
|
+
@@keep = []
|
32
|
+
end
|
33
|
+
|
34
|
+
#require 'ox/version'
|
35
|
+
|
36
|
+
require 'oj/oj' # C extension
|
data/lib/oj/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oj
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.5'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Peter Ohler
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-19 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! "A fast JSON parser and object serializer that uses only standard C
|
15
|
+
lib.\n \nOptimized JSON (Oj), as the name implies was written to provide
|
16
|
+
speed optimized\nJSON handling. It was designed to be an alternative to Yajl and
|
17
|
+
other Ruby\nJSON parsers and as an alternative to Marshal for Object serialization. "
|
18
|
+
email: peter@ohler.com
|
19
|
+
executables: []
|
20
|
+
extensions:
|
21
|
+
- ext/oj/extconf.rb
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.md
|
24
|
+
files:
|
25
|
+
- lib/oj/version.rb
|
26
|
+
- lib/oj.rb
|
27
|
+
- ext/oj/extconf.rb
|
28
|
+
- ext/oj/oj.h
|
29
|
+
- ext/oj/dump.c
|
30
|
+
- ext/oj/load.c
|
31
|
+
- ext/oj/oj.c
|
32
|
+
- LICENSE
|
33
|
+
- README.md
|
34
|
+
homepage: https://github.com/ohler55/oj
|
35
|
+
licenses: []
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --main
|
39
|
+
- README.md
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
- ext
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubyforge_project: oj
|
57
|
+
rubygems_version: 1.8.15
|
58
|
+
signing_key:
|
59
|
+
specification_version: 3
|
60
|
+
summary: A fast JSON parser and serializer.
|
61
|
+
test_files: []
|