Skip to content
Browse files

circular references now supported, optimization and format change is …

…next
  • Loading branch information...
1 parent fdacd8d commit 71c1fc57e7b975af7f4c40e2a46af7a4fdd07163 Peter Ohler committed
Showing with 88 additions and 27 deletions.
  1. +1 −1 README.md
  2. +28 −5 ext/oj/dump.c
  3. +35 −10 ext/oj/load.c
  4. +18 −5 notes
  5. +3 −3 test/perf_obj.rb
  6. +3 −3 test/tests.rb
View
2 README.md
@@ -281,7 +281,7 @@ Object being encoded. It is used when the :circular flag is set. It can appear
in either a JSON Object or in a JSON Array. In an Object the "^i" key has a
corresponding reference Fixnum. In an array the sequence will include an
embedded reference number. An example is
-{"^o":"Oj::Bag","^i":1,"x":["^i2":2,true],"me":{"^r":1}}.
+{"^o":"Oj::Bag","^i":1,"x":["^i2",true],"me":{"^r":1}}.
13. A "^r" JSON entry in an Object is a references to a Object or Array that
already appears in the JSON String. It must match up with a previous "^i"
View
33 ext/oj/dump.c
@@ -487,18 +487,41 @@ dump_class_obj(VALUE obj, Out out) {
static void
dump_array(VALUE a, int depth, Out out) {
- VALUE *np = RARRAY_PTR(a);
- size_t size = 2;
- int cnt = (int)RARRAY_LEN(a);
+ VALUE *np;
+ size_t size;
+ int cnt;
int d2 = depth + 1;
-
+ long id = check_circular(a, out);
+
+ if (id < 0) {
+ return;
+ }
+ // TBD check circular
+ np = RARRAY_PTR(a);
+ cnt = (int)RARRAY_LEN(a);
+ *out->cur++ = '[';
+ if (0 < id) {
+ size = d2 * out->indent + 16;
+ if (out->end - out->cur <= (long)size) {
+ grow(out, size);
+ }
+ fill_indent(out, d2);
+ *out->cur++ = '"';
+ *out->cur++ = '^';
+ *out->cur++ = 'i';
+ dump_ulong(id, out);
+ *out->cur++ = '"';
+ }
+ size = 2;
if (out->end - out->cur <= (long)size) {
grow(out, size);
}
- *out->cur++ = '[';
if (0 == cnt) {
*out->cur++ = ']';
} else {
+ if (0 < id) {
+ *out->cur++ = ',';
+ }
size = d2 * out->indent + 2;
for (; 0 < cnt; cnt--, np++) {
if (out->end - out->cur <= (long)size) {
View
45 ext/oj/load.c
@@ -198,6 +198,20 @@ structname2obj(const char *name) {
}
#endif
+inline static unsigned long
+read_ulong(const char *s, ParseInfo pi) {
+ unsigned long n = 0;
+
+ for (; '\0' != *s; s++) {
+ if ('0' <= *s && *s <= '9') {
+ n = n * 10 + (*s - '0');
+ } else {
+ raise_error("Not a valid ID number", pi->str, pi->s);
+ }
+ }
+ return n;
+}
+
static CircArray
circ_array_new() {
CircArray ca;
@@ -222,7 +236,7 @@ circ_array_free(CircArray ca) {
static void
circ_array_set(CircArray ca, VALUE obj, unsigned long id) {
- if (0 < id) {
+ if (0 < id && 0 != ca) {
unsigned long i;
if (ca->size < id) {
@@ -255,7 +269,7 @@ static VALUE
circ_array_get(CircArray ca, unsigned long id) {
VALUE obj = Qnil;
- if (id <= ca->cnt) {
+ if (id <= ca->cnt && 0 != ca) {
obj = ca->objs[id - 1];
}
return obj;
@@ -474,7 +488,7 @@ read_array(ParseInfo pi, int hint) {
VALUE a = Qundef;
VALUE e;
int type = T_NONE;
- int cnt;
+ int cnt = 0;
long slen = 0;
pi->s++;
@@ -483,7 +497,7 @@ read_array(ParseInfo pi, int hint) {
pi->s++;
return rb_ary_new();
}
- for (cnt = -1; 1; cnt++) {
+ while (1) {
if (Qundef == (e = read_next(pi, 0))) {
raise_error("unexpected character", pi->str, pi->s);
}
@@ -492,25 +506,36 @@ read_array(ParseInfo pi, int hint) {
a = structname2obj(StringValuePtr(e));
type = T_STRUCT;
slen = RSTRUCT_LEN(a);
+ e = Qundef;
}
#endif
if (Qundef == a) {
a = rb_ary_new();
type = T_ARRAY;
}
- if (T_STRUCT == type) {
+ if (T_STRING == rb_type(e)) {
+ const char *s = StringValuePtr(e);
+
+ if ('^' == *s && 'i' == s[1]) {
+ circ_array_set(pi->circ_array, a, read_ulong(s + 2, pi));
+ e = Qundef;
+ }
+ // TBD if begins with \^ then redo e one char shorter
+ }
+ if (Qundef != e) {
+ if (T_STRUCT == type) {
#ifdef NO_RSTRUCT
- raise_error("Ruby structs not supported with this verion of Ruby", pi->str, pi->s);
+ raise_error("Ruby structs not supported with this version of Ruby", pi->str, pi->s);
#else
- if (0 <= cnt) {
if (slen <= cnt) {
raise_error("Too many elements for Struct", pi->str, pi->s);
}
RSTRUCT_PTR(a)[cnt] = e;
- }
#endif
- } else {
- a = rb_ary_push(a, e);
+ } else {
+ a = rb_ary_push(a, e);
+ }
+ cnt++;
}
next_non_white(pi); // skip white space
if (',' == *pi->s) {
View
23 notes
@@ -7,11 +7,24 @@
- next
- circular reference
- - test
- - array
- - ^in for reference to current array
- - ^r for reference
- - perf
+ - change ref to ^r1234
+ - handle array element with "^i222" that is not an id
+ - handle value that is "^r222" that is not a reference
+
+ - compare
+ - yajl - core dumps ruby
+ - json fails
+ - msgpack fails
+
+
+- format
+ - escape ^ when reading and convert immediately to alternate sequences
+ - ^t12345.67890 for time value instead of hash?
+ - number is more natural for comatibility
+ - ^r12345 for references
+ - \: for symbol or reverse vs string
+ - ^i123 for array id
+
- stream
View
6 test/perf_obj.rb
@@ -63,8 +63,8 @@
if files.empty?
$obj = do_sample ? sample_doc(2) : files('..')
$mars = Marshal.dump($obj)
- $xml = Ox.dump($obj, :indent => $indent, circular: $circular)
- $json = Oj.dump($obj, :indent => $indent, circular: $circular)
+ $xml = Ox.dump($obj, :indent => $indent, :circular => $circular)
+ $json = Oj.dump($obj, :indent => $indent, :circular => $circular)
File.open('sample.xml', 'w') { |f| f.write($xml) }
File.open('sample.json', 'w') { |f| f.write($json) }
File.open('sample.marshal', 'w') { |f| f.write($mars) }
@@ -79,7 +79,7 @@
end
end
-Oj.default_options = { :mode => :object, :indent => $indent }
+Oj.default_options = { :mode => :object, :indent => $indent, :circular => $circular }
if do_load
puts '-' * 80
View
6 test/tests.rb
@@ -437,7 +437,7 @@ def test_circular_hash
assert_equal(h['b'].__id__, h.__id__)
end
- def xtest_circular_array
+ def test_circular_array
a = [7]
a << a
json = Oj.dump(a, :mode => :object, :indent => 2, :circular => true)
@@ -446,8 +446,8 @@ def xtest_circular_array
"^i1",
7,
{"^r":1}]}, json)
- h2 = Oj.load(json, :mode => :object, :circular => true)
- assert_equal(h['b'].__id__, h.__id__)
+ a2 = Oj.load(json, :mode => :object, :circular => true)
+ assert_equal(a[1].__id__, a.__id__)
end
def test_circular

0 comments on commit 71c1fc5

Please sign in to comment.
Something went wrong with that request. Please try again.