usamin 7.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 38c48b49c05c5a95289abb777b32db4b47252fd31c5553acf09b11f3c076b500
4
+ data.tar.gz: 023641ec4fb9fc7277044ae4c6a1dcca134ace8ed4aaddf2b64c80a4fb7ef89e
5
+ SHA512:
6
+ metadata.gz: 493e984049b444963a1be108cc624bc6a426edbf3c951c860f4662f4f3121e309e1768be11a919a16353c36fc96fb90124daf68bb68ec8f5756a82850c4a7618
7
+ data.tar.gz: 74cfe2c5148808a6be7f43a6bddf5ec1306ee3692a7833ea775392b61948fee537fc83befe55d0961d5ff3771e43a91d208294d2a8b4c41c8df20c738976cb5a
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
14
+
15
+ .DS_Store
16
+ /testdata/
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in usamin.gemspec
6
+ gemspec
@@ -0,0 +1,29 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ usamin (7.7.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ coderay (1.1.2)
10
+ method_source (0.9.0)
11
+ pry (0.11.3)
12
+ coderay (~> 1.1.0)
13
+ method_source (~> 0.9.0)
14
+ rake (12.3.0)
15
+ rake-compiler (1.0.4)
16
+ rake
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ bundler (~> 1.16)
23
+ pry (~> 0.11)
24
+ rake (~> 12.3)
25
+ rake-compiler
26
+ usamin!
27
+
28
+ BUNDLED WITH
29
+ 1.16.1
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Ishotihadus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,255 @@
1
+ # Usamin
2
+
3
+ A fast JSON serializer / deserializer for Ruby with [RapidJSON](http://rapidjson.org/).
4
+
5
+ The name of "Usamin" is derived from [Nana Abe](https://www.project-imas.com/wiki/Nana_Abe).
6
+
7
+ ## Installation
8
+
9
+ Install RapidJSON beforehand.
10
+
11
+ Next, add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'usamin'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install usamin
24
+
25
+ The directory of RapidJSON can be explicitly specified with `--with-rapidjson-dir` option.
26
+
27
+ $ gem install usamin -- --with-rapidjson-dir=/usr/local/opt/rapidjson
28
+
29
+ ## Usage
30
+
31
+ ### Loading library
32
+
33
+ ```ruby
34
+ require 'usamin'
35
+ ```
36
+
37
+ ### Parsing
38
+
39
+ ```ruby
40
+ json = '{
41
+ "miku maekawa": {
42
+ "age": 15,
43
+ "height": 152,
44
+ "weight": 45,
45
+ "body_size": [85, 55, 81],
46
+ "birthday": [2, 22],
47
+ "cv": "natsumi takamori"
48
+ },
49
+ "nana abe": {
50
+ "age": 17,
51
+ "height": 146,
52
+ "weight": 40,
53
+ "body_size": [84, 57, 84],
54
+ "birthday": [5, 15],
55
+ "cv": "marie miyake"
56
+ }
57
+ }'
58
+
59
+ Usamin.parse(json)
60
+ ```
61
+
62
+ ### Lazy loading
63
+
64
+ ```ruby
65
+ data = Usamin.load(json)
66
+ ```
67
+
68
+ Here, `data` is not a Hash, but this can be handled like a Hash.
69
+
70
+ ```ruby
71
+ data.keys
72
+ #=> ["miku maekawa", "nana abe"]
73
+
74
+ data.map{|k, v| [k, v['age']]}.to_h
75
+ #=> {"miku maekawa"=>15, "nana abe"=>17}
76
+ ```
77
+
78
+ Array data also can be handled like an Array object.
79
+
80
+ ```ruby
81
+ data['nana abe']['body_size'].size
82
+ #=> 3
83
+
84
+ data['nana abe']['body_size'].inject(:+)
85
+ #=> 225
86
+ ```
87
+
88
+ The `eval` and `eval_r` converts these data structures into the Ruby data structures. `_r` means recursiveness.
89
+
90
+ ```ruby
91
+ data.eval
92
+ #=> {"miku maekawa"=>#<Usamin::Hash>, "nana abe"=>#<Usamin::Hash>}
93
+
94
+ data['miku maekawa'].eval_r
95
+ #=> {"age"=>15, "height"=>152, "weight"=>45, "body_size"=>[85, 55, 81], "birthday"=>[2, 22], "cv"=>"natsumi takamori"}
96
+
97
+ data.eval_r
98
+ #=> same as Usamin.parse(json)
99
+ ```
100
+
101
+ #### Notes about lazy loading data
102
+
103
+ - Frozen. Modification is not allowed.
104
+ - A key list of Hash is based on not hash tables but arrays. An index access to hash costs O(n).
105
+
106
+ ### Generating
107
+
108
+ ```ruby
109
+ data = {
110
+ "miku maekawa" => {
111
+ "age"=>15,
112
+ "height"=>152,
113
+ "weight"=>45,
114
+ "body_size"=>[85, 55, 81],
115
+ "birthday"=>[2, 22],
116
+ "cv"=>"natsumi takamori"},
117
+ "nana abe"=> {
118
+ "age"=>17,
119
+ "height"=>146,
120
+ "weight"=>40,
121
+ "body_size"=>[84, 57, 84],
122
+ "birthday"=>[5, 15],
123
+ "cv"=>"marie miyake"}}
124
+
125
+ Usamin.generate(data)
126
+
127
+ # pretty generation is also supported
128
+ Usamin.pretty_generate(data)
129
+ ```
130
+
131
+ Of course, UsaminValue also can be serialized.
132
+
133
+ ```ruby
134
+ data = Usamin.load(json)
135
+
136
+ Usamin.generate(data['nana abe'])
137
+ ```
138
+
139
+ ### Fast parsing
140
+
141
+ Usamin uses `kParseFullPrecisionFlag` by default, but this option makes parsing a little slow.
142
+
143
+ You can use `:fast` option to avoid this.
144
+
145
+ ```ruby
146
+ # default
147
+ Usamin.parse('77.777700000000795')
148
+ #=> 77.77770000000079
149
+
150
+ # fast but not precise
151
+ Usamin.parse('77.777700000000795', fast: true)
152
+ #=> 77.7777000000008
153
+ ```
154
+
155
+ ### Error handling
156
+
157
+ ```ruby
158
+ str = '{"this is bad example"'
159
+ Usamin.parse(str)
160
+ # Usamin::ParserError: Missing a colon after a name of object member. Offset: 22
161
+ ```
162
+
163
+ ### Documentation
164
+
165
+ See: http://www.rubydoc.info/gems/usamin/
166
+
167
+ ## Benchmarks
168
+
169
+ Based on [nativejson-benchmark](https://github.com/miloyip/nativejson-benchmark).
170
+
171
+ ### Roundtrips
172
+
173
+ Usamin passes all roundtrips, and the results are same as official JSON package.
174
+
175
+ ### Reliability of parsed data
176
+
177
+ Usamin and JSON load the same data from 3 big json data in nativejson-benchmark.
178
+
179
+ ### Performance
180
+
181
+ The values show the elapsed time for operating 20 times.
182
+
183
+ #### Parsing
184
+
185
+ ```
186
+ nativejson-benchmark/data/canada.json
187
+ json 0.785524 0.007927 0.793451 ( 0.805269)
188
+ oj 1.833580 0.043648 1.877228 ( 1.895704)
189
+ usamin 0.595523 0.012299 0.607822 ( 0.612215)
190
+ usamin (fast) 0.286458 0.008710 0.295168 ( 0.297998)
191
+ usamin (load) 0.484056 0.011770 0.495826 ( 0.498110)
192
+ usamin (load / fast) 0.187712 0.020202 0.207914 ( 0.210711)
193
+
194
+ nativejson-benchmark/data/citm_catalog.json
195
+ json 0.469471 0.005208 0.474679 ( 0.478920)
196
+ oj 0.327143 0.002947 0.330090 ( 0.331882)
197
+ usamin 0.362764 0.005633 0.368397 ( 0.370940)
198
+ usamin (fast) 0.359859 0.005884 0.365743 ( 0.371016)
199
+ usamin (load) 0.117140 0.007003 0.124143 ( 0.126548)
200
+ usamin (load / fast) 0.115237 0.010061 0.125298 ( 0.128470)
201
+
202
+ nativejson-benchmark/data/twitter.json
203
+ json 0.238582 0.002561 0.241143 ( 0.243726)
204
+ oj 0.162212 0.001031 0.163243 ( 0.165047)
205
+ usamin 0.194476 0.001523 0.195999 ( 0.197920)
206
+ usamin (fast) 0.192985 0.001339 0.194324 ( 0.197404)
207
+ usamin (load) 0.070360 0.005012 0.075372 ( 0.078090)
208
+ usamin (load / fast) 0.067618 0.006416 0.074034 ( 0.076244)
209
+ ```
210
+
211
+ #### Generating
212
+
213
+ ```
214
+ nativejson-benchmark/data/canada.json
215
+ json 1.988155 0.029728 2.017883 ( 2.023475)
216
+ oj 2.092999 0.033136 2.126135 ( 2.135765)
217
+ usamin 0.296385 0.031412 0.327797 ( 0.330314)
218
+
219
+ nativejson-benchmark/data/citm_catalog.json
220
+ json 0.243795 0.007463 0.251258 ( 0.256621)
221
+ oj 0.076474 0.009617 0.086091 ( 0.087966)
222
+ usamin 0.059434 0.009616 0.069050 ( 0.071158)
223
+
224
+ nativejson-benchmark/data/twitter.json
225
+ json 0.159030 0.006730 0.165760 ( 0.170796)
226
+ oj 0.052856 0.009164 0.062020 ( 0.064344)
227
+ usamin 0.047707 0.010265 0.057972 ( 0.061729)
228
+ ```
229
+
230
+ #### Pretty Generating
231
+
232
+ ```
233
+ nativejson-benchmark/data/canada.json
234
+ json 2.188493 0.066003 2.254496 ( 2.260332)
235
+ oj 1.583613 0.029798 1.613411 ( 1.619652)
236
+ usamin 0.399066 0.081314 0.480380 ( 0.482841)
237
+
238
+ nativejson-benchmark/data/citm_catalog.json
239
+ json 0.300601 0.029596 0.330197 ( 0.335351)
240
+ oj 0.065151 0.009553 0.074704 ( 0.077010)
241
+ usamin 0.089872 0.022203 0.112075 ( 0.116063)
242
+
243
+ nativejson-benchmark/data/twitter.json
244
+ json 0.189439 0.010086 0.199525 ( 0.204491)
245
+ oj 0.040982 0.008203 0.049185 ( 0.049352)
246
+ usamin 0.044296 0.010095 0.054391 ( 0.056245)
247
+ ```
248
+
249
+ ## Contributing
250
+
251
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Ishotihadus/usamin.
252
+
253
+ ## License
254
+
255
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT) at the request of RapidJSON.
@@ -0,0 +1,18 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ require "rake/extensiontask"
11
+
12
+ task :build => :compile
13
+
14
+ Rake::ExtensionTask.new("usamin") do |ext|
15
+ ext.lib_dir = "lib/usamin"
16
+ end
17
+
18
+ task :default => [:clobber, :compile, :test]
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "usamin"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "pry"
14
+ Pry.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ have_library('stdc++')
4
+ dir_config('rapidjson')
5
+ $CPPFLAGS << ' -O3 -Wall -Wextra -Wvla -std=c++14'
6
+ create_makefile('usamin/usamin')
@@ -0,0 +1,987 @@
1
+ #include <string.h>
2
+ #include <stdint.h>
3
+ #include <ruby.h>
4
+ #include <ruby/encoding.h>
5
+ #define RAPIDJSON_PARSE_DEFAULT_FLAGS \
6
+ rapidjson::kParseFullPrecisionFlag | rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag | rapidjson::kParseNanAndInfFlag
7
+ #define kParseFastFlags (rapidjson::kParseDefaultFlags & ~rapidjson::kParseFullPrecisionFlag)
8
+ #define RAPIDJSON_WRITE_DEFAULT_FLAGS rapidjson::kWriteNanAndInfFlag
9
+ #include <rapidjson/document.h>
10
+ #include <rapidjson/writer.h>
11
+ #include <rapidjson/prettywriter.h>
12
+ #include <rapidjson/error/en.h>
13
+
14
+ #if SIZEOF_VALUE < SIZEOF_VOIDP
15
+ #error SIZEOF_VOIDP must not be greater than SIZEOF_VALUE.
16
+ #endif
17
+
18
+
19
+ rb_encoding *utf8;
20
+ int utf8index;
21
+ ID id_to_s;
22
+ VALUE rb_cUsaminValue, rb_cUsaminHash, rb_cUsaminArray, rb_eUsaminError, rb_eParserError;
23
+ VALUE utf8value, sym_fast, sym_indent, sym_single_line_array;
24
+
25
+ class UsaminValue {
26
+ public:
27
+ rapidjson::Value *value;
28
+ bool free_flag;
29
+
30
+ UsaminValue(rapidjson::Value *value = nullptr, bool free_flag = false);
31
+ ~UsaminValue();
32
+ };
33
+
34
+ static inline VALUE get_utf8_str(VALUE str) {
35
+ Check_Type(str, T_STRING);
36
+ int encoding = rb_enc_get_index(str);
37
+ if (encoding == utf8index || rb_enc_compatible(str, utf8value) == utf8)
38
+ return str;
39
+ else
40
+ return rb_str_conv_enc(str, rb_enc_from_index(encoding), utf8);
41
+ }
42
+
43
+ static inline VALUE new_utf8_str(const char* cstr, const long len) {
44
+ VALUE ret = rb_str_new(cstr, len);
45
+ rb_enc_set_index(ret, utf8index);
46
+ return ret;
47
+ }
48
+
49
+ static inline bool str_compare(const char* str1, const long len1, const char* str2, const long len2) {
50
+ if (len1 != len2 || len1 < 0)
51
+ return false;
52
+ return memcmp(str1, str2, len1) == 0;
53
+ }
54
+
55
+ static inline void check_value(UsaminValue *ptr) {
56
+ if (!ptr || !ptr->value)
57
+ rb_raise(rb_eUsaminError, "Null Reference.");
58
+ }
59
+
60
+ static inline void check_object(UsaminValue *ptr) {
61
+ check_value(ptr);
62
+ if (!ptr->value->IsObject())
63
+ rb_raise(rb_eUsaminError, "Value is not an object.");
64
+ }
65
+
66
+ static inline void check_array(UsaminValue *ptr) {
67
+ check_value(ptr);
68
+ if (!ptr->value->IsArray())
69
+ rb_raise(rb_eUsaminError, "Value is not an array.");
70
+ }
71
+
72
+
73
+ static VALUE usamin_free(UsaminValue **ptr) {
74
+ if (*ptr)
75
+ delete *ptr;
76
+ ruby_xfree(ptr);
77
+ return Qnil;
78
+ }
79
+
80
+ static VALUE usamin_alloc(const VALUE klass) {
81
+ UsaminValue** ptr = (UsaminValue**)ruby_xmalloc(sizeof(UsaminValue*));
82
+ *ptr = nullptr;
83
+ return Data_Wrap_Struct(klass, NULL, usamin_free, ptr);
84
+ }
85
+
86
+
87
+ static inline UsaminValue* get_value(VALUE value) {
88
+ UsaminValue** ptr;
89
+ Data_Get_Struct(value, UsaminValue*, ptr);
90
+ return *ptr;
91
+ }
92
+
93
+ static inline void set_value(VALUE value, UsaminValue *usamin) {
94
+ UsaminValue **ptr;
95
+ Data_Get_Struct(value, UsaminValue*, ptr);
96
+ *ptr = usamin;
97
+ }
98
+
99
+ static inline VALUE make_hash(UsaminValue *value) {
100
+ VALUE ret = rb_obj_alloc(rb_cUsaminHash);
101
+ set_value(ret, value);
102
+ return ret;
103
+ }
104
+
105
+ static inline VALUE make_array(UsaminValue *value) {
106
+ VALUE ret = rb_obj_alloc(rb_cUsaminArray);
107
+ set_value(ret, value);
108
+ return ret;
109
+ }
110
+
111
+ static inline rapidjson::ParseResult parse(rapidjson::Document &doc, const VALUE str, bool fast = false) {
112
+ VALUE v = get_utf8_str(str);
113
+ return fast ? doc.Parse<kParseFastFlags>(RSTRING_PTR(v), RSTRING_LEN(v)) : doc.Parse(RSTRING_PTR(v), RSTRING_LEN(v));
114
+ }
115
+
116
+
117
+ static inline VALUE eval_num(rapidjson::Value &value);
118
+ static inline VALUE eval_str(rapidjson::Value &value);
119
+ static inline VALUE eval_object(rapidjson::Value &value);
120
+ static inline VALUE eval_object_r(rapidjson::Value &value);
121
+ static inline VALUE eval_array(rapidjson::Value &value);
122
+ static inline VALUE eval_array_r(rapidjson::Value &value);
123
+
124
+ static VALUE eval(rapidjson::Value &value) {
125
+ switch (value.GetType()) {
126
+ case rapidjson::kObjectType:
127
+ return make_hash(new UsaminValue(&value, false));
128
+ case rapidjson::kArrayType:
129
+ return make_array(new UsaminValue(&value, false));
130
+ case rapidjson::kNullType:
131
+ return Qnil;
132
+ case rapidjson::kFalseType:
133
+ return Qfalse;
134
+ case rapidjson::kTrueType:
135
+ return Qtrue;
136
+ case rapidjson::kNumberType:
137
+ return eval_num(value);
138
+ case rapidjson::kStringType:
139
+ return eval_str(value);
140
+ default:
141
+ rb_raise(rb_eUsaminError, "Unknown Value Type: %d", value.GetType());
142
+ return Qnil;
143
+ }
144
+ }
145
+
146
+ static VALUE eval_r(rapidjson::Value &value) {
147
+ switch (value.GetType()) {
148
+ case rapidjson::kObjectType:
149
+ return eval_object_r(value);
150
+ case rapidjson::kArrayType:
151
+ return eval_array_r(value);
152
+ case rapidjson::kNullType:
153
+ return Qnil;
154
+ case rapidjson::kFalseType:
155
+ return Qfalse;
156
+ case rapidjson::kTrueType:
157
+ return Qtrue;
158
+ case rapidjson::kNumberType:
159
+ return eval_num(value);
160
+ case rapidjson::kStringType:
161
+ return eval_str(value);
162
+ default:
163
+ rb_raise(rb_eUsaminError, "Unknown Value Type: %d", value.GetType());
164
+ return Qnil;
165
+ }
166
+ }
167
+
168
+ static inline VALUE eval_num(rapidjson::Value &value) {
169
+ if (value.IsInt())
170
+ return INT2FIX(value.GetInt());
171
+ else if (value.IsUint())
172
+ return UINT2NUM(value.GetUint());
173
+ else if (value.IsInt64())
174
+ return LL2NUM(value.GetInt64());
175
+ else if (value.IsUint64())
176
+ return ULL2NUM(value.GetUint64());
177
+ else
178
+ return DBL2NUM(value.GetDouble());
179
+ }
180
+
181
+ static inline VALUE eval_str(rapidjson::Value &value) {
182
+ return new_utf8_str(value.GetString(), value.GetStringLength());
183
+ }
184
+
185
+ static inline VALUE eval_object(rapidjson::Value &value) {
186
+ VALUE ret = rb_hash_new();
187
+ for (auto &m : value.GetObject())
188
+ rb_hash_aset(ret, eval_str(m.name), eval(m.value));
189
+ return ret;
190
+ }
191
+
192
+ static inline VALUE eval_object_r(rapidjson::Value &value) {
193
+ VALUE ret = rb_hash_new();
194
+ for (auto &m : value.GetObject())
195
+ rb_hash_aset(ret, eval_str(m.name), eval_r(m.value));
196
+ return ret;
197
+ }
198
+
199
+ static inline VALUE eval_array(rapidjson::Value &value) {
200
+ VALUE ret = rb_ary_new2(value.Size());
201
+ for (auto &v : value.GetArray())
202
+ rb_ary_push(ret, eval(v));
203
+ return ret;
204
+ }
205
+
206
+ static inline VALUE eval_array_r(rapidjson::Value &value) {
207
+ VALUE ret = rb_ary_new2(value.Size());
208
+ for (auto &v : value.GetArray())
209
+ rb_ary_push(ret, eval_r(v));
210
+ return ret;
211
+ }
212
+
213
+
214
+ UsaminValue::UsaminValue(rapidjson::Value *value, bool free_flag) {
215
+ this->value = value;
216
+ this->free_flag = free_flag;
217
+ }
218
+
219
+ UsaminValue::~UsaminValue() {
220
+ if (value && free_flag)
221
+ delete value;
222
+ }
223
+
224
+ /*
225
+ * Parse the JSON string into UsaminValue.
226
+ * If the document root is not an object or an array, the same value as {#parse} will be returned.
227
+ *
228
+ * @overload load(source, opts = {})
229
+ * @param [String] source JSON string to parse
230
+ * @param [::Hash] opts options
231
+ * @option opts :fast fast mode (but not precise)
232
+ * @return [Object]
233
+ */
234
+ static VALUE w_load(const int argc, VALUE *argv, const VALUE self) {
235
+ VALUE source, options;
236
+ rb_scan_args(argc, argv, "1:", &source, &options);
237
+ rapidjson::Document *doc = new rapidjson::Document;
238
+ auto result = parse(*doc, source, argc > 1 && RTEST(rb_hash_lookup(options, sym_fast)));
239
+ if (!result) {
240
+ delete doc;
241
+ rb_raise(rb_eParserError, "%s Offset: %lu", GetParseError_En(result.Code()), result.Offset());
242
+ }
243
+
244
+ VALUE ret;
245
+ switch (doc->GetType()) {
246
+ case rapidjson::kObjectType:
247
+ return make_hash(new UsaminValue(doc, true));
248
+ case rapidjson::kArrayType:
249
+ return make_array(new UsaminValue(doc, true));
250
+ case rapidjson::kNullType:
251
+ delete doc;
252
+ return Qnil;
253
+ case rapidjson::kFalseType:
254
+ delete doc;
255
+ return Qfalse;
256
+ case rapidjson::kTrueType:
257
+ delete doc;
258
+ return Qtrue;
259
+ case rapidjson::kNumberType:
260
+ ret = eval_num(*doc);
261
+ delete doc;
262
+ return ret;
263
+ case rapidjson::kStringType:
264
+ ret = eval_str(*doc);
265
+ delete doc;
266
+ return ret;
267
+ default:
268
+ rb_raise(rb_eUsaminError, "Unknown Value Type: %d", doc->GetType());
269
+ return Qnil;
270
+ }
271
+ }
272
+
273
+ /*
274
+ * Parse the JSON string into Ruby data structures.
275
+ *
276
+ * @overload parse(source, opts = {})
277
+ * @param [String] source a JSON string to parse
278
+ * @param [::Hash] opts options
279
+ * @option opts :fast fast mode (but not precise)
280
+ * @return [Object]
281
+ */
282
+ static VALUE w_parse(const int argc, VALUE *argv, const VALUE self) {
283
+ VALUE source, options;
284
+ rb_scan_args(argc, argv, "1:", &source, &options);
285
+ rapidjson::Document doc;
286
+ auto result = parse(doc, source, argc > 1 && RTEST(rb_hash_lookup(options, sym_fast)));
287
+ if (!result)
288
+ rb_raise(rb_eParserError, "%s Offset: %lu", GetParseError_En(result.Code()), result.Offset());
289
+ return eval_r(doc);
290
+ }
291
+
292
+ /*
293
+ * Returns whether the value is array.
294
+ */
295
+ static VALUE w_value_isarray(const VALUE self) {
296
+ UsaminValue *value = get_value(self);
297
+ check_value(value);
298
+ return value->value->IsArray() ? Qtrue : Qfalse;
299
+ }
300
+
301
+ /*
302
+ * Returns whether the value is object.
303
+ */
304
+ static VALUE w_value_isobject(const VALUE self) {
305
+ UsaminValue *value = get_value(self);
306
+ check_value(value);
307
+ return value->value->IsObject() ? Qtrue : Qfalse;
308
+ }
309
+
310
+ /*
311
+ * Convert to Ruby data structures.
312
+ *
313
+ * @return [Object]
314
+ */
315
+ static VALUE w_value_eval(const VALUE self) {
316
+ UsaminValue *value = get_value(self);
317
+ check_value(value);
318
+ if (value->value->IsObject())
319
+ return eval_object(*(value->value));
320
+ else if (value->value->IsArray())
321
+ return eval_array(*(value->value));
322
+ return Qnil;
323
+ }
324
+
325
+ /*
326
+ * Convert to Ruby data structures recursively.
327
+ *
328
+ * @return [Object]
329
+ */
330
+ static VALUE w_value_eval_r(const VALUE self) {
331
+ UsaminValue *value = get_value(self);
332
+ check_value(value);
333
+ return eval_r(*(value->value));
334
+ }
335
+
336
+ /*
337
+ * Always true.
338
+ */
339
+ static VALUE w_value_isfrozen(const VALUE self) {
340
+ return Qtrue;
341
+ }
342
+
343
+ template <class Writer> static void write_value(Writer&, rapidjson::Value&);
344
+
345
+ /*
346
+ * Dumps data in JSON.
347
+ *
348
+ * @return [String]
349
+ */
350
+ static VALUE w_value_marshal_dump(const VALUE self) {
351
+ UsaminValue *value = get_value(self);
352
+ check_value(value);
353
+ rapidjson::StringBuffer buf;
354
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
355
+ write_value(writer, *(value->value));
356
+ return rb_str_new(buf.GetString(), buf.GetSize());
357
+ }
358
+
359
+ /*
360
+ * Loads marshal data.
361
+ *
362
+ * @return [self]
363
+ */
364
+ static VALUE w_value_marshal_load(const VALUE self, VALUE source) {
365
+ Check_Type(source, T_STRING);
366
+ rapidjson::Document *doc = new rapidjson::Document();
367
+ rapidjson::ParseResult result = doc->Parse<rapidjson::kParseFullPrecisionFlag | rapidjson::kParseNanAndInfFlag>(RSTRING_PTR(source), RSTRING_LEN(source));
368
+ if (!result) {
369
+ delete doc;
370
+ rb_raise(rb_eParserError, "%s Offset: %lu", GetParseError_En(result.Code()), result.Offset());
371
+ }
372
+ if (doc->IsObject() || doc->IsArray()) {
373
+ set_value(self, new UsaminValue(doc, true));
374
+ return self;
375
+ }
376
+ auto type = doc->GetType();
377
+ delete doc;
378
+ rb_raise(rb_eUsaminError, "Invalid Value Type for marshal_load: %d", type);
379
+ return Qnil;
380
+ }
381
+
382
+
383
+ /*
384
+ * @overload [](name)
385
+ * @return [Object | nil]
386
+ *
387
+ * @note This method has linear time complexity.
388
+ */
389
+ static VALUE w_hash_operator_indexer(const VALUE self, VALUE key) {
390
+ UsaminValue *value = get_value(self);
391
+ check_object(value);
392
+ VALUE kvalue = get_utf8_str(key);
393
+ for (auto &m : value->value->GetObject())
394
+ if (str_compare(RSTRING_PTR(kvalue), RSTRING_LEN(kvalue), m.name.GetString(), m.name.GetStringLength()))
395
+ return eval(m.value);
396
+ return Qnil;
397
+ }
398
+
399
+ static VALUE hash_enum_size(const VALUE self, VALUE args, VALUE eobj) {
400
+ UsaminValue *value = get_value(self);
401
+ check_object(value);
402
+ return UINT2NUM(value->value->MemberCount());
403
+ }
404
+
405
+ /*
406
+ * @yield [key, value]
407
+ * @yieldparam key [String]
408
+ * @return [Enumerator | self]
409
+ */
410
+ static VALUE w_hash_each(const VALUE self) {
411
+ UsaminValue *value = get_value(self);
412
+ check_object(value);
413
+ RETURN_SIZED_ENUMERATOR(self, 0, nullptr, hash_enum_size);
414
+ if (rb_proc_arity(rb_block_proc()) > 1) {
415
+ for (auto &m : value->value->GetObject()) {
416
+ VALUE args[] = { eval_str(m.name), eval(m.value) };
417
+ rb_yield_values2(2, args);
418
+ }
419
+ } else {
420
+ for (auto &m : value->value->GetObject())
421
+ rb_yield(rb_assoc_new(eval_str(m.name), eval(m.value)));
422
+ }
423
+ return self;
424
+ }
425
+
426
+ /*
427
+ * @yield [key]
428
+ * @yieldparam key [String]
429
+ * @return [Enumerator | self]
430
+ */
431
+ static VALUE w_hash_each_key(const VALUE self) {
432
+ UsaminValue *value = get_value(self);
433
+ check_object(value);
434
+ RETURN_SIZED_ENUMERATOR(self, 0, nullptr, hash_enum_size);
435
+ for (auto &m : value->value->GetObject())
436
+ rb_yield(eval_str(m.name));
437
+ return self;
438
+ }
439
+
440
+ /*
441
+ * @yield [value]
442
+ * @return [Enumerator | self]
443
+ */
444
+ static VALUE w_hash_each_value(const VALUE self) {
445
+ UsaminValue *value = get_value(self);
446
+ check_object(value);
447
+ RETURN_SIZED_ENUMERATOR(self, 0, nullptr, hash_enum_size);
448
+ for (auto &m : value->value->GetObject())
449
+ rb_yield(eval(m.value));
450
+ return self;
451
+ }
452
+
453
+ static VALUE w_hash_isempty(const VALUE self) {
454
+ UsaminValue *value = get_value(self);
455
+ check_object(value);
456
+ return value->value->MemberCount() == 0 ? Qtrue : Qfalse;
457
+ }
458
+
459
+ /*
460
+ * @note This method has linear time complexity.
461
+ */
462
+ static VALUE w_hash_haskey(const VALUE self, VALUE name) {
463
+ UsaminValue *value = get_value(self);
464
+ check_object(value);
465
+ VALUE key = get_utf8_str(name);
466
+ for (auto &m : value->value->GetObject())
467
+ if (str_compare(RSTRING_PTR(key), RSTRING_LEN(key), m.name.GetString(), m.name.GetStringLength()))
468
+ return Qtrue;
469
+ return Qfalse;
470
+ }
471
+
472
+ /*
473
+ * @return [::Array<String>]
474
+ */
475
+ static VALUE w_hash_keys(const VALUE self) {
476
+ UsaminValue *value = get_value(self);
477
+ check_object(value);
478
+ VALUE ret = rb_ary_new2(value->value->MemberCount());
479
+ for (auto &m : value->value->GetObject())
480
+ rb_ary_push(ret, eval_str(m.name));
481
+ return ret;
482
+ }
483
+
484
+ /*
485
+ * @return [Integer]
486
+ */
487
+ static VALUE w_hash_length(const VALUE self) {
488
+ UsaminValue *value = get_value(self);
489
+ check_object(value);
490
+ return UINT2NUM(value->value->MemberCount());
491
+ }
492
+
493
+ /*
494
+ * Convert to Ruby Hash. Same as {Value#eval}.
495
+ *
496
+ * @return [::Hash]
497
+ */
498
+ static VALUE w_hash_eval(const VALUE self) {
499
+ UsaminValue *value = get_value(self);
500
+ check_object(value);
501
+ return eval_object(*(value->value));
502
+ }
503
+
504
+ /*
505
+ * @return [::Array<Object>]
506
+ */
507
+ static VALUE w_hash_values(const VALUE self) {
508
+ UsaminValue *value = get_value(self);
509
+ check_object(value);
510
+ VALUE ret = rb_ary_new2(value->value->MemberCount());
511
+ for (auto &m : value->value->GetObject())
512
+ rb_ary_push(ret, eval(m.value));
513
+ return ret;
514
+ }
515
+
516
+
517
+ /*
518
+ * @overload [](nth)
519
+ * @param [Integer] nth
520
+ * @return [Object | nil]
521
+ *
522
+ * @overload [](start, length)
523
+ * @param [Integer] start
524
+ * @param [Integer] length
525
+ * @return [::Array<Object> | nil]
526
+ *
527
+ * @overload [](range)
528
+ * @param [Range] range
529
+ * @return [::Array<Object> | nil]
530
+ */
531
+ static VALUE w_array_operator_indexer(const int argc, VALUE* argv, const VALUE self) {
532
+ rb_check_arity(argc, 1, 2);
533
+ UsaminValue *value = get_value(self);
534
+ check_array(value);
535
+ if (argc == 2) {
536
+ long beg = NUM2LONG(argv[0]);
537
+ long len = NUM2LONG(argv[1]);
538
+ if (beg < 0)
539
+ beg = beg + value->value->Size();
540
+ if (beg >= 0 && len >= 0) {
541
+ long end = beg + len;
542
+ if (end > value->value->Size())
543
+ end = value->value->Size();
544
+ VALUE ret = rb_ary_new2(end - beg);
545
+ for (long i = beg; i < end; i++)
546
+ rb_ary_push(ret, eval((*(value->value))[static_cast<unsigned int>(i)]));
547
+ return ret;
548
+ }
549
+ } else if (rb_obj_is_kind_of(argv[0], rb_cRange)) {
550
+ long beg, len;
551
+ if (rb_range_beg_len(argv[0], &beg, &len, value->value->Size(), 0) == Qtrue) {
552
+ VALUE ret = rb_ary_new2(len);
553
+ for (long i = beg; i < beg + len; i++)
554
+ rb_ary_push(ret, eval((*(value->value))[static_cast<unsigned int>(i)]));
555
+ return ret;
556
+ }
557
+ } else {
558
+ long l = NUM2LONG(argv[0]);
559
+ if (l < 0)
560
+ l = l + value->value->Size();
561
+ if (l >= 0) {
562
+ unsigned int i = static_cast<unsigned int>(l);
563
+ if (i < value->value->Size())
564
+ return eval((*(value->value))[i]);
565
+ }
566
+ }
567
+ return Qnil;
568
+ }
569
+
570
+ static VALUE array_enum_size(const VALUE self, VALUE args, VALUE eobj) {
571
+ UsaminValue *value = get_value(self);
572
+ check_array(value);
573
+ return UINT2NUM(value->value->Size());
574
+ }
575
+
576
+ /*
577
+ * @yield [value]
578
+ * @return [Enumerator | self]
579
+ */
580
+ static VALUE w_array_each(const VALUE self) {
581
+ UsaminValue *value = get_value(self);
582
+ check_array(value);
583
+ RETURN_SIZED_ENUMERATOR(self, 0, nullptr, array_enum_size);
584
+ for (auto &v : value->value->GetArray())
585
+ rb_yield(eval(v));
586
+ return self;
587
+ }
588
+
589
+ /*
590
+ * @yield [index]
591
+ * @yieldparam index [Integer]
592
+ * @return [Enumerator | self]
593
+ */
594
+ static VALUE w_array_each_index(const VALUE self) {
595
+ UsaminValue *value = get_value(self);
596
+ check_array(value);
597
+ RETURN_SIZED_ENUMERATOR(self, 0, nullptr, array_enum_size);
598
+ for (rapidjson::SizeType i = 0; i < value->value->Size(); i++)
599
+ rb_yield(UINT2NUM(i));
600
+ return self;
601
+ }
602
+
603
+ static VALUE w_array_isempty(const VALUE self) {
604
+ UsaminValue *value = get_value(self);
605
+ check_array(value);
606
+ return value->value->Size() == 0 ? Qtrue : Qfalse;
607
+ }
608
+
609
+ /*
610
+ * @yield [value]
611
+ * @return [Integer]
612
+ */
613
+ static VALUE w_array_index(int argc, VALUE* argv, const VALUE self) {
614
+ rb_check_arity(argc, 0, 1);
615
+ UsaminValue *value = get_value(self);
616
+ check_array(value);
617
+
618
+ if (argc == 1) {
619
+ for (rapidjson::SizeType i = 0; i < value->value->Size(); i++) {
620
+ if (rb_equal(argv[0], eval((*(value->value))[i])) == Qtrue)
621
+ return UINT2NUM(i);
622
+ }
623
+ return Qnil;
624
+ }
625
+
626
+ RETURN_SIZED_ENUMERATOR(self, 0, nullptr, array_enum_size);
627
+ for (rapidjson::SizeType i = 0; i < value->value->Size(); i++) {
628
+ if (RTEST(rb_yield(eval((*(value->value))[i]))))
629
+ return UINT2NUM(i);
630
+ }
631
+ return Qnil;
632
+ }
633
+
634
+ /*
635
+ * @overload first
636
+ * @return [Object | nil]
637
+ *
638
+ * @overload first(n)
639
+ * @return [::Array<Object>]
640
+ */
641
+ static VALUE w_array_first(const int argc, VALUE* argv, const VALUE self) {
642
+ rb_check_arity(argc, 0, 1);
643
+ UsaminValue *value = get_value(self);
644
+ check_array(value);
645
+
646
+ if (argc == 0) {
647
+ if (value->value->Size() == 0)
648
+ return Qnil;
649
+ return eval((*(value->value))[0]);
650
+ }
651
+
652
+ long l = NUM2LONG(argv[0]);
653
+ if (l < 0)
654
+ l = 0;
655
+ else if (l > value->value->Size())
656
+ l = value->value->Size();
657
+ unsigned int li = static_cast<unsigned int>(l);
658
+ VALUE ret = rb_ary_new2(li);
659
+ for (unsigned int i = 0; i < li; i++)
660
+ rb_ary_push(ret, eval((*(value->value))[i]));
661
+
662
+ return ret;
663
+ }
664
+
665
+ /*
666
+ * @return [Integer]
667
+ */
668
+ static VALUE w_array_length(const VALUE self) {
669
+ UsaminValue *value = get_value(self);
670
+ check_array(value);
671
+ return UINT2NUM(value->value->Size());
672
+ }
673
+
674
+ /*
675
+ * Convert to Ruby data structures. Same as {Value#eval}.
676
+ *
677
+ * @return [::Array<Object>]
678
+ */
679
+ static VALUE w_array_eval(const VALUE self) {
680
+ UsaminValue *value = get_value(self);
681
+ check_array(value);
682
+ return eval_array(*(value->value));
683
+ }
684
+
685
+
686
+ template <class Writer> static void write(Writer&, const VALUE);
687
+
688
+ template <class Writer> static inline void write_str(Writer &writer, const VALUE value) {
689
+ VALUE v = get_utf8_str(value);
690
+ writer.String(RSTRING_PTR(v), static_cast<unsigned int>(RSTRING_LEN(v)));
691
+ }
692
+
693
+ template <class Writer> static inline void write_to_s(Writer &writer, const VALUE value) {
694
+ write_str(writer, rb_funcall(value, id_to_s, 0));
695
+ }
696
+
697
+ template <class Writer> static inline void write_key_str(Writer &writer, const VALUE value) {
698
+ VALUE v = get_utf8_str(value);
699
+ writer.Key(RSTRING_PTR(v), static_cast<unsigned int>(RSTRING_LEN(v)));
700
+ }
701
+
702
+ template <class Writer> static inline void write_key_to_s(Writer &writer, const VALUE value) {
703
+ write_key_str(writer, rb_funcall(value, id_to_s, 0));
704
+ }
705
+
706
+ template <class Writer> static inline void write_bignum(Writer &writer, const VALUE value) {
707
+ VALUE v = rb_big2str(value, 10);
708
+ writer.RawValue(RSTRING_PTR(v), static_cast<unsigned int>(RSTRING_LEN(v)), rapidjson::kNumberType);
709
+ }
710
+
711
+ template <class Writer> static inline int write_hash_each(VALUE key, VALUE value, Writer *writer) {
712
+ if (RB_TYPE_P(key, T_STRING))
713
+ write_key_str(*writer, key);
714
+ else if (RB_TYPE_P(key, T_SYMBOL))
715
+ write_key_str(*writer, rb_sym_to_s(key));
716
+ else
717
+ write_key_to_s(*writer, key);
718
+ write(*writer, value);
719
+ return ST_CONTINUE;
720
+ }
721
+
722
+ template <class Writer> static inline void write_hash(Writer &writer, const VALUE hash) {
723
+ writer.StartObject();
724
+ rb_hash_foreach(hash, (int (*)(ANYARGS))write_hash_each<Writer>, reinterpret_cast<VALUE>((&writer)));
725
+ writer.EndObject();
726
+ }
727
+
728
+ template <class Writer> static inline void write_array(Writer &writer, const VALUE value) {
729
+ writer.StartArray();
730
+ const VALUE *ptr = rb_array_const_ptr(value);
731
+ for (long i = 0; i < rb_array_len(value); i++, ptr++)
732
+ write(writer, *ptr);
733
+ writer.EndArray();
734
+ }
735
+
736
+ template <class Writer> static inline void write_struct(Writer &writer, const VALUE value) {
737
+ writer.StartObject();
738
+ VALUE members = rb_struct_members(value);
739
+ const VALUE *ptr = rb_array_const_ptr(members);
740
+ for (long i = 0; i < rb_array_len(members); i++, ptr++) {
741
+ if (RB_TYPE_P(*ptr, T_SYMBOL))
742
+ write_key_str(writer, rb_sym_to_s(*ptr));
743
+ else if (RB_TYPE_P(*ptr, T_STRING))
744
+ write_key_str(writer, *ptr);
745
+ else
746
+ write_key_to_s(writer, *ptr);
747
+ write(writer, rb_struct_aref(value, *ptr));
748
+ }
749
+ writer.EndObject();
750
+ }
751
+
752
+ template <class Writer> static void write_value(Writer &writer, rapidjson::Value &value) {
753
+ switch (value.GetType()) {
754
+ case rapidjson::kObjectType:
755
+ writer.StartObject();
756
+ for (auto &m : value.GetObject()) {
757
+ writer.Key(m.name.GetString(), m.name.GetStringLength());
758
+ write_value(writer, m.value);
759
+ }
760
+ writer.EndObject();
761
+ break;
762
+ case rapidjson::kArrayType:
763
+ writer.StartArray();
764
+ for (auto &v : value.GetArray())
765
+ write_value(writer, v);
766
+ writer.EndArray();
767
+ break;
768
+ case rapidjson::kNullType:
769
+ writer.Null();
770
+ break;
771
+ case rapidjson::kFalseType:
772
+ writer.Bool(false);
773
+ break;
774
+ case rapidjson::kTrueType:
775
+ writer.Bool(true);
776
+ break;
777
+ case rapidjson::kNumberType:
778
+ if (value.IsInt())
779
+ writer.Int(value.GetInt());
780
+ else if (value.IsUint())
781
+ writer.Uint(value.GetUint());
782
+ else if (value.IsInt64())
783
+ writer.Int64(value.GetInt64());
784
+ else if (value.IsUint64())
785
+ writer.Uint64(value.GetUint64());
786
+ else
787
+ writer.Double(value.GetDouble());
788
+ break;
789
+ case rapidjson::kStringType:
790
+ writer.String(value.GetString(), value.GetStringLength());
791
+ break;
792
+ default:
793
+ rb_raise(rb_eUsaminError, "Unknown Value Type: %d", value.GetType());
794
+ }
795
+ }
796
+
797
+ template <class Writer> static inline void write_usamin(Writer &writer, const VALUE self) {
798
+ UsaminValue *value = get_value(self);
799
+ check_value(value);
800
+ write_value(writer, *(value->value));
801
+ }
802
+
803
+ template <class Writer> static void write(Writer &writer, const VALUE value) {
804
+ if (value == Qnil) {
805
+ writer.Null();
806
+ } else if (value == Qfalse) {
807
+ writer.Bool(false);
808
+ } else if (value == Qtrue) {
809
+ writer.Bool(true);
810
+ } else if (RB_FIXNUM_P(value)) {
811
+ writer.Int64(FIX2LONG(value));
812
+ } else if (RB_FLOAT_TYPE_P(value)) {
813
+ writer.Double(NUM2DBL(value));
814
+ } else if (RB_STATIC_SYM_P(value)) {
815
+ write_str(writer, rb_sym_to_s(value));
816
+ } else {
817
+ switch (RB_BUILTIN_TYPE(value)) {
818
+ case T_STRING:
819
+ write_str(writer, value);
820
+ break;
821
+ case T_HASH:
822
+ write_hash(writer, value);
823
+ break;
824
+ case T_ARRAY:
825
+ write_array(writer, value);
826
+ break;
827
+ case T_BIGNUM:
828
+ write_bignum(writer, value);
829
+ break;
830
+ case T_STRUCT:
831
+ write_struct(writer, value);
832
+ break;
833
+ default:
834
+ if (rb_obj_is_kind_of(value, rb_cUsaminValue))
835
+ write_usamin(writer, value);
836
+ else
837
+ write_to_s(writer, value);
838
+ break;
839
+ }
840
+ }
841
+ }
842
+
843
+ /*
844
+ * Generate the JSON string from Ruby data structures.
845
+ *
846
+ * @overload generate(obj)
847
+ * @param [Object] obj an object to serialize
848
+ * @return [String]
849
+ */
850
+ static VALUE w_generate(const VALUE self, VALUE value) {
851
+ rapidjson::StringBuffer buf;
852
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
853
+ write(writer, value);
854
+ return new_utf8_str(buf.GetString(), buf.GetSize());
855
+ }
856
+
857
+ /*
858
+ * Generate the prettified JSON string from Ruby data structures.
859
+ *
860
+ * @overload generate(obj, opts = {})
861
+ * @param [Object] obj an object to serialize
862
+ * @param [::Hash] opts options
863
+ * @option opts [String] :indent (' ') a string used to indent
864
+ * @return [String]
865
+ */
866
+ static VALUE w_pretty_generate(const int argc, VALUE *argv, const VALUE self) {
867
+ VALUE value, options;
868
+ rb_scan_args(argc, argv, "1:", &value, &options);
869
+ rapidjson::StringBuffer buf;
870
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buf);
871
+
872
+ char indent_char = ' ';
873
+ unsigned int indent_count = 2;
874
+ if (argc > 1) {
875
+ VALUE v_indent = rb_hash_lookup(options, sym_indent);
876
+ VALUE v_single_line_array = rb_hash_lookup(options, sym_single_line_array);
877
+
878
+ if (RTEST(v_indent)) {
879
+ if (RB_FIXNUM_P(v_indent)) {
880
+ long l = FIX2LONG(v_indent);
881
+ indent_count = l > 0 ? static_cast<unsigned int>(l) : 0;
882
+ } else {
883
+ VALUE v = get_utf8_str(v_indent);
884
+ long vlen = RSTRING_LEN(v);
885
+ if (vlen == 0) {
886
+ indent_count = 0;
887
+ } else {
888
+ const char *indent_str = RSTRING_PTR(v);
889
+ switch (indent_str[0]) {
890
+ case '\0':
891
+ indent_count = 0;
892
+ break;
893
+ case ' ':
894
+ case '\t':
895
+ case '\r':
896
+ case '\n':
897
+ indent_char = indent_str[0];
898
+ break;
899
+ default:
900
+ rb_raise(rb_eUsaminError, ":indent must be a repetation of \" \", \"\\t\", \"\\r\" or \"\\n\".");
901
+ }
902
+
903
+ for (long i = 1; i < vlen; i++)
904
+ if (indent_str[0] != indent_str[i])
905
+ rb_raise(rb_eUsaminError, ":indent must be a repetation of \" \", \"\\t\", \"\\r\" or \"\\n\".");
906
+ indent_count = static_cast<unsigned int>(vlen);
907
+ }
908
+ }
909
+ }
910
+
911
+ if (RTEST(v_single_line_array))
912
+ writer.SetFormatOptions(rapidjson::kFormatSingleLineArray);
913
+ }
914
+ writer.SetIndent(indent_char, indent_count);
915
+
916
+ write(writer, value);
917
+ return new_utf8_str(buf.GetString(), buf.GetSize());
918
+ }
919
+
920
+
921
+ extern "C" void Init_usamin(void) {
922
+ utf8 = rb_utf8_encoding();
923
+ utf8index = rb_enc_to_index(utf8);
924
+ utf8value = rb_enc_from_encoding(utf8);
925
+ sym_fast = rb_id2sym(rb_intern("fast"));
926
+ sym_indent = rb_id2sym(rb_intern("indent"));
927
+ sym_single_line_array = rb_id2sym(rb_intern("single_line_array"));
928
+ id_to_s = rb_intern("to_s");
929
+
930
+ VALUE rb_mUsamin = rb_define_module("Usamin");
931
+ rb_define_module_function(rb_mUsamin, "load", RUBY_METHOD_FUNC(w_load), -1);
932
+ rb_define_module_function(rb_mUsamin, "parse", RUBY_METHOD_FUNC(w_parse), -1);
933
+ rb_define_module_function(rb_mUsamin, "generate", RUBY_METHOD_FUNC(w_generate), 1);
934
+ rb_define_module_function(rb_mUsamin, "pretty_generate", RUBY_METHOD_FUNC(w_pretty_generate), -1);
935
+
936
+ rb_cUsaminValue = rb_define_class_under(rb_mUsamin, "Value", rb_cObject);
937
+ rb_define_alloc_func(rb_cUsaminValue, usamin_alloc);
938
+ rb_undef_method(rb_cUsaminValue, "initialize");
939
+ rb_define_method(rb_cUsaminValue, "array?", RUBY_METHOD_FUNC(w_value_isarray), 0);
940
+ rb_define_method(rb_cUsaminValue, "hash?", RUBY_METHOD_FUNC(w_value_isobject), 0);
941
+ rb_define_method(rb_cUsaminValue, "object?", RUBY_METHOD_FUNC(w_value_isobject), 0);
942
+ rb_define_method(rb_cUsaminValue, "eval", RUBY_METHOD_FUNC(w_value_eval), 0);
943
+ rb_define_method(rb_cUsaminValue, "eval_r", RUBY_METHOD_FUNC(w_value_eval_r), 0);
944
+ rb_define_method(rb_cUsaminValue, "frozen?", RUBY_METHOD_FUNC(w_value_isfrozen), 0);
945
+ rb_define_method(rb_cUsaminValue, "marshal_dump", RUBY_METHOD_FUNC(w_value_marshal_dump), 0);
946
+ rb_define_method(rb_cUsaminValue, "marshal_load", RUBY_METHOD_FUNC(w_value_marshal_load), 1);
947
+
948
+ rb_cUsaminHash = rb_define_class_under(rb_mUsamin, "Hash", rb_cUsaminValue);
949
+ rb_include_module(rb_cUsaminHash, rb_mEnumerable);
950
+ rb_define_alloc_func(rb_cUsaminHash, usamin_alloc);
951
+ rb_undef_method(rb_cUsaminHash, "initialize");
952
+ rb_define_method(rb_cUsaminHash, "[]", RUBY_METHOD_FUNC(w_hash_operator_indexer), 1);
953
+ rb_define_method(rb_cUsaminHash, "each", RUBY_METHOD_FUNC(w_hash_each), 0);
954
+ rb_define_method(rb_cUsaminHash, "each_pair", RUBY_METHOD_FUNC(w_hash_each), 0);
955
+ rb_define_method(rb_cUsaminHash, "each_key", RUBY_METHOD_FUNC(w_hash_each_key), 0);
956
+ rb_define_method(rb_cUsaminHash, "each_value", RUBY_METHOD_FUNC(w_hash_each_value), 0);
957
+ rb_define_method(rb_cUsaminHash, "empty?", RUBY_METHOD_FUNC(w_hash_isempty), 0);
958
+ rb_define_method(rb_cUsaminHash, "has_key?", RUBY_METHOD_FUNC(w_hash_haskey), 1);
959
+ rb_define_method(rb_cUsaminHash, "include?", RUBY_METHOD_FUNC(w_hash_haskey), 1);
960
+ rb_define_method(rb_cUsaminHash, "key?", RUBY_METHOD_FUNC(w_hash_haskey), 1);
961
+ rb_define_method(rb_cUsaminHash, "member?", RUBY_METHOD_FUNC(w_hash_haskey), 1);
962
+ rb_define_method(rb_cUsaminHash, "keys", RUBY_METHOD_FUNC(w_hash_keys), 0);
963
+ rb_define_method(rb_cUsaminHash, "length", RUBY_METHOD_FUNC(w_hash_length), 0);
964
+ rb_define_method(rb_cUsaminHash, "size", RUBY_METHOD_FUNC(w_hash_length), 0);
965
+ rb_define_method(rb_cUsaminHash, "to_h", RUBY_METHOD_FUNC(w_hash_eval), 0);
966
+ rb_define_method(rb_cUsaminHash, "to_hash", RUBY_METHOD_FUNC(w_hash_eval), 0);
967
+ rb_define_method(rb_cUsaminHash, "values", RUBY_METHOD_FUNC(w_hash_values), 0);
968
+
969
+ rb_cUsaminArray = rb_define_class_under(rb_mUsamin, "Array", rb_cUsaminValue);
970
+ rb_include_module(rb_cUsaminArray, rb_mEnumerable);
971
+ rb_define_alloc_func(rb_cUsaminArray, usamin_alloc);
972
+ rb_undef_method(rb_cUsaminArray, "initialize");
973
+ rb_define_method(rb_cUsaminArray, "[]", RUBY_METHOD_FUNC(w_array_operator_indexer), -1);
974
+ rb_define_method(rb_cUsaminArray, "each", RUBY_METHOD_FUNC(w_array_each), 0);
975
+ rb_define_method(rb_cUsaminArray, "each_index", RUBY_METHOD_FUNC(w_array_each_index), 0);
976
+ rb_define_method(rb_cUsaminArray, "empty?", RUBY_METHOD_FUNC(w_array_isempty), 0);
977
+ rb_define_method(rb_cUsaminArray, "index", RUBY_METHOD_FUNC(w_array_index), -1);
978
+ rb_define_method(rb_cUsaminArray, "find_index", RUBY_METHOD_FUNC(w_array_index), -1);
979
+ rb_define_method(rb_cUsaminArray, "first", RUBY_METHOD_FUNC(w_array_first), -1);
980
+ rb_define_method(rb_cUsaminArray, "length", RUBY_METHOD_FUNC(w_array_length), 0);
981
+ rb_define_method(rb_cUsaminArray, "size", RUBY_METHOD_FUNC(w_array_length), 0);
982
+ rb_define_method(rb_cUsaminArray, "to_a", RUBY_METHOD_FUNC(w_array_eval), 0);
983
+ rb_define_method(rb_cUsaminArray, "to_ary", RUBY_METHOD_FUNC(w_array_eval), 0);
984
+
985
+ rb_eUsaminError = rb_define_class_under(rb_mUsamin, "UsaminError", rb_eStandardError);
986
+ rb_eParserError = rb_define_class_under(rb_mUsamin, "ParserError", rb_eUsaminError);
987
+ }