Skip to content

Commit 9691d6d

Browse files
committed
Working
1 parent 01b38a9 commit 9691d6d

File tree

8 files changed

+216
-17
lines changed

8 files changed

+216
-17
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
*.o
1212
*.a
1313
mkmf.log
14+
perf.data

Gemfile

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ source "https://rubygems.org"
66
gemspec
77

88
gem "rake", "~> 13.0"
9-
109
gem "rake-compiler"
11-
1210
gem "minitest", "~> 5.0"
11+
12+
group :benchmark do
13+
gem "benchmark-ips"
14+
gem "oj"
15+
gem "yajl-ruby"
16+
gem "json"
17+
end

Gemfile.lock

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
PATH
2+
remote: .
3+
specs:
4+
rapidjson (0.1.0)
5+
6+
GEM
7+
remote: https://rubygems.org/
8+
specs:
9+
benchmark-ips (2.10.0)
10+
json (2.6.1)
11+
minitest (5.15.0)
12+
oj (3.13.11)
13+
rake (13.0.6)
14+
rake-compiler (1.1.9)
15+
rake
16+
yajl-ruby (1.4.1)
17+
18+
PLATFORMS
19+
x86_64-linux
20+
21+
DEPENDENCIES
22+
benchmark-ips
23+
json
24+
minitest (~> 5.0)
25+
oj
26+
rake (~> 13.0)
27+
rake-compiler
28+
rapidjson!
29+
yajl-ruby
30+
31+
BUNDLED WITH
32+
2.3.4

benchmark.rb

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
require "benchmark/ips"
2+
require "json"
3+
require "oj"
4+
require "yajl"
5+
require "rapidjson"
6+
7+
if ENV["ONLY"]
8+
RUN = ENV["ONLY"].split(/[,: ]/).map{|x| [x.to_sym, true] }.to_h
9+
RUN.default = false
10+
elsif ENV["EXCEPT"]
11+
RUN = ENV["only"].split(/[,: ]/).map{|x| [x.to_sym, false] }.to_h
12+
RUN.default = true
13+
else
14+
RUN = Hash.new(true)
15+
end
16+
17+
def benchmark_encoding(name, ruby_obj)
18+
json_output = JSON.dump(ruby_obj)
19+
puts "== Encoding #{name} (#{json_output.size} bytes)"
20+
21+
Benchmark.ips do |x|
22+
x.report("yajl") { Yajl::Encoder.new.encode(ruby_obj) } if RUN[:yajl]
23+
x.report("json") { JSON.dump(ruby_obj) } if RUN[:json]
24+
x.report("oj") { Oj.dump(ruby_obj) } if RUN[:oj]
25+
x.report("rapidjson") { RapidJSON.encode(ruby_obj) } if RUN[:rapidjson]
26+
end
27+
puts
28+
end
29+
30+
benchmark_encoding "small nested array", [[1,2,3,4,5]]*10
31+
benchmark_encoding "small hash", { "username" => "jhawthorn", "id" => 123, "event" => "wrote json serializer" }
32+
benchmark_encoding "canada.json", JSON.load_file("#{__dir__}/test/data/canada.json")

ext/rapidjson/cext.cc

+86-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ class RubyObjectEncoder {
1414
Writer<StringBuffer> writer;
1515

1616
void test() {
17-
writer.StartObject();
1817
writer.Key("hello");
1918
writer.String("world");
2019
writer.Key("t");
@@ -35,22 +34,105 @@ class RubyObjectEncoder {
3534
writer.EndObject();
3635
}
3736

37+
void encode_array(VALUE ary) {
38+
writer.StartArray();
39+
int length = RARRAY_LEN(ary);
40+
RARRAY_PTR_USE(ary, ptr, {
41+
for (int i = 0; i < length; i++) {
42+
encode_any(ptr[i]);
43+
}
44+
});
45+
writer.EndArray();
46+
}
47+
48+
void encode_key(VALUE key) {
49+
switch(rb_type(key)) {
50+
case T_STRING:
51+
writer.Key(RSTRING_PTR(key), RSTRING_LEN(key));
52+
return;
53+
default:
54+
raise_unknown(key);
55+
}
56+
}
57+
58+
static int encode_hash_i_cb(VALUE key, VALUE val, VALUE ctx) {
59+
RubyObjectEncoder *encoder = (RubyObjectEncoder *)ctx;
60+
encoder->encode_hash_i(key, val);
61+
return ST_CONTINUE;
62+
}
63+
64+
void encode_hash_i(VALUE key, VALUE val) {
65+
encode_key(key);
66+
encode_any(val);
67+
}
68+
69+
void encode_hash(VALUE hash) {
70+
writer.StartObject();
71+
rb_hash_foreach(hash, encode_hash_i_cb, (VALUE)this);
72+
writer.EndObject();
73+
}
74+
75+
void encode_fixnum(VALUE v) {
76+
writer.Int(RB_FIX2INT(v));
77+
}
78+
79+
void encode_float(VALUE v) {
80+
double f = rb_float_value(v);
81+
writer.Double(f);
82+
}
83+
84+
void encode_string(VALUE v) {
85+
// fixme: copy boolean?
86+
writer.String(RSTRING_PTR(v), RSTRING_LEN(v));
87+
}
88+
89+
void encode_any(VALUE v) {
90+
switch(rb_type(v)) {
91+
case T_NIL:
92+
writer.Null();
93+
return;
94+
case T_FALSE:
95+
writer.Bool(false);
96+
return;
97+
case T_TRUE:
98+
writer.Bool(true);
99+
return;
100+
case T_FIXNUM:
101+
return encode_fixnum(v);
102+
case T_FLOAT:
103+
return encode_float(v);
104+
case T_HASH:
105+
return encode_hash(v);
106+
case T_ARRAY:
107+
return encode_array(v);
108+
case T_STRING:
109+
return encode_string(v);
110+
default:
111+
raise_unknown(v);
112+
}
113+
}
114+
115+
void raise_unknown(VALUE obj) {
116+
VALUE inspect = rb_inspect(obj);
117+
rb_raise(rb_eRuntimeError, "can't encode type: %s", StringValueCStr(inspect));
118+
}
119+
38120
public:
39121
RubyObjectEncoder(): depth(0), buf(), writer(buf) {
40122
};
41123

42124
int depth;
43125

44-
VALUE encode() {
45-
test();
126+
VALUE encode(VALUE obj) {
127+
encode_any(obj);
46128
VALUE ruby_string = rb_str_new(buf.GetString(), buf.GetSize());
47129
return ruby_string;
48130
}
49131
};
50132

51133
VALUE encode(VALUE _self, VALUE obj) {
52134
RubyObjectEncoder encoder;
53-
return encoder.encode();
135+
return encoder.encode(obj);
54136
}
55137

56138
extern "C" void

ext/rapidjson/extconf.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
require "mkmf"
44

55
submodule = "#{__dir__}/rapidjson"
6-
$CXXFLAGS += " -I#{submodule}/include/ "
6+
$CXXFLAGS += " -O3 -I#{submodule}/include/ "
77

88
create_makefile("rapidjson/rapidjson")

rapidjson.gemspec

+5-7
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,15 @@ Gem::Specification.new do |spec|
88
spec.authors = ["John Hawthorn"]
99
spec.email = ["[email protected]"]
1010

11-
spec.summary = "TODO: Write a short summary, because RubyGems requires one."
12-
spec.description = "TODO: Write a longer description or delete this line."
13-
spec.homepage = "TODO: Put your gem's website or public repo URL here."
11+
spec.summary = "Fast JSON encoder/decoder based using RapidJSON"
12+
spec.description = spec.summary
13+
spec.homepage = "https://github.com/jhawthorn/rapidjson"
1414
spec.license = "MIT"
1515
spec.required_ruby_version = ">= 2.6.0"
1616

17-
spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
18-
1917
spec.metadata["homepage_uri"] = spec.homepage
20-
spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
21-
spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
18+
spec.metadata["source_code_uri"] = spec.homepage
19+
spec.metadata["changelog_uri"] = spec.homepage
2220

2321
# Specify which files should be added to the gem when it is released.
2422
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.

test/test_encoder.rb

+52-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,57 @@
33
require "test_helper"
44

55
class TestRapidjson < Minitest::Test
6-
def test_encoding
7-
s = RapidJSON.dump({})
8-
p s
6+
def encode obj
7+
RapidJSON.encode(obj)
8+
end
9+
10+
def test_encode_array
11+
assert_equal "[]", encode([])
12+
assert_equal "[1,2,3]", encode([1,2,3])
13+
assert_equal "[1,2,3,4]", encode([1,2,3,4])
14+
assert_equal "[1.0]", encode([1.0])
15+
end
16+
17+
def test_encode_fixnum
18+
assert_equal "-5", encode(-5)
19+
assert_equal "-1", encode(-1)
20+
assert_equal "0", encode(0)
21+
assert_equal "5", encode(5)
22+
assert_equal "123", encode(123)
23+
end
24+
25+
def test_encode_float
26+
assert_equal "0.0", encode(0.0)
27+
assert_equal "-0.0", encode(-0.0)
28+
assert_equal "155.0", encode(155.0)
29+
end
30+
31+
def test_encode_hash
32+
assert_equal '{}', encode({})
33+
assert_equal '{"foo":"bar"}', encode({ "foo" => "bar" })
34+
end
35+
36+
def test_encode_string
37+
assert_equal '""', encode("")
38+
assert_equal '"foo"', encode("foo")
39+
assert_equal '"abcdefghijklmnopqrstuvwxyz"', encode("abcdefghijklmnopqrstuvwxyz")
40+
end
41+
42+
def test_encode_object
43+
assert_raises RuntimeError do
44+
encode(Object.new)
45+
end
46+
end
47+
48+
def test_encode_true
49+
assert_equal "true", encode(true)
50+
end
51+
52+
def test_encode_false
53+
assert_equal "false", encode(false)
54+
end
55+
56+
def test_encode_nil
57+
assert_equal "null", encode(nil)
958
end
1059
end

0 commit comments

Comments
 (0)