Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

circular references now supported, optimization and format change is …

…next
  • Loading branch information...
commit 71c1fc57e7b975af7f4c40e2a46af7a4fdd07163 1 parent fdacd8d
authored March 04, 2012
2  README.md
Source Rendered
@@ -281,7 +281,7 @@ Object being encoded. It is used when the :circular flag is set. It can appear
281 281
 in either a JSON Object or in a JSON Array. In an Object the "^i" key has a
282 282
 corresponding reference Fixnum. In an array the sequence will include an
283 283
 embedded reference number. An example is
284  
-{"^o":"Oj::Bag","^i":1,"x":["^i2":2,true],"me":{"^r":1}}.
  284
+{"^o":"Oj::Bag","^i":1,"x":["^i2",true],"me":{"^r":1}}.
285 285
 
286 286
 13. A "^r" JSON entry in an Object is a references to a Object or Array that
287 287
 already appears in the JSON String. It must match up with a previous "^i"
33  ext/oj/dump.c
@@ -487,18 +487,41 @@ dump_class_obj(VALUE obj, Out out) {
487 487
 
488 488
 static void
489 489
 dump_array(VALUE a, int depth, Out out) {
490  
-    VALUE       *np = RARRAY_PTR(a);
491  
-    size_t      size = 2;
492  
-    int		cnt = (int)RARRAY_LEN(a);
  490
+    VALUE	*np;
  491
+    size_t	size;
  492
+    int		cnt;
493 493
     int		d2 = depth + 1;
494  
-    
  494
+    long	id = check_circular(a, out);
  495
+
  496
+    if (id < 0) {
  497
+	return;
  498
+    }
  499
+    // TBD check circular
  500
+    np = RARRAY_PTR(a);
  501
+    cnt = (int)RARRAY_LEN(a);
  502
+    *out->cur++ = '[';
  503
+    if (0 < id) {
  504
+	size = d2 * out->indent + 16;
  505
+	if (out->end - out->cur <= (long)size) {
  506
+	    grow(out, size);
  507
+	}
  508
+	fill_indent(out, d2);
  509
+	*out->cur++ = '"';
  510
+	*out->cur++ = '^';
  511
+	*out->cur++ = 'i';
  512
+	dump_ulong(id, out);
  513
+	*out->cur++ = '"';
  514
+    }
  515
+    size = 2;
495 516
     if (out->end - out->cur <= (long)size) {
496 517
         grow(out, size);
497 518
     }
498  
-    *out->cur++ = '[';
499 519
     if (0 == cnt) {
500 520
 	*out->cur++ = ']';
501 521
     } else {
  522
+	if (0 < id) {
  523
+	    *out->cur++ = ',';
  524
+	}
502 525
 	size = d2 * out->indent + 2;
503 526
 	for (; 0 < cnt; cnt--, np++) {
504 527
 	    if (out->end - out->cur <= (long)size) {
45  ext/oj/load.c
@@ -198,6 +198,20 @@ structname2obj(const char *name) {
198 198
 }
199 199
 #endif
200 200
 
  201
+inline static unsigned long
  202
+read_ulong(const char *s, ParseInfo pi) {
  203
+    unsigned long	n = 0;
  204
+
  205
+    for (; '\0' != *s; s++) {
  206
+	if ('0' <= *s && *s <= '9') {
  207
+	    n = n * 10 + (*s - '0');
  208
+	} else {
  209
+	    raise_error("Not a valid ID number", pi->str, pi->s);
  210
+	}
  211
+    }
  212
+    return n;
  213
+}
  214
+
201 215
 static CircArray
202 216
 circ_array_new() {
203 217
     CircArray   ca;
@@ -222,7 +236,7 @@ circ_array_free(CircArray ca) {
222 236
 
223 237
 static void
224 238
 circ_array_set(CircArray ca, VALUE obj, unsigned long id) {
225  
-    if (0 < id) {
  239
+    if (0 < id && 0 != ca) {
226 240
         unsigned long   i;
227 241
 
228 242
         if (ca->size < id) {
@@ -255,7 +269,7 @@ static VALUE
255 269
 circ_array_get(CircArray ca, unsigned long id) {
256 270
     VALUE       obj = Qnil;
257 271
 
258  
-    if (id <= ca->cnt) {
  272
+    if (id <= ca->cnt && 0 != ca) {
259 273
         obj = ca->objs[id - 1];
260 274
     }
261 275
     return obj;
@@ -474,7 +488,7 @@ read_array(ParseInfo pi, int hint) {
474 488
     VALUE	a = Qundef;
475 489
     VALUE	e;
476 490
     int		type = T_NONE;
477  
-    int		cnt;
  491
+    int		cnt = 0;
478 492
     long	slen = 0;
479 493
 
480 494
     pi->s++;
@@ -483,7 +497,7 @@ read_array(ParseInfo pi, int hint) {
483 497
 	pi->s++;
484 498
 	return rb_ary_new();
485 499
     }
486  
-    for (cnt = -1; 1; cnt++) {
  500
+    while (1) {
487 501
 	if (Qundef == (e = read_next(pi, 0))) {
488 502
 	    raise_error("unexpected character", pi->str, pi->s);
489 503
 	}
@@ -492,25 +506,36 @@ read_array(ParseInfo pi, int hint) {
492 506
 	    a = structname2obj(StringValuePtr(e));
493 507
 	    type = T_STRUCT;
494 508
 	    slen = RSTRUCT_LEN(a);
  509
+	    e = Qundef;
495 510
 	}
496 511
 #endif
497 512
 	if (Qundef == a) {
498 513
 	    a = rb_ary_new();
499 514
 	    type = T_ARRAY;
500 515
 	}
501  
-	if (T_STRUCT == type) {
  516
+	if (T_STRING == rb_type(e)) {
  517
+	    const char	*s = StringValuePtr(e);
  518
+
  519
+	    if ('^' == *s && 'i' == s[1]) {
  520
+		circ_array_set(pi->circ_array, a, read_ulong(s + 2, pi));
  521
+		e = Qundef;
  522
+	    }
  523
+	    // TBD if begins with \^ then redo e one char shorter
  524
+	}
  525
+	if (Qundef != e) {
  526
+	    if (T_STRUCT == type) {
502 527
 #ifdef NO_RSTRUCT
503  
-	    raise_error("Ruby structs not supported with this verion of Ruby", pi->str, pi->s);
  528
+		raise_error("Ruby structs not supported with this version of Ruby", pi->str, pi->s);
504 529
 #else
505  
-	    if (0 <= cnt) {
506 530
 		if (slen <= cnt) {
507 531
 		    raise_error("Too many elements for Struct", pi->str, pi->s);
508 532
 		}
509 533
 		RSTRUCT_PTR(a)[cnt] = e;
510  
-	    }
511 534
 #endif
512  
-	} else {
513  
-	    a = rb_ary_push(a, e);
  535
+	    } else {
  536
+		a = rb_ary_push(a, e);
  537
+	    }
  538
+	    cnt++;
514 539
 	}
515 540
 	next_non_white(pi);	// skip white space
516 541
 	if (',' == *pi->s) {
23  notes
@@ -7,11 +7,24 @@
7 7
 - next
8 8
 
9 9
  - circular reference
10  
-  - test
11  
-   - array
12  
-    - ^in for reference to current array
13  
-    - ^r for reference
14  
-   - perf
  10
+  - change ref to ^r1234
  11
+  - handle array element with "^i222" that is not an id
  12
+  - handle value that is "^r222" that is not a reference
  13
+
  14
+  - compare
  15
+   - yajl - core dumps ruby
  16
+   - json fails
  17
+   - msgpack fails
  18
+
  19
+
  20
+- format
  21
+ - escape ^ when reading and convert immediately to alternate sequences
  22
+ - ^t12345.67890 for time value instead of hash?
  23
+  - number is more natural for comatibility
  24
+ - ^r12345 for references
  25
+ - \: for symbol or reverse vs string
  26
+ - ^i123 for array id
  27
+
15 28
 
16 29
 
17 30
  - stream
6  test/perf_obj.rb
@@ -63,8 +63,8 @@
63 63
 if files.empty?
64 64
   $obj = do_sample ? sample_doc(2) : files('..')
65 65
   $mars = Marshal.dump($obj)
66  
-  $xml = Ox.dump($obj, :indent => $indent, circular: $circular)
67  
-  $json = Oj.dump($obj, :indent => $indent, circular: $circular)
  66
+  $xml = Ox.dump($obj, :indent => $indent, :circular => $circular)
  67
+  $json = Oj.dump($obj, :indent => $indent, :circular => $circular)
68 68
   File.open('sample.xml', 'w') { |f| f.write($xml) }
69 69
   File.open('sample.json', 'w') { |f| f.write($json) }
70 70
   File.open('sample.marshal', 'w') { |f| f.write($mars) }
@@ -79,7 +79,7 @@
79 79
   end
80 80
 end
81 81
 
82  
-Oj.default_options = { :mode => :object, :indent => $indent }
  82
+Oj.default_options = { :mode => :object, :indent => $indent, :circular => $circular }
83 83
 
84 84
 if do_load
85 85
   puts '-' * 80
6  test/tests.rb
@@ -437,7 +437,7 @@ def test_circular_hash
437 437
     assert_equal(h['b'].__id__, h.__id__)
438 438
   end
439 439
 
440  
-  def xtest_circular_array
  440
+  def test_circular_array
441 441
     a = [7]
442 442
     a << a
443 443
     json = Oj.dump(a, :mode => :object, :indent => 2, :circular => true)
@@ -446,8 +446,8 @@ def xtest_circular_array
446 446
   "^i1",
447 447
   7,
448 448
   {"^r":1}]}, json)
449  
-    h2 = Oj.load(json, :mode => :object, :circular => true)
450  
-    assert_equal(h['b'].__id__, h.__id__)
  449
+    a2 = Oj.load(json, :mode => :object, :circular => true)
  450
+    assert_equal(a[1].__id__, a.__id__)
451 451
   end
452 452
 
453 453
   def test_circular

0 notes on commit 71c1fc5

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