oj 1.0.5 → 1.0.6

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/README.md CHANGED
@@ -24,9 +24,9 @@ A fast JSON parser and Object marshaller as a Ruby gem.
24
24
 
25
25
  ## <a name="release">Release Notes</a>
26
26
 
27
- ### Release 1.0.5
27
+ ### Release 1.0.6
28
28
 
29
- - Added :ascii_only options for dumping JSON where all high-bit characters are encoded as escaped sequences.
29
+ - Gave Oj::Doc a speed increase. It is now 8 times fast than JSON::Ext.
30
30
 
31
31
  ## <a name="description">Description</a>
32
32
 
@@ -97,6 +97,14 @@ static void grow(Out out, size_t len);
97
97
  static size_t json_friendly_size(const u_char *str, size_t len);
98
98
  static size_t ascii_friendly_size(const u_char *str, size_t len);
99
99
 
100
+ static void dump_leaf_to_json(Leaf leaf, Options copts, Out out);
101
+ static void dump_leaf(Leaf leaf, int depth, Out out);
102
+ static void dump_leaf_str(Leaf leaf, Out out);
103
+ static void dump_leaf_fixnum(Leaf leaf, Out out);
104
+ static void dump_leaf_float(Leaf leaf, Out out);
105
+ static void dump_leaf_array(Leaf leaf, int depth, Out out);
106
+ static void dump_leaf_hash(Leaf leaf, int depth, Out out);
107
+
100
108
 
101
109
  static const char hex_chars[17] = "0123456789abcdef";
102
110
 
@@ -287,7 +295,7 @@ static void
287
295
  dump_fixnum(VALUE obj, Out out) {
288
296
  char buf[32];
289
297
  char *b = buf + sizeof(buf) - 1;
290
- long num = NUM2LONG(obj);
298
+ long num = NUM2LONG(obj);
291
299
  int neg = 0;
292
300
 
293
301
  if (0 > num) {
@@ -407,7 +415,6 @@ dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
407
415
  *out->cur++ = '0';
408
416
  dump_hex((u_char)*str, out);
409
417
  } else { // continuation?
410
- // TBD lead with \u00 . grab next char?
411
418
  *out->cur++ = '0';
412
419
  *out->cur++ = '0';
413
420
  dump_hex((u_char)*str, out);
@@ -1128,7 +1135,224 @@ oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
1128
1135
  rb_raise(rb_eIOError, "%s\n", strerror(errno));
1129
1136
  }
1130
1137
  if (size != fwrite(out.buf, 1, size, f)) {
1131
- int err = ferror(f);
1138
+ int err = ferror(f);
1139
+
1140
+ rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
1141
+ }
1142
+ xfree(out.buf);
1143
+ fclose(f);
1144
+ }
1145
+
1146
+ // dump leaf functions
1147
+
1148
+ inline static void
1149
+ dump_chars(const char *s, size_t size, Out out) {
1150
+ if (out->end - out->cur <= (long)size) {
1151
+ grow(out, size);
1152
+ }
1153
+ memcpy(out->cur, s, size);
1154
+ out->cur += size;
1155
+ *out->cur = '\0';
1156
+ }
1157
+
1158
+ static void
1159
+ dump_leaf_str(Leaf leaf, Out out) {
1160
+ switch (leaf->value_type) {
1161
+ case STR_VAL:
1162
+ dump_cstr(leaf->str, strlen(leaf->str), 0, 0, out);
1163
+ break;
1164
+ case RUBY_VAL:
1165
+ dump_cstr(StringValuePtr(leaf->value), RSTRING_LEN(leaf->value), 0, 0, out);
1166
+ break;
1167
+ case COL_VAL:
1168
+ default:
1169
+ rb_raise(rb_eTypeError, "Unexpected value type %02x.", leaf->value_type);
1170
+ break;
1171
+ }
1172
+ }
1173
+
1174
+ static void
1175
+ dump_leaf_fixnum(Leaf leaf, Out out) {
1176
+ switch (leaf->value_type) {
1177
+ case STR_VAL:
1178
+ dump_chars(leaf->str, strlen(leaf->str), out);
1179
+ break;
1180
+ case RUBY_VAL:
1181
+ if (T_BIGNUM == rb_type(leaf->value)) {
1182
+ dump_bignum(leaf->value, out);
1183
+ } else {
1184
+ dump_fixnum(leaf->value, out);
1185
+ }
1186
+ break;
1187
+ case COL_VAL:
1188
+ default:
1189
+ rb_raise(rb_eTypeError, "Unexpected value type %02x.", leaf->value_type);
1190
+ break;
1191
+ }
1192
+ }
1193
+
1194
+ static void
1195
+ dump_leaf_float(Leaf leaf, Out out) {
1196
+ switch (leaf->value_type) {
1197
+ case STR_VAL:
1198
+ dump_chars(leaf->str, strlen(leaf->str), out);
1199
+ break;
1200
+ case RUBY_VAL:
1201
+ dump_float(leaf->value, out);
1202
+ break;
1203
+ case COL_VAL:
1204
+ default:
1205
+ rb_raise(rb_eTypeError, "Unexpected value type %02x.", leaf->value_type);
1206
+ break;
1207
+ }
1208
+ }
1209
+
1210
+ static void
1211
+ dump_leaf_array(Leaf leaf, int depth, Out out) {
1212
+ size_t size;
1213
+ int d2 = depth + 1;
1214
+
1215
+ size = 2;
1216
+ if (out->end - out->cur <= (long)size) {
1217
+ grow(out, size);
1218
+ }
1219
+ *out->cur++ = '[';
1220
+ if (0 == leaf->elements) {
1221
+ *out->cur++ = ']';
1222
+ } else {
1223
+ Leaf first = leaf->elements->next;
1224
+ Leaf e = first;
1225
+
1226
+ size = d2 * out->indent + 2;
1227
+ do {
1228
+ if (out->end - out->cur <= (long)size) {
1229
+ grow(out, size);
1230
+ }
1231
+ fill_indent(out, d2);
1232
+ dump_leaf(e, d2, out);
1233
+ if (e->next != first) {
1234
+ *out->cur++ = ',';
1235
+ }
1236
+ e = e->next;
1237
+ } while (e != first);
1238
+ size = depth * out->indent + 1;
1239
+ if (out->end - out->cur <= (long)size) {
1240
+ grow(out, size);
1241
+ }
1242
+ fill_indent(out, depth);
1243
+ *out->cur++ = ']';
1244
+ }
1245
+ *out->cur = '\0';
1246
+ }
1247
+
1248
+ static void
1249
+ dump_leaf_hash(Leaf leaf, int depth, Out out) {
1250
+ size_t size;
1251
+ int d2 = depth + 1;
1252
+
1253
+ size = 2;
1254
+ if (out->end - out->cur <= (long)size) {
1255
+ grow(out, size);
1256
+ }
1257
+ *out->cur++ = '{';
1258
+ if (0 == leaf->elements) {
1259
+ *out->cur++ = '}';
1260
+ } else {
1261
+ Leaf first = leaf->elements->next;
1262
+ Leaf e = first;
1263
+
1264
+ size = d2 * out->indent + 2;
1265
+ do {
1266
+ if (out->end - out->cur <= (long)size) {
1267
+ grow(out, size);
1268
+ }
1269
+ fill_indent(out, d2);
1270
+ dump_cstr(e->key, strlen(e->key), 0, 0, out);
1271
+ *out->cur++ = ':';
1272
+ dump_leaf(e, d2, out);
1273
+ if (e->next != first) {
1274
+ *out->cur++ = ',';
1275
+ }
1276
+ e = e->next;
1277
+ } while (e != first);
1278
+ size = depth * out->indent + 1;
1279
+ if (out->end - out->cur <= (long)size) {
1280
+ grow(out, size);
1281
+ }
1282
+ fill_indent(out, depth);
1283
+ *out->cur++ = '}';
1284
+ }
1285
+ *out->cur = '\0';
1286
+ }
1287
+
1288
+ static void
1289
+ dump_leaf(Leaf leaf, int depth, Out out) {
1290
+ switch (leaf->type) {
1291
+ case T_NIL:
1292
+ dump_nil(out);
1293
+ break;
1294
+ case T_TRUE:
1295
+ dump_true(out);
1296
+ break;
1297
+ case T_FALSE:
1298
+ dump_false(out);
1299
+ break;
1300
+ case T_STRING:
1301
+ dump_leaf_str(leaf, out);
1302
+ break;
1303
+ case T_FIXNUM:
1304
+ dump_leaf_fixnum(leaf, out);
1305
+ break;
1306
+ case T_FLOAT:
1307
+ dump_leaf_float(leaf, out);
1308
+ break;
1309
+ case T_ARRAY:
1310
+ dump_leaf_array(leaf, depth, out);
1311
+ break;
1312
+ case T_HASH:
1313
+ dump_leaf_hash(leaf, depth, out);
1314
+ break;
1315
+ default:
1316
+ rb_raise(rb_eTypeError, "Unexpected type %02x.", leaf->type);
1317
+ break;
1318
+ }
1319
+ }
1320
+
1321
+ static void
1322
+ dump_leaf_to_json(Leaf leaf, Options copts, Out out) {
1323
+ out->buf = ALLOC_N(char, 65336);
1324
+ out->end = out->buf + 65325; // 10 less than end plus extra for possible errors
1325
+ out->cur = out->buf;
1326
+ out->circ_cnt = 0;
1327
+ out->opts = copts;
1328
+ out->hash_cnt = 0;
1329
+ out->indent = copts->indent;
1330
+ dump_leaf(leaf, 0, out);
1331
+ }
1332
+
1333
+ char*
1334
+ oj_write_leaf_to_str(Leaf leaf, Options copts) {
1335
+ struct _Out out;
1336
+
1337
+ dump_leaf_to_json(leaf, copts, &out);
1338
+
1339
+ return out.buf;
1340
+ }
1341
+
1342
+ void
1343
+ oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts) {
1344
+ struct _Out out;
1345
+ size_t size;
1346
+ FILE *f;
1347
+
1348
+ dump_leaf_to_json(leaf, copts, &out);
1349
+ size = out.cur - out.buf;
1350
+ if (0 == (f = fopen(path, "w"))) {
1351
+ rb_raise(rb_eIOError, "%s\n", strerror(errno));
1352
+ }
1353
+ if (size != fwrite(out.buf, 1, size, f)) {
1354
+ int err = ferror(f);
1355
+
1132
1356
  rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
1133
1357
  }
1134
1358
  xfree(out.buf);
@@ -37,29 +37,6 @@
37
37
  #include "oj.h"
38
38
 
39
39
  #define MAX_STACK 100
40
-
41
- enum {
42
- STR_VAL = 0x00,
43
- COL_VAL = 0x01,
44
- RUBY_VAL = 0x02
45
- };
46
-
47
- typedef struct _Leaf {
48
- struct _Leaf *next;
49
- union {
50
- const char *key; // hash key
51
- size_t index; // array index, 0 is not set
52
- };
53
- union {
54
- char *str; // pointer to location in json string
55
- struct _Leaf *elements; // array and hash elements
56
- VALUE value;
57
- };
58
- uint8_t type;
59
- uint8_t parent_type;
60
- uint8_t value_type;
61
- } *Leaf;
62
-
63
40
  //#define BATCH_SIZE (4096 / sizeof(struct _Leaf) - 1)
64
41
  #define BATCH_SIZE 100
65
42
 
@@ -81,8 +58,6 @@ typedef struct _Doc {
81
58
  unsigned long size; // number of leaves/branches in the doc
82
59
  VALUE self;
83
60
  Batch batches;
84
- //Leaf where_array[MAX_STACK];
85
- //size_t where_len; // length of allocated if longer than where_array
86
61
  struct _Batch batch0;
87
62
  } *Doc;
88
63
 
@@ -748,8 +723,6 @@ read_quoted_value(ParseInfo pi) {
748
723
  // doc support functions
749
724
  inline static void
750
725
  doc_init(Doc doc) {
751
- //doc->where_path = doc->where_array;
752
- //doc->where_len = 0;
753
726
  doc->where = doc->where_path;
754
727
  *doc->where = 0;
755
728
  doc->data = 0;
@@ -776,11 +749,6 @@ doc_free(Doc doc) {
776
749
  xfree(b);
777
750
  }
778
751
  }
779
- /*
780
- if (doc->where_array != doc->where_path) {
781
- free(doc->where_path);
782
- }
783
- */
784
752
  //xfree(f);
785
753
  }
786
754
  }
@@ -792,6 +760,7 @@ protect_open_proc(VALUE x) {
792
760
  pi->doc->data = read_next(pi); // parse
793
761
  *pi->doc->where = pi->doc->data;
794
762
  pi->doc->where = pi->doc->where_path;
763
+
795
764
  return rb_yield(pi->doc->self); // caller processing
796
765
  }
797
766
 
@@ -848,6 +817,9 @@ static Leaf
848
817
  get_leaf(Leaf *stack, Leaf *lp, const char *path) {
849
818
  Leaf leaf = *lp;
850
819
 
820
+ if (MAX_STACK <= lp - stack) {
821
+ rb_raise(rb_eIOError, "Path too deep. limit is %d levels.\n", MAX_STACK);
822
+ }
851
823
  if ('\0' != *path) {
852
824
  if ('.' == *path && '.' == *(path + 1)) {
853
825
  path += 2;
@@ -864,7 +836,6 @@ get_leaf(Leaf *stack, Leaf *lp, const char *path) {
864
836
  Leaf e = first;
865
837
  int type = leaf->type;
866
838
 
867
- // TBD fail if stack too deep
868
839
  leaf = 0;
869
840
  if (T_ARRAY == type) {
870
841
  int cnt = 0;
@@ -933,7 +904,9 @@ each_leaf(Doc doc, VALUE self) {
933
904
 
934
905
  static int
935
906
  move_step(Doc doc, const char *path, int loc) {
936
- // TBD raise if too deep
907
+ if (MAX_STACK <= doc->where - doc->where_path) {
908
+ rb_raise(rb_eIOError, "Path too deep. limit is %d levels.\n", MAX_STACK);
909
+ }
937
910
  if ('\0' == *path) {
938
911
  loc = 0;
939
912
  } else {
@@ -1425,13 +1398,12 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
1425
1398
  return Qnil;
1426
1399
  }
1427
1400
 
1428
- // TBD improve to be more direct for higher performance
1429
-
1430
1401
  /* call-seq: dump(path=nil) => String
1431
1402
  *
1432
1403
  * Dumps the document or nodes to a new JSON document. It uses the default
1433
1404
  * options for generating the JSON.
1434
1405
  * @param [String] path if provided it identified the top of the branch to dump to JSON
1406
+ * @param [String] filename if provided it is the filename to write the output to
1435
1407
  * @example
1436
1408
  * Oj::Doc.open('[3,[2,1]]') { |doc|
1437
1409
  * doc.dump('/2')
@@ -1443,16 +1415,31 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
1443
1415
  Doc doc = DATA_PTR(self);
1444
1416
  Leaf leaf;
1445
1417
  const char *path = 0;
1446
- const char *json;
1418
+ const char *filename = 0;
1447
1419
 
1448
1420
  if (1 <= argc) {
1449
- Check_Type(*argv, T_STRING);
1450
- path = StringValuePtr(*argv);
1421
+ if (Qnil != *argv) {
1422
+ Check_Type(*argv, T_STRING);
1423
+ path = StringValuePtr(*argv);
1424
+ }
1425
+ if (2 <= argc) {
1426
+ Check_Type(argv[1], T_STRING);
1427
+ filename = StringValuePtr(argv[1]);
1428
+ }
1451
1429
  }
1452
1430
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1453
- json = oj_write_obj_to_str(leaf_value(doc, leaf), &oj_default_options);
1431
+ char *json;
1432
+ VALUE rjson;
1454
1433
 
1455
- return rb_str_new2(json);
1434
+ if (0 == filename) {
1435
+ json = oj_write_leaf_to_str(leaf, &oj_default_options);
1436
+ rjson = rb_str_new2(json);
1437
+ xfree(json);
1438
+ } else {
1439
+ oj_write_leaf_to_file(leaf, filename, &oj_default_options);
1440
+ rjson = Qnil;
1441
+ }
1442
+ return rjson;
1456
1443
  }
1457
1444
  return Qnil;
1458
1445
  }
@@ -89,9 +89,33 @@ typedef struct _Options {
89
89
  char mode; // Mode
90
90
  } *Options;
91
91
 
92
+ enum {
93
+ STR_VAL = 0x00,
94
+ COL_VAL = 0x01,
95
+ RUBY_VAL = 0x02
96
+ };
97
+
98
+ typedef struct _Leaf {
99
+ struct _Leaf *next;
100
+ union {
101
+ const char *key; // hash key
102
+ size_t index; // array index, 0 is not set
103
+ };
104
+ union {
105
+ char *str; // pointer to location in json string
106
+ struct _Leaf *elements; // array and hash elements
107
+ VALUE value;
108
+ };
109
+ uint8_t type;
110
+ uint8_t parent_type;
111
+ uint8_t value_type;
112
+ } *Leaf;
113
+
92
114
  extern VALUE oj_parse(char *json, Options options);
93
115
  extern char* oj_write_obj_to_str(VALUE obj, Options copts);
94
116
  extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
117
+ extern char* oj_write_leaf_to_str(Leaf leaf, Options copts);
118
+ extern void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts);
95
119
 
96
120
  extern void _oj_raise_error(const char *msg, const char *xml, const char *current, const char* file, int line);
97
121
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '1.0.5'
4
+ VERSION = '1.0.6'
5
5
  end
@@ -14,9 +14,10 @@ require 'oj'
14
14
 
15
15
  $verbose = false
16
16
  $indent = 0
17
- $iter = 100000
17
+ $iter = 10000
18
18
  $gets = 0
19
19
  $fetch = false
20
+ $write = false
20
21
 
21
22
  opts = OptionParser.new
22
23
  opts.on("-v", "verbose") { $verbose = true }
@@ -24,6 +25,7 @@ opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
24
25
  opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
25
26
  opts.on("-g", "--gets [Int]", Integer, "number of gets") { |i| $gets = i }
26
27
  opts.on("-f", "fetch") { $fetch = true }
28
+ opts.on("-w", "write") { $write = true }
27
29
  opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
28
30
  files = opts.parse(ARGV)
29
31
 
@@ -82,6 +84,17 @@ perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) } unless $failed.has_key?(
82
84
  perf.add('JSON::Ext', 'parse') { JSON::Ext::Parser.new($json).parse } unless $failed.has_key?('JSON::Ext')
83
85
  perf.run($iter)
84
86
 
87
+ puts '-' * 80
88
+ puts "JSON generation Performance"
89
+ Oj::Doc.open($json) do |doc|
90
+ perf = Perf.new()
91
+ perf.add('Oj::Doc', 'dump') { doc.dump() }
92
+ perf.add('Yajl', 'encode') { Yajl::Encoder.encode($obj) }
93
+ perf.add('JSON::Ext', 'fast_generate') { JSON.fast_generate($obj) }
94
+ perf.before('JSON::Ext') { JSON.generator = JSON::Ext::Generator }
95
+ perf.run($iter)
96
+ end
97
+
85
98
  if 0 < $gets
86
99
  puts '-' * 80
87
100
  puts "Parse and get all values Performance"
@@ -113,6 +126,19 @@ if $fetch
113
126
  end
114
127
  end
115
128
 
129
+ if $write
130
+ puts '-' * 80
131
+ puts "JSON write to file Performance"
132
+ Oj::Doc.open($json) do |doc|
133
+ perf = Perf.new()
134
+ perf.add('Oj::Doc', 'dump') { doc.dump(nil, 'oj.json') }
135
+ perf.add('Yajl', 'encode') { File.open('yajl.json', 'w') { |f| Yajl::Encoder.encode($obj, f) } }
136
+ perf.add('JSON::Ext', 'fast_generate') { File.open('json_ext.json', 'w') { |f| f.write(JSON.fast_generate($obj)) } }
137
+ perf.before('JSON::Ext') { JSON.generator = JSON::Ext::Generator }
138
+ perf.run($iter)
139
+ end
140
+ end
141
+
116
142
  unless $failed.empty?
117
143
  puts "The following packages were not included for the reason listed"
118
144
  $failed.each { |tag,msg| puts "***** #{tag}: #{msg}" }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-17 00:00:00.000000000 Z
12
+ date: 2012-03-20 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! 'The fastest JSON parser and object serializer. '
15
15
  email: peter@ohler.com