oj 2.7.3 → 2.8.0

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 86fa5360a8dad0be6218e5b707229d1123242f5c
4
- data.tar.gz: bd7112345eca19080d314ef865db6d377a81596a
3
+ metadata.gz: 44d745373ae1530f6a0ae6ec981b62c84eac8f2d
4
+ data.tar.gz: c5c3c1506daf7f2ea35185d67fe1d469ced94fda
5
5
  SHA512:
6
- metadata.gz: ccc6d971d9a8f0be85cb7c67b523a981d756c73fb1a2d1bd499f61bb1120a6da5925d8723cca8c008e8e2b281be7e81c20574358fb20b8b53340fb88f74439bc
7
- data.tar.gz: 172de575b67abc4012818a9e526be9661bd78e0817bb62db6c060692f3dfd19fd29f5d40240d3d45e9557085fa34ebf9de2a1d23fe43daacf781cb7665d0b4eb
6
+ metadata.gz: ef8a3aee09c48dce3f3a8565b1f4412c3703c3c9aad396f08573fc978631ea364dd58d2bd684960b29ec2dfd2b989ea3145f93a994c75b2c1eb0d8a9e5ed1620
7
+ data.tar.gz: 95429819075ae22192b957da153ea1768eab8e93441e74e988daa4f994d7723e6947d8d6d341f05a3253717c08a3e800d7bd4b770e39ffc702028faf57b96025
data/README.md CHANGED
@@ -26,9 +26,12 @@ Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announceme
26
26
 
27
27
  [![Build Status](https://secure.travis-ci.org/ohler55/oj.png?branch=master)](http://travis-ci.org/ohler55/oj)
28
28
 
29
- ### Current Release 2.7.3
29
+ ### Current Release 2.8.0
30
30
 
31
- - Fixed bug where load and dump of Structs in modules did not work correctly.
31
+ - Added support for registering special encoding and decoding rules for
32
+ specific classes. This the ActiveSupport subclass of the String class for
33
+ safe strings. For an example look at the `test_object.rb` file,
34
+ `test_odd_string` test.
32
35
 
33
36
  [Older release notes](http://www.ohler.com/dev/oj_misc/release_notes.html).
34
37
 
@@ -70,7 +70,7 @@ static void dump_raw(const char *str, size_t cnt, Out out);
70
70
  static void dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out);
71
71
  static void dump_hex(uint8_t c, Out out);
72
72
  static void dump_str_comp(VALUE obj, Out out);
73
- static void dump_str_obj(VALUE obj, Out out);
73
+ static void dump_str_obj(VALUE obj, int depth, Out out);
74
74
  static void dump_sym_comp(VALUE obj, Out out);
75
75
  static void dump_sym_obj(VALUE obj, Out out);
76
76
  static void dump_class_comp(VALUE obj, Out out);
@@ -545,12 +545,19 @@ dump_str_comp(VALUE obj, Out out) {
545
545
  }
546
546
 
547
547
  static void
548
- dump_str_obj(VALUE obj, Out out) {
549
- const char *s = rb_string_value_ptr((VALUE*)&obj);
550
- size_t len = RSTRING_LEN(obj);
551
- char s1 = s[1];
548
+ dump_str_obj(VALUE obj, int depth, Out out) {
549
+ VALUE clas = rb_obj_class(obj);
550
+ Odd odd;
551
+
552
+ if (rb_cString != clas && 0 != (odd = oj_get_odd(clas))) {
553
+ dump_odd(obj, odd, clas, depth + 1, out);
554
+ } else {
555
+ const char *s = rb_string_value_ptr((VALUE*)&obj);
556
+ size_t len = RSTRING_LEN(obj);
557
+ char s1 = s[1];
552
558
 
553
- dump_cstr(s, len, 0, (':' == *s || ('^' == *s && ('r' == s1 || 'i' == s1))), out);
559
+ dump_cstr(s, len, 0, (':' == *s || ('^' == *s && ('r' == s1 || 'i' == s1))), out);
560
+ }
554
561
  }
555
562
 
556
563
  static void
@@ -812,7 +819,7 @@ hash_cb_object(VALUE key, VALUE value, Out out) {
812
819
  }
813
820
  fill_indent(out, depth);
814
821
  if (rb_type(key) == T_STRING) {
815
- dump_str_obj(key, out);
822
+ dump_str_obj(key, depth, out);
816
823
  *out->cur++ = ':';
817
824
  dump_val(value, depth, out);
818
825
  } else if (rb_type(key) == T_SYMBOL) {
@@ -1609,36 +1616,17 @@ raise_strict(VALUE obj) {
1609
1616
 
1610
1617
  static void
1611
1618
  dump_val(VALUE obj, int depth, Out out) {
1619
+ int type = rb_type(obj);
1620
+
1612
1621
  if (MAX_DEPTH < depth) {
1613
1622
  rb_raise(rb_eNoMemError, "Too deeply nested.\n");
1614
1623
  }
1615
1624
  switch (rb_type(obj)) {
1616
- case T_NIL: dump_nil(out); break;
1617
- case T_TRUE: dump_true(out); break;
1618
- case T_FALSE: dump_false(out); break;
1619
- case T_FIXNUM: dump_fixnum(obj, out); break;
1620
- case T_FLOAT: dump_float(obj, out); break;
1621
- case T_BIGNUM: dump_bignum(obj, out); break;
1622
- case T_STRING:
1623
- switch (out->opts->mode) {
1624
- case StrictMode:
1625
- case NullMode:
1626
- case CompatMode: dump_str_comp(obj, out); break;
1627
- case ObjectMode:
1628
- default: dump_str_obj(obj, out); break;
1629
- }
1630
- break;
1631
- case T_SYMBOL:
1632
- switch (out->opts->mode) {
1633
- case StrictMode: raise_strict(obj); break;
1634
- case NullMode: dump_nil(out); break;
1635
- case CompatMode: dump_sym_comp(obj, out); break;
1636
- case ObjectMode:
1637
- default: dump_sym_obj(obj, out); break;
1638
- }
1639
- break;
1640
- case T_ARRAY: dump_array(obj, depth, out); break;
1641
- case T_HASH: dump_hash(obj, depth, out->opts->mode, out); break;
1625
+ case T_NIL: dump_nil(out); break;
1626
+ case T_TRUE: dump_true(out); break;
1627
+ case T_FALSE: dump_false(out); break;
1628
+ case T_FIXNUM: dump_fixnum(obj, out); break;
1629
+ case T_FLOAT: dump_float(obj, out); break;
1642
1630
  case T_CLASS:
1643
1631
  switch (out->opts->mode) {
1644
1632
  case StrictMode: raise_strict(obj); break;
@@ -1648,28 +1636,15 @@ dump_val(VALUE obj, int depth, Out out) {
1648
1636
  default: dump_class_obj(obj, out); break;
1649
1637
  }
1650
1638
  break;
1651
- #if (defined T_RATIONAL && defined RRATIONAL)
1652
- case T_RATIONAL:
1653
- #endif
1654
- case T_OBJECT:
1655
- switch (out->opts->mode) {
1656
- case StrictMode: dump_data_strict(obj, out); break;
1657
- case NullMode: dump_data_null(obj, out); break;
1658
- case CompatMode: dump_obj_comp(obj, depth, out); break;
1659
- case ObjectMode:
1660
- default: dump_obj_obj(obj, depth, out); break;
1661
- }
1662
- break;
1663
- case T_DATA:
1639
+ case T_SYMBOL:
1664
1640
  switch (out->opts->mode) {
1665
- case StrictMode: dump_data_strict(obj, out); break;
1666
- case NullMode: dump_data_null(obj, out); break;
1667
- case CompatMode: dump_data_comp(obj, depth, out);break;
1641
+ case StrictMode: raise_strict(obj); break;
1642
+ case NullMode: dump_nil(out); break;
1643
+ case CompatMode: dump_sym_comp(obj, out); break;
1668
1644
  case ObjectMode:
1669
- default: dump_data_obj(obj, depth, out); break;
1645
+ default: dump_sym_obj(obj, out); break;
1670
1646
  }
1671
1647
  break;
1672
-
1673
1648
  case T_STRUCT: // for Range
1674
1649
  switch (out->opts->mode) {
1675
1650
  case StrictMode: raise_strict(obj); break;
@@ -1679,23 +1654,70 @@ dump_val(VALUE obj, int depth, Out out) {
1679
1654
  default: dump_struct_obj(obj, depth, out); break;
1680
1655
  }
1681
1656
  break;
1682
-
1657
+ default:
1658
+ // Most developers have enough sense not to subclass primitive types but
1659
+ // since these classes could potentially be subclassed a check for odd
1660
+ // classes is performed.
1661
+ {
1662
+ VALUE clas = rb_obj_class(obj);
1663
+ Odd odd;
1664
+
1665
+ if (ObjectMode == out->opts->mode && 0 != (odd = oj_get_odd(clas))) {
1666
+ dump_odd(obj, odd, clas, depth + 1, out);
1667
+ return;
1668
+ }
1669
+ switch (type) {
1670
+ case T_BIGNUM: dump_bignum(obj, out); break;
1671
+ case T_STRING:
1672
+ switch (out->opts->mode) {
1673
+ case StrictMode:
1674
+ case NullMode:
1675
+ case CompatMode: dump_str_comp(obj, out); break;
1676
+ case ObjectMode:
1677
+ default: dump_str_obj(obj, depth, out); break;
1678
+ }
1679
+ break;
1680
+ case T_ARRAY: dump_array(obj, depth, out); break;
1681
+ case T_HASH: dump_hash(obj, depth, out->opts->mode, out); break;
1682
+ #if (defined T_RATIONAL && defined RRATIONAL)
1683
+ case T_RATIONAL:
1684
+ #endif
1685
+ case T_OBJECT:
1686
+ switch (out->opts->mode) {
1687
+ case StrictMode: dump_data_strict(obj, out); break;
1688
+ case NullMode: dump_data_null(obj, out); break;
1689
+ case CompatMode: dump_obj_comp(obj, depth, out); break;
1690
+ case ObjectMode:
1691
+ default: dump_obj_obj(obj, depth, out); break;
1692
+ }
1693
+ break;
1694
+ case T_DATA:
1695
+ switch (out->opts->mode) {
1696
+ case StrictMode: dump_data_strict(obj, out); break;
1697
+ case NullMode: dump_data_null(obj, out); break;
1698
+ case CompatMode: dump_data_comp(obj, depth, out);break;
1699
+ case ObjectMode:
1700
+ default: dump_data_obj(obj, depth, out); break;
1701
+ }
1702
+ break;
1683
1703
  #if (defined T_COMPLEX && defined RCOMPLEX)
1684
- case T_COMPLEX:
1704
+ case T_COMPLEX:
1685
1705
  #endif
1686
- case T_REGEXP:
1687
- switch (out->opts->mode) {
1688
- case StrictMode: raise_strict(obj); break;
1689
- case NullMode: dump_nil(out); break;
1690
- case CompatMode:
1691
- case ObjectMode:
1692
- default: dump_obj_comp(obj, depth, out); break;
1706
+ case T_REGEXP:
1707
+ switch (out->opts->mode) {
1708
+ case StrictMode: raise_strict(obj); break;
1709
+ case NullMode: dump_nil(out); break;
1710
+ case CompatMode:
1711
+ case ObjectMode:
1712
+ default: dump_obj_comp(obj, depth, out); break;
1713
+ }
1714
+ break;
1715
+ default:
1716
+ rb_raise(rb_eNotImpError, "Failed to dump '%s' Object (%02x)\n",
1717
+ rb_class2name(rb_obj_class(obj)), rb_type(obj));
1718
+ break;
1719
+ }
1693
1720
  }
1694
- break;
1695
- default:
1696
- rb_raise(rb_eNotImpError, "Failed to dump '%s' Object (%02x)\n",
1697
- rb_class2name(rb_obj_class(obj)), rb_type(obj));
1698
- break;
1699
1721
  }
1700
1722
  }
1701
1723
 
@@ -28,9 +28,13 @@
28
28
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
  */
30
30
 
31
+ #include <string.h>
32
+
31
33
  #include "odd.h"
32
34
 
33
- struct _Odd odds[5]; // bump up if new Odd classes are added
35
+ static struct _Odd _odds[4]; // bump up if new initial Odd classes are added
36
+ static struct _Odd *odds = _odds;
37
+ static int odd_cnt = 0;
34
38
 
35
39
  static void
36
40
  set_class(Odd odd, const char *classname) {
@@ -96,16 +100,15 @@ oj_odd_init() {
96
100
  *np++ = 0;
97
101
  set_class(odd, "Range");
98
102
  odd->attr_cnt = 3;
99
- // The end. bump up the size of odds if a new class is added.
100
- odd++;
101
- odd->clas = Qundef;
103
+
104
+ odd_cnt = odd - odds + 1;
102
105
  }
103
106
 
104
107
  Odd
105
108
  oj_get_odd(VALUE clas) {
106
- Odd odd = odds;
109
+ Odd odd;
107
110
 
108
- for (; Qundef != odd->clas; odd++) {
111
+ for (odd = odds + odd_cnt - 1; odds <= odd; odd--) {
109
112
  if (clas == odd->clas) {
110
113
  return odd;
111
114
  }
@@ -115,9 +118,9 @@ oj_get_odd(VALUE clas) {
115
118
 
116
119
  Odd
117
120
  oj_get_oddc(const char *classname, size_t len) {
118
- Odd odd = odds;
121
+ Odd odd;
119
122
 
120
- for (; Qundef != odd->clas; odd++) {
123
+ for (odd = odds + odd_cnt - 1; odds <= odd; odd--) {
121
124
  if (len == odd->clen && 0 == strncmp(classname, odd->classname, len)) {
122
125
  return odd;
123
126
  }
@@ -157,3 +160,42 @@ oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value) {
157
160
  }
158
161
  return -1;
159
162
  }
163
+
164
+ void
165
+ oj_reg_odd(VALUE clas, VALUE create_method, int mcnt, VALUE *members) {
166
+ Odd odd;
167
+ const char **np;
168
+ ID *ap;
169
+
170
+ if (_odds == odds) {
171
+ odds = ALLOC_N(struct _Odd, odd_cnt + 1);
172
+
173
+ memcpy(odds, _odds, sizeof(struct _Odd) * odd_cnt);
174
+ } else {
175
+ REALLOC_N(odds, struct _Odd, odd_cnt + 1);
176
+ }
177
+ odd = odds + odd_cnt;
178
+ odd->clas = clas;
179
+ odd->classname = strdup(rb_class2name(clas));
180
+ odd->clen = strlen(odd->classname);
181
+ odd->create_obj = clas;
182
+ odd->create_op = SYM2ID(create_method);
183
+ odd->attr_cnt = mcnt;
184
+ for (ap = odd->attrs, np = odd->attr_names; 0 < mcnt; mcnt--, ap++, np++, members++) {
185
+ switch (rb_type(*members)) {
186
+ case T_STRING:
187
+ *np = strdup(rb_string_value_ptr(members));
188
+ break;
189
+ case T_SYMBOL:
190
+ *np = rb_id2name(SYM2ID(*members));
191
+ break;
192
+ default:
193
+ rb_raise(rb_eArgError, "registered member identifiers must be Strings or Symbols.");
194
+ break;
195
+ }
196
+ *ap = rb_intern(*np);
197
+ }
198
+ *np = 0;
199
+ *ap = 0;
200
+ odd_cnt++;
201
+ }
@@ -57,5 +57,6 @@ extern Odd oj_get_oddc(const char *classname, size_t len);
57
57
  extern OddArgs oj_odd_alloc_args(Odd odd);
58
58
  extern void oj_odd_free(OddArgs args);
59
59
  extern int oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value);
60
+ extern void oj_reg_odd(VALUE clas, VALUE create_method, int mcnt, VALUE *members);
60
61
 
61
62
  #endif /* __OJ_ODD_H__ */
@@ -841,6 +841,36 @@ to_stream(int argc, VALUE *argv, VALUE self) {
841
841
  return Qnil;
842
842
  }
843
843
 
844
+ /* call-seq: register_odd(clas, create_method, *members)
845
+ *
846
+ * Registers a class as special. This is useful for working around subclasses of
847
+ * primitive types as is done with ActiveSupport classes. The use of this
848
+ * function should be limited to just classes that can not be handled in the
849
+ * normal way. It is not intended as a hook for changing the output of all
850
+ * classes as it is not optimized for large numbers of classes.
851
+ *
852
+ * @param [Class] clas Class to me made special
853
+ * @param [Symbol] create_method method on the clas that will create a new
854
+ * instance of the clas when given all the member values in the
855
+ * order specified.
856
+ * @param [Symbol|String] members methods used to get the member values from
857
+ * instances of the clas
858
+ */
859
+ static VALUE
860
+ register_odd(int argc, VALUE *argv, VALUE self) {
861
+ if (2 > argc) {
862
+ rb_raise(rb_eArgError, "incorrect number of arguments.");
863
+ }
864
+ Check_Type(argv[0], T_CLASS);
865
+ Check_Type(argv[1], T_SYMBOL);
866
+ if (MAX_ODD_ARGS < argc - 1) {
867
+ rb_raise(rb_eArgError, "too many members.");
868
+ }
869
+ oj_reg_odd(argv[0], argv[1], argc - 2, argv + 2);
870
+
871
+ return Qnil;
872
+ }
873
+
844
874
  static void
845
875
  str_writer_free(void *ptr) {
846
876
  StrWriter sw;
@@ -1804,6 +1834,7 @@ void Init_oj() {
1804
1834
  rb_define_module_function(Oj, "dump", dump, -1);
1805
1835
  rb_define_module_function(Oj, "to_file", to_file, -1);
1806
1836
  rb_define_module_function(Oj, "to_stream", to_stream, -1);
1837
+ rb_define_module_function(Oj, "register_odd", register_odd, -1);
1807
1838
 
1808
1839
  rb_define_module_function(Oj, "saj_parse", oj_saj_parse, -1);
1809
1840
  rb_define_module_function(Oj, "sc_parse", oj_sc_parse, -1);
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '2.7.3'
4
+ VERSION = '2.8.0'
5
5
  end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby -wW1
2
+ # encoding: UTF-8
3
+
4
+ $: << File.dirname(__FILE__)
5
+ $: << File.join(File.dirname(__FILE__), "../lib")
6
+ $: << File.join(File.dirname(__FILE__), "../ext")
7
+
8
+ require 'pp'
9
+ require 'oj'
10
+ require 'perf'
11
+
12
+ obj = [[1],[2],[3],[4],[5],[6],[7],[8],[9]]
13
+ obj = [[],[],[],[],[],[],[],[],[]]
14
+ obj = {
15
+ 'a' => 'Alpha', # string
16
+ 'b' => true, # boolean
17
+ 'c' => 12345, # number
18
+ 'd' => [ true, [false, [12345, nil], 3.967, ['something', false], nil]], # mix it up array
19
+ 'e' => { 'one' => 1, 'two' => 2 }, # hash
20
+ 'f' => nil, # nil
21
+ 'g' => 12345678901234567890123456789, # big number
22
+ 'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
23
+ 'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
24
+ }
25
+
26
+ json = Oj.dump(obj, mode: :compat)
27
+
28
+ puts json
29
+ #pp Oj.saj_parse(nil, json)
30
+ pp Oj.t_parse(json)
31
+
32
+ if true
33
+ perf = Perf.new()
34
+ perf.add('SAJ', 'oj') { Oj.saj_parse(nil, json) }
35
+ perf.add('T', 'oj') { Oj.t_parse(json) }
36
+ perf.add('load', 'oj') { Oj.load(json) }
37
+ perf.run(10000)
38
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
5
+ # required. That can be set in the RUBYOPT environment variable.
6
+ # export RUBYOPT=-w
7
+
8
+ $VERBOSE = true
9
+
10
+ $: << File.join(File.dirname(__FILE__), "../lib")
11
+ $: << File.join(File.dirname(__FILE__), "../ext")
12
+
13
+ require 'oj'
14
+
15
+ obj = Oj.load_file('bug.json', :mode => :object)
16
+
17
+ puts Oj.dump(obj, :mode => :object, :indent => 0)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.join(File.dirname(__FILE__), "../lib")
4
+ $: << File.join(File.dirname(__FILE__), "../ext")
5
+
6
+ require 'oj'
7
+
8
+ json = %{"\xc2\xa9\xc3\x98"}
9
+ puts "original:\n#{json}"
10
+
11
+ str = Oj.load(json)
12
+ puts "out: #{str}"
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
5
+ # required. That can be set in the RUBYOPT environment variable.
6
+ # export RUBYOPT=-w
7
+
8
+ $VERBOSE = true
9
+
10
+ $: << File.join(File.dirname(__FILE__), "../lib")
11
+ $: << File.join(File.dirname(__FILE__), "../ext")
12
+
13
+ require 'oj'
14
+
15
+ reltypes={}
16
+ Oj::Doc.open_file('foo.json') do |doc|
17
+ doc.each_child do |target|
18
+ puts "#{target.local_key} is #{target.local_key.class}"
19
+ target.each_leaf do |score|
20
+ reltype=score.local_key
21
+ reltypes[reltype] = (reltypes[reltype] || 0) + 1
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
5
+ # required. That can be set in the RUBYOPT environment variable.
6
+ # export RUBYOPT=-w
7
+
8
+ $VERBOSE = true
9
+
10
+ $: << File.join(File.dirname(__FILE__), "../lib")
11
+ $: << File.join(File.dirname(__FILE__), "../ext")
12
+
13
+ require 'oj'
14
+
15
+ module One
16
+ module Two
17
+ module Three
18
+ class Empty
19
+
20
+ def initialize()
21
+ end
22
+
23
+ def eql?(o)
24
+ self.class == o.class
25
+ end
26
+ alias == eql?
27
+
28
+ def to_hash()
29
+ {'json_class' => "#{self.class.name}"}
30
+ end
31
+
32
+ def to_json(*a)
33
+ %{{"json_class":"#{self.class.name}"}}
34
+ end
35
+
36
+ def self.json_create(h)
37
+ self.new()
38
+ end
39
+ end # Empty
40
+ end # Three
41
+ end # Two
42
+ end # One
43
+
44
+ $obj = {
45
+ 'a' => 'Alpha', # string
46
+ 'b' => true, # boolean
47
+ 'c' => 12345, # number
48
+ 'd' => [ true, [false, [-123456789, nil], 3.9676, ['Something else.', false], nil]], # mix it up array
49
+ 'e' => { 'zero' => nil, 'one' => 1, 'two' => 2, 'three' => [3], 'four' => [0, 1, 2, 3, 4] }, # hash
50
+ 'f' => nil, # nil
51
+ 'g' => One::Two::Three::Empty.new(),
52
+ 'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
53
+ 'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
54
+ }
55
+
56
+ $obj = [$obj]*10000
57
+
58
+ Oj.default_options = { :indent => 2, :mode => :compat }
59
+
60
+ $json = Oj.dump($obj, :mode => :compat)
61
+
62
+ $result = nil
63
+ 100.times { |i|
64
+ print(".") if (0 == i % 10)
65
+ $result = Oj.compat_load($json)
66
+ }
67
+
68
+
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ $: << File.join(File.dirname(__FILE__), "../lib")
5
+ $: << File.join(File.dirname(__FILE__), "../ext")
6
+ # $: << File.join(File.dirname(__FILE__), "../../multi_json/lib")
7
+
8
+ require 'multi_json'
9
+ require 'benchmark'
10
+ require 'yajl'
11
+ require 'json'
12
+ require 'oj'
13
+
14
+ iter = 1_000_000
15
+ iter = 100_000
16
+
17
+ json = %({"k1":"val1","k2":"val2","k3":"val3"})
18
+ obj = { k1: "val1", k2: "val2", k3: "val3" }
19
+
20
+ puts "Benchmarks for different JSON handlers with MultiJson."
21
+ puts " Ruby #{RUBY_VERSION}"
22
+ puts " #{iter} iterations"
23
+
24
+ MultiJson.engine = :oj
25
+ dt = Benchmark.realtime { iter.times { MultiJson.decode(json) }}
26
+ et = Benchmark.realtime { iter.times { MultiJson.encode(obj) }}
27
+ puts " Oj decode: #{dt} encode: #{et}"
28
+
29
+ MultiJson.engine = :yajl
30
+ dt = Benchmark.realtime { iter.times { MultiJson.decode(json) }}
31
+ et = Benchmark.realtime { iter.times { MultiJson.encode(obj) }}
32
+ puts " Yajl decode: #{dt} encode: #{et}"
33
+
34
+ MultiJson.engine = :json_gem
35
+ dt = Benchmark.realtime { iter.times { MultiJson.decode(json) }}
36
+ et = Benchmark.realtime { iter.times { MultiJson.encode(obj) }}
37
+ puts " Json decode: #{dt} encode: #{et}"
38
+
39
+ Oj.default_options = { :mode => :compat, :time_format => :ruby }
40
+ dt = Benchmark.realtime { iter.times { Oj.load(json) }}
41
+ et = Benchmark.realtime { iter.times { Oj.dump(obj) }}
42
+ puts "Raw Oj decode: #{dt} encode: #{et}"
43
+
44
+ ye = Yajl::Encoder.new
45
+ dt = Benchmark.realtime { iter.times { Yajl::Parser.parse(json) }}
46
+ et = Benchmark.realtime { iter.times { Yajl::Encoder.encode(obj) }}
47
+ e2 = Benchmark.realtime { iter.times { ye.encode(obj) }}
48
+ puts "Raw Yajl decode: #{dt} encode: #{et}, encoder: #{e2}"
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
5
+ # required. That can be set in the RUBYOPT environment variable.
6
+ # export RUBYOPT=-w
7
+
8
+ $VERBOSE = true
9
+
10
+ $: << File.join(File.dirname(__FILE__), "../lib")
11
+ $: << File.join(File.dirname(__FILE__), "../ext")
12
+
13
+ require 'oj'
14
+
15
+ A = Struct.new(:a,:b,:c,:d)
16
+ B = Struct.new(:e,:f)
17
+
18
+ obj = [A.new(55, B.new(1, 'X'), B.new(2, 'Y'), 3)]
19
+
20
+ s = Oj.dump(obj, :mode => :object)
21
+
22
+ 100000.times do
23
+ Oj.load(s, :mode => :object)
24
+ # ds = Oj.dump(o, :mode => :object)
25
+ # if ds != s
26
+ # puts ds
27
+ # raise "holy crap"
28
+ # end
29
+ end
@@ -77,6 +77,31 @@ end # One
77
77
  class Stuck < Struct.new(:a, :b)
78
78
  end
79
79
 
80
+ class Strung < String
81
+
82
+ def initialize(str, safe)
83
+ super(str)
84
+ @safe = safe
85
+ end
86
+
87
+ def safe?()
88
+ @safe
89
+ end
90
+
91
+ def self.create(str, safe)
92
+ new(str, safe)
93
+ end
94
+
95
+ def eql?(o)
96
+ super && self.class == o.class && @safe == o.safe?
97
+ end
98
+ alias == eql?
99
+
100
+ def inspect()
101
+ return super + '(' + @safe + ')'
102
+ end
103
+ end
104
+
80
105
  def hash_eql(h1, h2)
81
106
  return false if h1.size != h2.size
82
107
  h1.keys.each do |k|
@@ -390,6 +415,12 @@ class ObjectJuice < ::Test::Unit::TestCase
390
415
  dump_and_load(Date.new(2012, 6, 19), false)
391
416
  end
392
417
 
418
+ def test_odd_string
419
+ Oj.register_odd(Strung, :create, :to_s, 'safe?')
420
+ s = Strung.new("Pete", true)
421
+ dump_and_load(Strung.new("Pete", true), false)
422
+ end
423
+
393
424
  def dump_and_load(obj, trace=false)
394
425
  json = Oj.dump(obj, :indent => 2, :mode => :object)
395
426
  puts json if trace
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
5
+ # required. That can be set in the RUBYOPT environment variable.
6
+ # export RUBYOPT=-w
7
+
8
+ $VERBOSE = true
9
+
10
+ $: << File.join(File.dirname(__FILE__), "../lib")
11
+ $: << File.join(File.dirname(__FILE__), "../ext")
12
+
13
+ require 'oj'
14
+
15
+ # Oj is not able to deserialize all classes that are a subclass of a Ruby
16
+ # Exception. Only exception that take one required string argument in the
17
+ # initialize() method are supported. This is an example of how to write an
18
+ # Exception subclass that supports both a single string intializer and an
19
+ # Exception as an argument. Additional optional arguments can be added as well.
20
+ #
21
+ # The reason for this restriction has to do with a design decision on the part
22
+ # of the Ruby developers. Exceptions are special Objects. They do not follow the
23
+ # rules of other Objects. Exceptions have 'mesg' and a 'bt' attribute. Note that
24
+ # these are not '@mesg' and '@bt'. They can not be set using the normal C or
25
+ # Ruby calls. The only way I have found to set the 'mesg' attribute is through
26
+ # the initializer. Unfortunately that means any subclass that provides a
27
+ # different initializer can not be automatically decoded. A way around this is
28
+ # to use a create function but this example shows an alternative.
29
+
30
+ class WrapException < StandardError
31
+ attr_reader :original
32
+
33
+ def initialize(msg_or_err)
34
+ if msg_or_err.is_a?(Exception)
35
+ super(msg_or_err.message)
36
+ @original = msg_or_err
37
+ set_backtrace(msg_or_err.backtrace)
38
+ else
39
+ super(message)
40
+ @original = nil
41
+ end
42
+ end
43
+ end
44
+
45
+ e = WrapException.new(RuntimeError.new("Something broke."))
46
+
47
+ json = Oj.dump(e, :mode => :object)
48
+ puts "original:\n#{json}"
49
+ # outputs:
50
+ # original:
51
+ # {"^o":"WrapException","original":{"^o":"RuntimeError","~mesg":"Something broke.","~bt":null},"~mesg":"Something broke.","~bt":null}
52
+
53
+ e2 = Oj.load(json, :mode => :object)
54
+ puts "dumped, loaded, and dumped again:\n#{Oj.dump(e2, :mode => :object)}"
55
+ # outputs:
56
+ # original: {"^o":"WrapException","original":{"^o":"RuntimeError","~mesg":"Something broke.","~bt":null},"~mesg":"Something broke.","~bt":null}
57
+ # dumped, loaded, and dumped again:
58
+ # {"^o":"WrapException","original":{"^o":"RuntimeError","~mesg":"Something broke.","~bt":null},"~mesg":"Something broke.","~bt":null}
59
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.3
4
+ version: 2.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-11 00:00:00.000000000 Z
11
+ date: 2014-04-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'The fastest JSON parser and object serializer. '
14
14
  email: peter@ohler.com
@@ -56,14 +56,17 @@ files:
56
56
  - lib/oj/saj.rb
57
57
  - lib/oj/schandler.rb
58
58
  - lib/oj/version.rb
59
+ - test/a.rb
60
+ - test/bug.rb
59
61
  - test/debian_test.rb
62
+ - test/e.rb
60
63
  - test/files.rb
64
+ - test/foo.rb
65
+ - test/lots.rb
66
+ - test/mj.rb
61
67
  - test/perf.rb
62
- - test/perf1.rb
63
- - test/perf2.rb
64
68
  - test/perf_compat.rb
65
69
  - test/perf_fast.rb
66
- - test/perf_obj_old.rb
67
70
  - test/perf_object.rb
68
71
  - test/perf_saj.rb
69
72
  - test/perf_scp.rb
@@ -83,6 +86,7 @@ files:
83
86
  - test/sample/shape.rb
84
87
  - test/sample/text.rb
85
88
  - test/sample_json.rb
89
+ - test/struct.rb
86
90
  - test/test_compat.rb
87
91
  - test/test_fast.rb
88
92
  - test/test_gc.rb
@@ -94,6 +98,7 @@ files:
94
98
  - test/test_strict.rb
95
99
  - test/test_writer.rb
96
100
  - test/tests.rb
101
+ - test/x.rb
97
102
  homepage: http://www.ohler.com/oj
98
103
  licenses:
99
104
  - MIT
@@ -123,3 +128,4 @@ signing_key:
123
128
  specification_version: 4
124
129
  summary: A fast JSON parser and serializer.
125
130
  test_files: []
131
+ has_rdoc: true
@@ -1,64 +0,0 @@
1
- #!/usr/bin/env ruby -wW1
2
- # encoding: UTF-8
3
-
4
- $: << File.join(File.dirname(__FILE__), "../lib")
5
- $: << File.join(File.dirname(__FILE__), "../ext")
6
-
7
- #require 'test/unit'
8
- require 'optparse'
9
- require 'oj'
10
- require 'ox'
11
-
12
- $indent = 2
13
-
14
- opts = OptionParser.new
15
- opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
16
- files = opts.parse(ARGV)
17
-
18
- iter = 100000
19
- s = %{
20
- { "class": "Foo::Bar",
21
- "attr1": [ true, [false, [12345, null], 3.967, ["something", false], null]],
22
- "attr2": { "one": 1 }
23
- }
24
- }
25
- #s = File.read('sample.json')
26
-
27
- Oj.default_options = { :indent => 0 }
28
-
29
- obj = Oj.load(s)
30
- xml = Ox.dump(obj, :indent => 0)
31
-
32
- puts xml
33
-
34
- start = Time.now
35
- iter.times do
36
- Oj.load(s)
37
- end
38
- dt = Time.now - start
39
- puts "%d Oj.load()s in %0.3f seconds or %0.1f loads/msec" % [iter, dt, iter/dt/1000.0]
40
-
41
- start = Time.now
42
- iter.times do
43
- Ox.load(xml)
44
- end
45
- dt = Time.now - start
46
- puts "%d Ox.load()s in %0.3f seconds or %0.1f loads/msec" % [iter, dt, iter/dt/1000.0]
47
-
48
- puts
49
-
50
- start = Time.now
51
- iter.times do
52
- Oj.dump(obj)
53
- end
54
- dt = Time.now - start
55
- puts "%d Oj.dump()s in %0.3f seconds or %0.1f dumps/msec" % [iter, dt, iter/dt/1000.0]
56
-
57
- start = Time.now
58
- iter.times do
59
- Ox.dump(obj)
60
- end
61
- dt = Time.now - start
62
- puts "%d Ox.dump()s in %0.3f seconds or %0.1f dumps/msec" % [iter, dt, iter/dt/1000.0]
63
-
64
- puts
@@ -1,76 +0,0 @@
1
- #!/usr/bin/env ruby -wW1
2
- # encoding: UTF-8
3
-
4
- $: << File.join(File.dirname(__FILE__), "../lib")
5
- $: << File.join(File.dirname(__FILE__), "../ext")
6
-
7
- #require 'test/unit'
8
- require 'optparse'
9
- require 'yajl'
10
- require 'oj'
11
-
12
- $indent = 2
13
-
14
- opts = OptionParser.new
15
- opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
16
- files = opts.parse(ARGV)
17
-
18
- class Foo
19
- def initialize()
20
- @x = true
21
- @y = 58
22
- end
23
- def to_json()
24
- %{{"x":#{@x},"y":#{@y}}}
25
- end
26
- def to_hash()
27
- { 'x' => @x, 'y' => @y }
28
- end
29
- end
30
-
31
- iter = 100000
32
- s = %{
33
- { "class": "Foo::Bar",
34
- "attr1": [ true, [false, [12345, null], 3.967, ["something", false], null]],
35
- "attr2": { "one": 1 }
36
- }
37
- }
38
-
39
- obj = Oj.load(s)
40
- obj["foo"] = Foo.new()
41
-
42
- Oj.default_options = { :indent => 0, :effort => :internal }
43
-
44
- puts
45
-
46
- start = Time.now
47
- iter.times do
48
- Oj.load(s)
49
- end
50
- dt = Time.now - start
51
- puts "%d Oj.load()s in %0.3f seconds or %0.1f loads/msec" % [iter, dt, iter/dt/1000.0]
52
-
53
- start = Time.now
54
- iter.times do
55
- Yajl::Parser.parse(s)
56
- end
57
- dt = Time.now - start
58
- puts "%d Yajl::Parser.parse()s in %0.3f seconds or %0.1f parses/msec" % [iter, dt, iter/dt/1000.0]
59
-
60
- puts
61
-
62
- start = Time.now
63
- iter.times do
64
- Oj.dump(obj)
65
- end
66
- dt = Time.now - start
67
- puts "%d Oj.dump()s in %0.3f seconds or %0.1f dumps/msec" % [iter, dt, iter/dt/1000.0]
68
-
69
- start = Time.now
70
- iter.times do
71
- Yajl::Encoder.encode(obj)
72
- end
73
- dt = Time.now - start
74
- puts "%d Yajl::Encoder.encode()s in %0.3f seconds or %0.1f encodes/msec" % [iter, dt, iter/dt/1000.0]
75
-
76
- puts
@@ -1,213 +0,0 @@
1
- #!/usr/bin/env ruby -wW1
2
-
3
- $: << '.'
4
- $: << '..'
5
- $: << '../lib'
6
- $: << '../ext'
7
-
8
- if __FILE__ == $0
9
- if (i = ARGV.index('-I'))
10
- x,path = ARGV.slice!(i, 2)
11
- $: << path
12
- end
13
- end
14
-
15
- require 'optparse'
16
- require 'ox'
17
- require 'oj'
18
- require 'perf'
19
- require 'sample'
20
- require 'files'
21
-
22
- $verbose = 0
23
- $circular = false
24
- $indent = 0
25
-
26
- do_sample = false
27
- do_files = false
28
-
29
- do_load = false
30
- do_dump = false
31
- do_read = false
32
- do_write = false
33
- $iter = 1000
34
-
35
- opts = OptionParser.new
36
- opts.on("-v", "increase verbosity") { $verbose += 1 }
37
-
38
- opts.on("-c", "circular options") { $circular = true }
39
-
40
- opts.on("-s", "load and dump as sample Ruby object") { do_sample = true }
41
- opts.on("-f", "load and dump as files Ruby object") { do_files = true }
42
-
43
- opts.on("-l", "load") { do_load = true }
44
- opts.on("-d", "dump") { do_dump = true }
45
- opts.on("-r", "read") { do_read = true }
46
- opts.on("-w", "write") { do_write = true }
47
- opts.on("-a", "load, dump, read and write") { do_load = true; do_dump = true; do_read = true; do_write = true }
48
-
49
- opts.on("-i", "--iterations [Int]", Integer, "iterations") { |i| $iter = i }
50
-
51
- opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
52
- files = opts.parse(ARGV)
53
-
54
- if files.empty?
55
- data = []
56
- obj = do_sample ? sample_doc(2) : files('..')
57
- mars = Marshal.dump(obj)
58
- xml = Ox.dump(obj, :indent => $indent, circular: $circular)
59
- json = Oj.dump(obj, :indent => $indent, circular: $circular)
60
- File.open('sample.xml', 'w') { |f| f.write(xml) }
61
- File.open('sample.json', 'w') { |f| f.write(json) }
62
- File.open('sample.marshal', 'w') { |f| f.write(mars) }
63
- data << { :file => 'sample.xml', :obj => obj, :xml => xml, :marshal => mars, :json => json }
64
- else
65
- puts "loading and parsing #{files}\n\n"
66
- # TBD change to allow xml and json
67
- data = files.map do |f|
68
- xml = File.read(f)
69
- obj = Ox.load(xml);
70
- mars = Marshal.dump(obj)
71
- json = Oj.dump(obj, :indent => $indent, circular: $circular)
72
- { :file => f, :obj => obj, :xml => xml, :marshal => mars, :json => json }
73
- end
74
- end
75
-
76
- $ox_load_time = 0
77
- $mars_load_time = 0
78
- $ox_dump_time = 0
79
- $oj_dump_time = 0
80
- $mars_dump_time = 0
81
-
82
- def perf_load(d)
83
- filename = d[:file]
84
- marshal_filename = 'sample.marshal'
85
- xml = d[:xml]
86
- mars = d[:marshal]
87
- json = d[:json]
88
-
89
- if 0 < $verbose
90
- obj = Ox.load(xml, :mode => :object, :trace => $verbose)
91
- return
92
- end
93
- start = Time.now
94
- (1..$iter).each do
95
- obj = Ox.load(xml, :mode => :object)
96
- end
97
- $ox_load_time = Time.now - start
98
- puts "Parsing #{$iter} times with Ox took #{$ox_load_time} seconds."
99
-
100
- start = Time.now
101
- (1..$iter).each do
102
- obj = Oj.load(json, :mode => :object)
103
- end
104
- $oj_load_time = Time.now - start
105
- puts "Parsing #{$iter} times with Oj took #{$oj_load_time} seconds."
106
-
107
- start = Time.now
108
- (1..$iter).each do
109
- obj = Marshal.load(mars)
110
- end
111
- $mars_load_time = Time.now - start
112
- puts "Marshalling #{$iter} times took #{$mars_load_time} seconds."
113
- puts ">>> Ox is %0.1f faster than Marshal loading.\n\n" % [$mars_load_time/$ox_load_time]
114
- end
115
-
116
- def perf_dump(d)
117
- obj = d[:obj]
118
-
119
- start = Time.now
120
- (1..$iter).each do
121
- xml = Ox.dump(obj, :indent => $indent, :circular => $circular)
122
- #puts "*** ox:\n#{xml}"
123
- end
124
- $ox_dump_time = Time.now - start
125
- puts "Ox dumping #{$iter} times with ox took #{$ox_dump_time} seconds."
126
-
127
- Oj.default_options = {:indent => $indent}
128
- start = Time.now
129
- (1..$iter).each do
130
- json = Oj.dump(obj)
131
- end
132
- $oj_dump_time = Time.now - start
133
- puts "Oj dumping #{$iter} times with oj took #{$oj_dump_time} seconds."
134
-
135
- obj = d[:obj]
136
- start = Time.now
137
- (1..$iter).each do
138
- m = Marshal.dump(obj)
139
- end
140
- $mars_dump_time = Time.now - start
141
- puts "Marshal dumping #{$iter} times took #{$mars_dump_time} seconds."
142
- puts ">>> Ox is %0.1f faster than Marshal dumping.\n\n" % [$mars_dump_time/$ox_dump_time]
143
- end
144
-
145
- def perf_read(d)
146
- ox_read_time = 0
147
- mars_read_time = 0
148
-
149
- filename = d[:file]
150
- marshal_filename = 'sample.marshal'
151
- xml = d[:xml]
152
- mars = d[:marshal]
153
-
154
- # now load from the file
155
- start = Time.now
156
- (1..$iter).each do
157
- obj = Ox.load_file(filename, :mode => :object)
158
- end
159
- ox_read_time = Time.now - start
160
- puts "Loading and parsing #{$iter} times with ox took #{ox_read_time} seconds."
161
-
162
- start = Time.now
163
- (1..$iter).each do
164
- m = File.read(marshal_filename)
165
- obj = Marshal.load(m)
166
- end
167
- mars_read_time = Time.now - start
168
- puts "Reading and marshalling #{$iter} times took #{mars_read_time} seconds."
169
- puts ">>> Ox is %0.1f faster than Marshal loading and parsing.\n\n" % [mars_read_time/ox_read_time]
170
-
171
- end
172
-
173
- def perf_write(d)
174
- ox_write_time = 0
175
- mars_write_time = 0
176
-
177
- ox_filename = 'out.xml'
178
- marshal_filename = 'out.marshal'
179
- obj = d[:obj]
180
-
181
- start = Time.now
182
- (1..$iter).each do
183
- xml = Ox.to_file(ox_filename, obj, :indent => $indent)
184
- end
185
- ox_write_time = Time.now - start
186
- puts "Ox dumping #{$iter} times with ox took #{ox_write_time} seconds."
187
-
188
- start = Time.now
189
- (1..$iter).each do
190
- m = Marshal.dump(obj, circular: $circular)
191
- File.open(marshal_filename, "w") { |f| f.write(m) }
192
- end
193
- mars_write_time = Time.now - start
194
- puts "Marshal dumping and writing #{$iter} times took #{mars_write_time} seconds."
195
- puts ">>> Ox is %0.1f faster than Marshal dumping.\n\n" % [mars_write_time/ox_write_time]
196
-
197
- end
198
-
199
- #if do_sample or do_files
200
- data.each do |d|
201
- puts "Using file #{d[:file]}."
202
-
203
- perf_load(d) if do_load
204
- perf_dump(d) if do_dump
205
- if do_load and do_dump
206
- puts ">>> Ox is %0.1f faster than Marshal dumping and loading.\n\n" % [($mars_load_time + $mars_dump_time)/($ox_load_time + $ox_dump_time)] unless 0 == $mars_load_time
207
- end
208
-
209
- perf_read(d) if do_read
210
- perf_write(d) if do_write
211
-
212
- end
213
- #end