fjson 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/CHANGES +5 -5
  2. data/README +7 -8
  3. data/Rakefile +56 -17
  4. data/TODO +1 -0
  5. data/ext/extensions/array_ext/array_ext.c +118 -0
  6. data/ext/extensions/array_ext/array_ext.h +12 -0
  7. data/ext/extensions/array_ext/extconf.rb +7 -0
  8. data/ext/extensions/false_class_ext/extconf.rb +5 -0
  9. data/ext/extensions/false_class_ext/false_class_ext.c +19 -0
  10. data/ext/extensions/false_class_ext/false_class_ext.h +4 -0
  11. data/ext/extensions/float_ext/extconf.rb +5 -0
  12. data/ext/extensions/float_ext/float_ext.c +19 -0
  13. data/ext/extensions/float_ext/float_ext.h +4 -0
  14. data/ext/extensions/hash_ext/extconf.rb +7 -0
  15. data/ext/extensions/hash_ext/hash_ext.c +128 -0
  16. data/ext/extensions/hash_ext/hash_ext.h +13 -0
  17. data/ext/extensions/integer_ext/extconf.rb +5 -0
  18. data/ext/extensions/integer_ext/integer_ext.c +19 -0
  19. data/ext/extensions/integer_ext/integer_ext.h +4 -0
  20. data/ext/extensions/nil_class_ext/extconf.rb +5 -0
  21. data/ext/extensions/nil_class_ext/nil_class_ext.c +19 -0
  22. data/ext/extensions/nil_class_ext/nil_class_ext.h +4 -0
  23. data/ext/extensions/object_ext/extconf.rb +5 -0
  24. data/ext/extensions/object_ext/object_ext.c +21 -0
  25. data/ext/extensions/object_ext/object_ext.h +4 -0
  26. data/ext/extensions/string_ext/extconf.rb +5 -0
  27. data/ext/extensions/string_ext/string_ext.c +27 -0
  28. data/ext/extensions/string_ext/string_ext.h +4 -0
  29. data/ext/extensions/true_class_ext/extconf.rb +5 -0
  30. data/ext/extensions/true_class_ext/true_class_ext.c +19 -0
  31. data/ext/extensions/true_class_ext/true_class_ext.h +4 -0
  32. data/ext/json_ext/extconf.rb +5 -0
  33. data/ext/json_ext/json_ext.c +180 -0
  34. data/ext/json_ext/json_ext.h +9 -0
  35. data/ext/state_ext/extconf.rb +5 -0
  36. data/ext/state_ext/state_ext.c +169 -0
  37. data/ext/state_ext/state_ext.h +14 -0
  38. data/hash_benchmark.rb +15 -0
  39. data/install.rb +21 -0
  40. data/rake_helper.rb +34 -0
  41. metadata +52 -20
  42. data/VERSION +0 -1
  43. data/lib/extensions/array_ext.so +0 -0
  44. data/lib/extensions/false_class_ext.so +0 -0
  45. data/lib/extensions/float_ext.so +0 -0
  46. data/lib/extensions/hash_ext.so +0 -0
  47. data/lib/extensions/integer_ext.so +0 -0
  48. data/lib/extensions/nil_class_ext.so +0 -0
  49. data/lib/extensions/object_ext.so +0 -0
  50. data/lib/extensions/string_ext.so +0 -0
  51. data/lib/extensions/true_class_ext.so +0 -0
  52. data/lib/json_ext.so +0 -0
  53. data/lib/state_ext.so +0 -0
@@ -0,0 +1,13 @@
1
+ #include "ruby.h"
2
+ #include <string.h>
3
+ #include "../../state_ext/state_ext.h"
4
+ #include "../../state_ext/state_ext.c"
5
+
6
+ static VALUE json_shift(VALUE, VALUE, VALUE, VALUE);
7
+ static VALUE generate_key_value_json(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE);
8
+ static VALUE process_internal_json(VALUE, VALUE, VALUE, VALUE, VALUE);
9
+ static VALUE json_transform(VALUE, VALUE, VALUE);
10
+ static VALUE json_check_circular_and_transform_call(VALUE);
11
+ static VALUE json_check_circular_and_transform_ensure(VALUE);
12
+ static VALUE json_check_circular_and_transform(VALUE, VALUE, VALUE);
13
+ static VALUE to_json(int, VALUE*, VALUE);
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'mkrf'
3
+
4
+ dir = File.dirname(__FILE__)
5
+ Mkrf::Generator.new('integer_ext', "#{dir}/*.c")
@@ -0,0 +1,19 @@
1
+ #include "integer_ext.h"
2
+
3
+ static VALUE cInteger;
4
+
5
+ void Init_integer_ext() {
6
+ cInteger = rb_const_get(rb_cObject, rb_intern("Integer"));
7
+ rb_define_method(cInteger, "to_json", (VALUE(*)(ANYARGS)) &to_json, -1);
8
+ }
9
+
10
+ /**
11
+ * Returns a JSON string representation for this Integer number.
12
+ */
13
+ static VALUE to_json(argc, argv, self)
14
+ int argc;
15
+ VALUE *argv;
16
+ VALUE self;
17
+ {
18
+ return rb_funcall(self, rb_intern("to_s"), 0);
19
+ }
@@ -0,0 +1,4 @@
1
+ #include "ruby.h"
2
+ #include <string.h>
3
+
4
+ static VALUE to_json(int, VALUE*, VALUE);
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'mkrf'
3
+
4
+ dir = File.dirname(__FILE__)
5
+ Mkrf::Generator.new('nil_class_ext', "#{dir}/*.c")
@@ -0,0 +1,19 @@
1
+ #include "nil_class_ext.h"
2
+
3
+ static VALUE cNilClass;
4
+
5
+ void Init_nil_class_ext() {
6
+ cNilClass = rb_const_get(rb_cObject, rb_intern("NilClass"));
7
+ rb_define_method(cNilClass, "to_json", (VALUE(*)(ANYARGS)) &to_json, -1);
8
+ }
9
+
10
+ /**
11
+ * Returns a JSON string for nil: 'null'.
12
+ */
13
+ static VALUE to_json(argc, argv, self)
14
+ int argc;
15
+ VALUE *argv;
16
+ VALUE self;
17
+ {
18
+ return rb_str_new2("null");
19
+ }
@@ -0,0 +1,4 @@
1
+ #include "ruby.h"
2
+ #include <string.h>
3
+
4
+ static VALUE to_json(int, VALUE*, VALUE);
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'mkrf'
3
+
4
+ dir = File.dirname(__FILE__)
5
+ Mkrf::Generator.new('object_ext', "#{dir}/*.c")
@@ -0,0 +1,21 @@
1
+ #include "object_ext.h"
2
+
3
+ void Init_object_ext() {
4
+ rb_define_method(rb_cObject, "to_json", (VALUE(*)(ANYARGS)) &to_json, -1);
5
+ }
6
+
7
+ /**
8
+ * Converts this object to a string (calling #to_s), converts
9
+ * it to a JSON string, and returns the result. This is a fallback, if no
10
+ * special method #to_json was defined for some object.
11
+ * _state_ is a JSON::State object, that can also be used
12
+ * to configure the produced JSON string output further.
13
+ */
14
+ static VALUE to_json(argc, argv, self)
15
+ int argc;
16
+ VALUE *argv;
17
+ VALUE self;
18
+ {
19
+ VALUE self_string = rb_funcall(self, rb_intern("to_s"), 0);
20
+ return rb_funcall(self_string, rb_intern("to_json"), 0);
21
+ }
@@ -0,0 +1,4 @@
1
+ #include "ruby.h"
2
+ #include <string.h>
3
+
4
+ static VALUE to_json(int, VALUE*, VALUE);
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'mkrf'
3
+
4
+ dir = File.dirname(__FILE__)
5
+ Mkrf::Generator.new('string_ext', "#{dir}/*.c")
@@ -0,0 +1,27 @@
1
+ #include "string_ext.h"
2
+
3
+ static VALUE cString;
4
+
5
+ void Init_string_ext() {
6
+ cString = rb_const_get(rb_cObject, rb_intern("String"));
7
+ rb_define_method(cString, "to_json", (VALUE(*)(ANYARGS)) &to_json, -1);
8
+ }
9
+
10
+ /**
11
+ * This string should be encoded with UTF-8 (if JSON unicode support is
12
+ * enabled). A call to this method returns a JSON string
13
+ * encoded with UTF16 big endian characters as \u????. If
14
+ * JSON.support_unicode? is false only control characters are encoded this
15
+ * way, all 8-bit bytes are just passed through.
16
+ */
17
+ static VALUE to_json(argc, argv, self)
18
+ int argc;
19
+ VALUE *argv;
20
+ VALUE self;
21
+ {
22
+ VALUE mJSON = rb_const_get(rb_cObject, rb_intern("JSON"));
23
+ VALUE json = rb_str_new2("\"");
24
+ rb_str_append(json, rb_funcall(mJSON, rb_intern("utf8_to_json"), 1, self));
25
+ rb_str_append(json, rb_str_new2("\""));
26
+ return json;
27
+ }
@@ -0,0 +1,4 @@
1
+ #include "ruby.h"
2
+ #include <string.h>
3
+
4
+ static VALUE to_json(int, VALUE*, VALUE);
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'mkrf'
3
+
4
+ dir = File.dirname(__FILE__)
5
+ Mkrf::Generator.new('true_class_ext', "#{dir}/*.c")
@@ -0,0 +1,19 @@
1
+ #include "true_class_ext.h"
2
+
3
+ static VALUE cTrueClass;
4
+
5
+ void Init_true_class_ext() {
6
+ cTrueClass = rb_const_get(rb_cObject, rb_intern("TrueClass"));
7
+ rb_define_method(cTrueClass, "to_json", (VALUE(*)(ANYARGS)) &to_json, -1);
8
+ }
9
+
10
+ /**
11
+ * Returns a JSON string for true: 'true'.
12
+ */
13
+ static VALUE to_json(argc, argv, self)
14
+ int argc;
15
+ VALUE *argv;
16
+ VALUE self;
17
+ {
18
+ return rb_funcall(self, rb_intern("to_s"), 0);
19
+ }
@@ -0,0 +1,4 @@
1
+ #include "ruby.h"
2
+ #include <string.h>
3
+
4
+ static VALUE to_json(int, VALUE*, VALUE);
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'mkrf'
3
+
4
+ dir = File.dirname(__FILE__)
5
+ Mkrf::Generator.new('json_ext', "#{dir}/*.c")
@@ -0,0 +1,180 @@
1
+ #include "json_ext.h"
2
+
3
+ static VALUE mJSON;
4
+ static VALUE mJSONModuleMethods;
5
+ static VALUE cStandardError;
6
+ static VALUE cJSONError;
7
+ static VALUE cParserError;
8
+ static VALUE cUnparserError;
9
+ static VALUE cCircularDatastructure;
10
+
11
+ void Init_json_ext() {
12
+ mJSON = rb_define_module("JSON");
13
+ mJSONModuleMethods = rb_define_module_under(mJSON, "ModuleMethods");
14
+
15
+ _begin_setup_iconv();
16
+
17
+ rb_define_method(mJSONModuleMethods, "utf8_to_json", utf8_to_json, 1);
18
+ rb_define_method(mJSONModuleMethods, "utf8_to_utf16", utf8_to_utf16, 1);
19
+
20
+ cStandardError = rb_const_get(rb_cObject,rb_intern("StandardError"));
21
+ /**
22
+ * The base exception for JSON errors.
23
+ */
24
+ cJSONError = rb_define_class_under(mJSON, "JSONError", cStandardError);
25
+
26
+ /**
27
+ * This exception is raise, if a parser error occurs.
28
+ */
29
+ cParserError = rb_define_class_under(mJSON, "ParserError", cJSONError);
30
+
31
+ /**
32
+ * This exception is raise, if a unparser error occurs.
33
+ */
34
+ cUnparserError = rb_define_class_under(mJSON, "UnparserError", cJSONError);
35
+
36
+ /**
37
+ * If a circular data structure is encountered while unparsing this exception is raised.
38
+ */
39
+ cCircularDatastructure = rb_define_class_under(mJSON, "CircularDatastructure", cUnparserError);
40
+ }
41
+
42
+ /**
43
+ * Convert _string_ from UTF8 encoding to UTF16 (big endian) encoding and return it.
44
+ */
45
+ static VALUE utf8_to_utf16(self, string)
46
+ VALUE self, string;
47
+ {
48
+ VALUE mUTF8toUTF16 = rb_const_get(mJSON, rb_intern("UTF8toUTF16"));
49
+ VALUE packed_bytes = rb_funcall(mUTF8toUTF16, rb_intern("iconv"), 1, string);
50
+ VALUE unpacked_bytes = rb_funcall(packed_bytes, rb_intern("unpack"), 1, rb_str_new2("H*"));
51
+ return rb_ary_entry(unpacked_bytes, 0);
52
+ }
53
+
54
+ static VALUE utf8_to_json(self, input)
55
+ VALUE self, input;
56
+ {
57
+ VALUE length = rb_funcall(input, rb_intern("length"), 0);
58
+ long c_length = NUM2LONG(length);
59
+
60
+ VALUE result = rb_str_new2("");
61
+
62
+ int c_increment_length;
63
+ size_t i = 0;
64
+ while(i < c_length) {
65
+ c_increment_length = _convert_utf8_char(input, i, result);
66
+ i += c_increment_length;
67
+ }
68
+
69
+ return result;
70
+ }
71
+
72
+ /** private **/
73
+
74
+ static void _begin_setup_iconv() {
75
+ rb_require("iconv");
76
+ VALUE mIconv = rb_const_get(rb_cObject, rb_intern("Iconv"));
77
+
78
+ VALUE mUTF8toUTF16 = rb_funcall(mIconv, rb_intern("new"), 2, rb_str_new2("utf-8"), rb_str_new2("utf-16be"));
79
+ rb_const_set(
80
+ mJSON,
81
+ rb_intern("UTF16toUTF8"),
82
+ mUTF8toUTF16
83
+ );
84
+
85
+ VALUE mUTF16toUTF8 = rb_funcall(mIconv, rb_intern("new"), 2, rb_str_new2("utf-16be"), rb_str_new2("utf-8"));
86
+ rb_const_set(
87
+ mJSON,
88
+ rb_intern("UTF8toUTF16"),
89
+ mUTF16toUTF8
90
+ );
91
+
92
+ rb_funcall(mUTF16toUTF8, rb_intern("iconv"), 1, rb_str_new2("no bom"));
93
+ }
94
+
95
+ static long _convert_utf8_char(input, i, result)
96
+ VALUE input;
97
+ long i;
98
+ VALUE result;
99
+ {
100
+ long c_increment_length = 1;
101
+ if (i < 0 || RSTRING(input)->len <= i) {
102
+ return Qnil;
103
+ }
104
+ char c_current = (RSTRING(input)->ptr[i] & 0xff);
105
+
106
+ if(c_current == 0x08) {
107
+ rb_str_cat2(result, "\\b");
108
+ }
109
+ else if(c_current == 0x09) {
110
+ rb_str_cat2(result, "\\t");
111
+ }
112
+ else if(c_current == 0x0A) {
113
+ rb_str_cat2(result, "\\n");
114
+ }
115
+ else if(c_current == 0x0C) {
116
+ rb_str_cat2(result, "\\f");
117
+ }
118
+ else if(c_current == 0x0D) {
119
+ rb_str_cat2(result, "\\r");
120
+ }
121
+ else if(c_current == '"') {
122
+ rb_str_cat2(result, "\\\"");
123
+ }
124
+ else if(c_current == '\\') {
125
+ rb_str_cat2(result, "\\\\");
126
+ }
127
+ else if(0x00 <= c_current && c_current <= 0x1f) {
128
+ VALUE format_string = rb_str_new2("\\u%04x");
129
+ rb_str_append(result, rb_funcall(format_string, rb_intern("%"), 1, LONG2NUM(c_current)));
130
+ }
131
+ else if(0x20 <= c_current && c_current <= 0x7f) {
132
+ rb_str_concat(result, LONG2NUM(c_current));
133
+ }
134
+ else if(
135
+ !(
136
+ rb_funcall(mJSON, rb_intern("support_unicode?"), 0) == Qtrue &&
137
+ rb_equal(rb_gv_get("KCODE"), rb_str_new2("UTF8"))
138
+ )) {
139
+ VALUE current = rb_funcall(input, rb_intern("[]"), 1, LONG2NUM(i));
140
+ rb_str_concat(result, current);
141
+ }
142
+ // else if((c_current & 0xe0) == 0xc0) {
143
+ // c_increment_length = 2;
144
+ // _convert_utf_8_string_part(result, input, i, c_increment_length);
145
+ // }
146
+ // else if((c_current & 0xf0) == 0xe0) {
147
+ // c_increment_length = 3;
148
+ // _convert_utf_8_string_part(result, input, i, c_increment_length);
149
+ // }
150
+ // else if((c_current & 0xf8) == 0xf0) {
151
+ // c_increment_length = 4;
152
+ // _convert_utf_8_string_part(result, input, i, c_increment_length);
153
+ // }
154
+ // // TODO: See if these next two steps are valid unicode.
155
+ // else if((c_current & 0xf8) == 0xf8) {
156
+ // c_increment_length = 5;
157
+ // _convert_utf_8_string_part(result, input, i, c_increment_length);
158
+ // }
159
+ // else if((c_current & 0xf8) == 0xfc) {
160
+ // c_increment_length = 6;
161
+ // _convert_utf_8_string_part(result, input, i, c_increment_length);
162
+ // }
163
+ else {
164
+ VALUE current = rb_funcall(input, rb_intern("[]"), 1, LONG2NUM(i));
165
+ rb_str_concat(result, current);
166
+ }
167
+
168
+ return c_increment_length;
169
+ }
170
+
171
+ static VALUE _convert_utf_8_string_part(result, input, i, length)
172
+ VALUE result, input;
173
+ long i, length;
174
+ {
175
+ rb_str_cat2(result, "\\u");
176
+ VALUE utf_header = rb_funcall(input, rb_intern("[]"), 2, LONG2NUM(i), LONG2NUM(length));
177
+ rb_str_append(result, utf8_to_utf16(mJSON, utf_header));
178
+
179
+ return Qnil;
180
+ }
@@ -0,0 +1,9 @@
1
+ #include "ruby.h"
2
+ #include <string.h>
3
+
4
+ static VALUE utf8_to_utf16(VALUE, VALUE);
5
+ static VALUE _convert_utf_8_string_part(VALUE, VALUE, long, long);
6
+ static long _convert_utf8_char(VALUE, long, VALUE);
7
+ static VALUE utf8_to_json(VALUE, VALUE);
8
+ static void _begin_setup_iconv();
9
+ void Init_json_ext();
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'mkrf'
3
+
4
+ dir = File.dirname(__FILE__)
5
+ Mkrf::Generator.new('state_ext', "#{dir}/*.c")
@@ -0,0 +1,169 @@
1
+ #include "state_ext.h"
2
+
3
+ static VALUE cState;
4
+ static VALUE mClassMethods;
5
+ static VALUE mJSON;
6
+
7
+ /**
8
+ * This class is used to create State instances, that are used to hold data
9
+ * while unparsing a Ruby data structure into a JSON string.
10
+ */
11
+ void Init_state_ext() {
12
+ mJSON = rb_const_get(rb_cObject, rb_intern("JSON"));
13
+ cState = rb_define_class_under(mJSON, "State", rb_cObject);
14
+ mClassMethods = rb_define_module_under(cState, "ClassMethods");
15
+
16
+ rb_define_method(mClassMethods, "from_state", from_state, 1);
17
+ rb_extend_object(cState, mClassMethods);
18
+
19
+ rb_define_method(cState, "initialize", initialize, -1);
20
+ rb_define_method(cState, "seen?", seen, 1);
21
+ rb_define_method(cState, "remember", remember, 1);
22
+ rb_define_method(cState, "forget", forget, 1);
23
+
24
+ /*
25
+ * This string is used to indent levels in the JSON string.
26
+ */
27
+ rb_define_method(cState, "indent", get_indent, 0);
28
+ rb_define_method(cState, "indent=", set_indent, 1);
29
+
30
+ /*
31
+ * This string is used to include a space between the tokens in a JSON string.
32
+ */
33
+ rb_define_attr(cState, "space", 1, 1);
34
+
35
+ /*
36
+ * This string is put at the end of a line that holds a JSON object (or Hash).
37
+ */
38
+ rb_define_attr(cState, "object_nl", 1, 1);
39
+
40
+ /*
41
+ * This string is put at the end of a line that holds a JSON array.
42
+ */
43
+ rb_define_attr(cState, "array_nl", 1, 1);
44
+ }
45
+
46
+ void rb_json_state_indent(state, json, depth)
47
+ VALUE state, json;
48
+ long depth;
49
+ {
50
+ VALUE indent = rb_funcall(state, rb_intern("indent"), 0);
51
+ char* c_indent = StringValueCStr(indent);
52
+ // Incrementing the depth
53
+ char* full_indent;
54
+ long i;
55
+ for(i=0; i<depth; i++) {
56
+ strcat(full_indent, c_indent);
57
+ }
58
+ rb_str_cat2(json, full_indent);
59
+ }
60
+
61
+ /** Static **/
62
+
63
+ /**
64
+ * Instantiates a new State object, configured by _opts_.
65
+ */
66
+ static VALUE initialize(argc, argv, self)
67
+ int argc;
68
+ VALUE *argv;
69
+ VALUE self;
70
+ {
71
+ VALUE opts;
72
+ rb_scan_args(argc, argv, "01", &opts);
73
+
74
+ VALUE indent, space, object_nl, array_nl;
75
+ if(opts == Qnil) {
76
+ indent = rb_str_new2("");
77
+ space = rb_str_new2("");
78
+ object_nl = rb_str_new2("");
79
+ array_nl = rb_str_new2("");
80
+ }
81
+ else {
82
+ indent = hash_value_or_empty_string_default(opts, "indent");
83
+ space = hash_value_or_empty_string_default(opts, "space");
84
+ object_nl = hash_value_or_empty_string_default(opts, "object_nl");
85
+ array_nl = hash_value_or_empty_string_default(opts, "array_nl");
86
+ }
87
+
88
+ rb_ivar_set(self, rb_intern("@indent"), indent);
89
+ rb_ivar_set(self, rb_intern("@space"), space);
90
+ rb_ivar_set(self, rb_intern("@object_nl"), object_nl);
91
+ rb_ivar_set(self, rb_intern("@array_nl"), array_nl);
92
+ rb_ivar_set(self, rb_intern("@seen"), rb_hash_new());
93
+ }
94
+
95
+ static VALUE get_indent(self)
96
+ VALUE self;
97
+ {
98
+ return rb_ivar_get(self, rb_intern("@indent"));
99
+ }
100
+
101
+ static VALUE set_indent(self, indent)
102
+ VALUE self, indent;
103
+ {
104
+ rb_ivar_set(self, rb_intern("@indent"), indent);
105
+ }
106
+
107
+ /**
108
+ * Creates a State object from _opts_, which ought to be Hash to create a
109
+ * new State instance configured by opts, something else to create an
110
+ * unconfigured instance. If _opts_ is a State object, it is just returned.
111
+ */
112
+ static VALUE from_state(self, opts)
113
+ VALUE self, opts;
114
+ {
115
+ if(rb_obj_is_kind_of(opts, cState)) {
116
+ return opts;
117
+ }
118
+ else if(rb_obj_is_kind_of(opts, rb_cHash)) {
119
+ return rb_funcall(self, rb_intern("new"), 1, opts);
120
+ }
121
+ else {
122
+ return rb_funcall(self, rb_intern("new"), 0);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Returns _true_, if _object_ was already seen during this Unparsing run.
128
+ */
129
+ static VALUE seen(self, object)
130
+ VALUE self, object;
131
+ {
132
+ VALUE seen_ivar = rb_ivar_get(self, rb_intern("@seen"));
133
+ return rb_hash_aref(seen_ivar, rb_obj_id(object));
134
+ }
135
+
136
+ /**
137
+ * Remember _object_, to find out if it was already encountered (to find out
138
+ * if a cyclic data structure is unparsed).
139
+ */
140
+ static VALUE remember(self, object)
141
+ VALUE self, object;
142
+ {
143
+ VALUE seen_ivar = rb_ivar_get(self, rb_intern("@seen"));
144
+ rb_hash_aset(seen_ivar, rb_obj_id(object), Qtrue);
145
+ }
146
+
147
+ /**
148
+ * Forget _object_ for this Unparsing run.
149
+ */
150
+ static VALUE forget(self, object)
151
+ VALUE self, object;
152
+ {
153
+ VALUE seen_ivar = rb_ivar_get(self, rb_intern("@seen"));
154
+ rb_hash_delete(seen_ivar, rb_obj_id(object));
155
+ }
156
+
157
+ /** private **/
158
+ static VALUE hash_value_or_empty_string_default(hash, cKey)
159
+ VALUE hash;
160
+ const char* cKey;
161
+ {
162
+ VALUE hash_value = rb_hash_aref(hash, ID2SYM(rb_intern(cKey)));
163
+ if(hash_value == Qnil) {
164
+ return rb_str_new2("");
165
+ }
166
+ else {
167
+ return hash_value;
168
+ }
169
+ }