asciipack 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,299 @@
1
+ #include "unpacker.h"
2
+
3
+ VALUE cAsciiPack_Unpacker;
4
+
5
+ static VALUE Unpacker_read(unpacker_t* ptr);
6
+
7
+ static void
8
+ unpacker_mark(unpacker_t* ptr)
9
+ {
10
+ }
11
+
12
+ static VALUE
13
+ Unpacker_alloc(VALUE klass)
14
+ {
15
+ unpacker_t *ptr = ALLOC(unpacker_t);
16
+ return Data_Wrap_Struct(klass, unpacker_mark, -1, ptr);
17
+ }
18
+
19
+ static VALUE
20
+ Unpacker_init(VALUE self, VALUE obj, int argc, VALUE *argv, VALUE size)
21
+ {
22
+ UNPACKER(self, ptr);
23
+
24
+ if (!ptr) {
25
+ rb_raise(rb_eArgError, "unallocated unpacker");
26
+ }
27
+
28
+ ptr->buffer = RSTRING_PTR(obj);
29
+ ptr->ch = ptr->buffer;
30
+
31
+ return self;
32
+ }
33
+
34
+ static VALUE
35
+ Unpacker_initialize(int argc, VALUE *argv, VALUE self)
36
+ {
37
+ VALUE obj = *argv++;
38
+ VALUE size = Qnil;
39
+ return Unpacker_init(self, obj, argc, argv, size);
40
+ }
41
+
42
+ static unsigned short
43
+ to_i16 (char ch)
44
+ {
45
+ unsigned short ret = 0;
46
+ if ('0' <= ch && ch <= '9') {
47
+ ret += ch - '0';
48
+ } else if ('a' <= ch && ch <= 'f') {
49
+ ret += ch - 'a' + 10;
50
+ }
51
+ return ret;
52
+ }
53
+
54
+ static uint64_t
55
+ to_i16all (unpacker_t* ptr, int len)
56
+ {
57
+ uint64_t ret = 0;
58
+ while (len--) {
59
+ ret += to_i16(*ptr->ch);
60
+ ptr->ch++;
61
+ if (len != 0) ret = ret << 4;
62
+ }
63
+ return ret;
64
+ }
65
+
66
+ static uint64_t
67
+ Unpacker_int (unpacker_t* ptr, size_t len)
68
+ {
69
+ size_t n = len;
70
+ char* head = ptr->ch;
71
+ uint64_t base = 1;
72
+ uint64_t ret = to_i16all(ptr, len);
73
+
74
+ if ('8' <= *head) {
75
+ ret -= (base << (n * 4));
76
+ }
77
+ return ret;
78
+ }
79
+
80
+ static uint64_t
81
+ Unpacker_uint (unpacker_t* ptr, size_t len)
82
+ {
83
+ return to_i16all(ptr, len);
84
+ }
85
+
86
+ static double
87
+ Unpacker_float (unpacker_t* ptr, size_t len)
88
+ {
89
+ uint64_t ret = to_i16all(ptr, len);
90
+ /* for convert unsigned long <-> float */
91
+ union {
92
+ unsigned long u;
93
+ double f64;
94
+ } converter = {ret};
95
+ return converter.f64;
96
+ }
97
+
98
+ static VALUE
99
+ Unpacker_str (unpacker_t* ptr, size_t len)
100
+ {
101
+ char* head = ptr->ch;
102
+
103
+ VALUE str = rb_str_new(head, len);
104
+ rb_funcall(str, rb_intern("force_encoding"), 1, rb_str_new2("utf-8"));
105
+ return str;
106
+ }
107
+
108
+ static VALUE
109
+ Unpacker_map (unpacker_t* ptr, size_t len)
110
+ {
111
+ VALUE map = rb_hash_new();
112
+ VALUE key, value;
113
+ while (len--) {
114
+ key = Unpacker_read(ptr);
115
+ value = Unpacker_read(ptr);
116
+ rb_hash_aset(map, key, value);
117
+ }
118
+ return map;
119
+ }
120
+
121
+ static VALUE
122
+ Unpacker_array (unpacker_t* ptr, size_t len)
123
+ {
124
+ VALUE array = rb_ary_new2(len);
125
+ while (len--) {
126
+ rb_ary_push(array, Unpacker_read(ptr));
127
+ }
128
+ return array;
129
+ }
130
+
131
+ static VALUE
132
+ Unpacker_read (unpacker_t* ptr)
133
+ {
134
+ uint64_t num;
135
+
136
+ ptr->ch++;
137
+
138
+ switch (*(ptr->ch - 1)) {
139
+ case 'a': // int 4
140
+ num = Unpacker_int(ptr, 1);
141
+ return INT2FIX(num);
142
+
143
+ case 'b': // int 8
144
+ num = Unpacker_int(ptr, 2);
145
+ return INT2FIX(num);
146
+
147
+ case 'c': // int 16
148
+ num = Unpacker_int(ptr, 4);
149
+ return INT2FIX(num);
150
+
151
+ case 'd': // int 32
152
+ num = Unpacker_int(ptr, 8);
153
+ return LONG2NUM(num);
154
+
155
+ case 'e': // int 64
156
+ num = Unpacker_int(ptr, 16);
157
+ return rb_ll2inum(num);
158
+
159
+ case 'g': // uint 8
160
+ num = Unpacker_uint(ptr, 2);
161
+ return INT2FIX(num);
162
+
163
+ case 'h': // uint 16
164
+ num = Unpacker_uint(ptr, 4);
165
+ return INT2FIX(num);
166
+
167
+ case 'i': // uint 32
168
+ num = Unpacker_uint(ptr, 8);
169
+ return LONG2NUM(num);
170
+
171
+ case 'j': // uint 64
172
+ num = Unpacker_uint(ptr, 16);
173
+ return rb_ull2inum(num);
174
+
175
+ case 'k': // float 32
176
+ return rb_float_new(Unpacker_float(ptr, 8));
177
+
178
+ case 'l': // float 64
179
+ return rb_float_new(Unpacker_float(ptr, 16));
180
+
181
+ case 'n': // str 8
182
+ num = Unpacker_uint(ptr, 2);
183
+ return Unpacker_str(ptr, num);
184
+
185
+ case 'o': // str 16
186
+ num = Unpacker_uint(ptr, 4);
187
+ return Unpacker_str(ptr, num);
188
+
189
+ case 'p': // str 32
190
+ num = Unpacker_uint(ptr, 8);
191
+ return Unpacker_str(ptr, num);
192
+
193
+ case 'r': // map 4
194
+ num = Unpacker_uint(ptr, 1);
195
+ return Unpacker_map(ptr, num);
196
+
197
+ case 's': // map 8
198
+ num = Unpacker_uint(ptr, 2);
199
+ return Unpacker_map(ptr, num);
200
+
201
+ case 't': // map16
202
+ num = Unpacker_uint(ptr, 4);
203
+ return Unpacker_map(ptr, num);
204
+
205
+ case 'u': // map 32
206
+ num = Unpacker_uint(ptr, 8);
207
+ return Unpacker_map(ptr, num);
208
+
209
+ case 'v': // array 4
210
+ num = Unpacker_uint(ptr, 1);
211
+ return Unpacker_array(ptr, num);
212
+
213
+ case 'w': // array 8
214
+ num = Unpacker_uint(ptr, 2);
215
+ return Unpacker_array(ptr, num);
216
+
217
+ case 'x': // array 16
218
+ num = Unpacker_uint(ptr, 4);
219
+ return Unpacker_array(ptr, num);
220
+
221
+ case 'y': // array 32
222
+ num = Unpacker_uint(ptr, 8);
223
+ return Unpacker_array(ptr, num);
224
+
225
+ // positive fixint
226
+ case '0': return INT2FIX(0);
227
+ case '1': return INT2FIX(1);
228
+ case '2': return INT2FIX(2);
229
+ case '3': return INT2FIX(3);
230
+ case '4': return INT2FIX(4);
231
+ case '5': return INT2FIX(5);
232
+ case '6': return INT2FIX(6);
233
+ case '7': return INT2FIX(7);
234
+ case '8': return INT2FIX(8);
235
+ case '9': return INT2FIX(9);
236
+ case 'A': return INT2FIX(10);
237
+ case 'B': return INT2FIX(11);
238
+ case 'C': return INT2FIX(12);
239
+ case 'D': return INT2FIX(13);
240
+ case 'E': return INT2FIX(14);
241
+ case 'F': return INT2FIX(15);
242
+
243
+ // fixstr
244
+ case 'G':
245
+ return rb_str_new2("");
246
+ case 'H':
247
+ case 'I':
248
+ case 'J':
249
+ case 'K':
250
+ case 'L':
251
+ case 'M':
252
+ case 'N':
253
+ case 'O':
254
+ case 'P':
255
+ case 'Q':
256
+ case 'R':
257
+ case 'S':
258
+ case 'T':
259
+ case 'U':
260
+ case 'V':
261
+ num = *(ptr->ch - 1) - 'G';
262
+ return Unpacker_str(ptr, num);
263
+
264
+ // others
265
+ case 'W': return Qnil;
266
+ case 'X': return Qfalse;
267
+ case 'Y': return Qtrue;
268
+ }
269
+
270
+ rb_raise(rb_eArgError, "undefined type:%c", *(ptr->ch));
271
+ return Qnil;
272
+ }
273
+
274
+ static VALUE
275
+ Unpacker_unpack (VALUE self)
276
+ {
277
+ UNPACKER(self, ptr);
278
+ return Unpacker_read(ptr);
279
+ }
280
+
281
+ static VALUE
282
+ AsciiPack_unpack (int argc, VALUE *argv, VALUE self)
283
+ {
284
+ VALUE unpacker = rb_funcall(cAsciiPack_Unpacker, rb_intern("new"), 1, argv[0]);
285
+ return rb_funcall(unpacker, rb_intern("unpack"), 0);
286
+ }
287
+
288
+ void
289
+ AsciiPack_Unpacker_init(VALUE mAsciiPack)
290
+ {
291
+ cAsciiPack_Unpacker = rb_define_class_under(mAsciiPack, "Unpacker", rb_cObject);
292
+
293
+ rb_define_alloc_func(cAsciiPack_Unpacker, Unpacker_alloc);
294
+
295
+ rb_define_method(cAsciiPack_Unpacker, "initialize", Unpacker_initialize, -1);
296
+ rb_define_method(cAsciiPack_Unpacker, "unpack", Unpacker_unpack, 0);
297
+
298
+ rb_define_module_function(mAsciiPack, "unpack", AsciiPack_unpack, -1);
299
+ }
@@ -0,0 +1,25 @@
1
+ #ifndef UNPACKER_H
2
+ # define UNPACKER_H
3
+
4
+ #include <stdlib.h>
5
+
6
+ struct unpacker {
7
+ char* buffer;
8
+ char* ch;
9
+ };
10
+ typedef struct unpacker unpacker_t;
11
+
12
+ #define UNPACKER(from, name) \
13
+ unpacker_t* name; \
14
+ Data_Get_Struct(from, unpacker_t, name); \
15
+ if (name == NULL) { \
16
+ rb_raise(rb_eArgError, "NULL found for " # name " when shouldn't be.'"); \
17
+ }
18
+
19
+ #include "ruby.h"
20
+
21
+ extern VALUE cAsciiPack_Unpacker;
22
+ void AsciiPack_Unpacker_init(VALUE mAsciiPack);
23
+
24
+ #endif /* ifndef UNPACKER_H */
25
+
@@ -1,3 +1,3 @@
1
1
  module AsciiPack
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/asciipack.rb CHANGED
@@ -1,3 +1,3 @@
1
+ here = File.expand_path(File.dirname(__FILE__))
2
+ require File.join(here, 'asciipack', 'asciipack')
1
3
  require "asciipack/version"
2
- require 'asciipack/packer.rb'
3
- require 'asciipack/unpacker.rb'
data/spec/bench.rb CHANGED
@@ -7,51 +7,70 @@ require 'asciipack'
7
7
  require 'json'
8
8
  require 'msgpack'
9
9
 
10
- def json_asciipack(name, obj)
10
+ def count
11
+ 10000
12
+ end
13
+
14
+ def reports (obj)
11
15
  obj = [obj]
12
16
  json = obj.to_json
13
17
  ap = AsciiPack.pack obj
14
18
  ms = Marshal.dump obj
15
19
  msg = MessagePack.pack obj
16
20
 
17
- p '[' + name + ']'
18
- bench "AsciiPack.pack" do AsciiPack.pack obj end
19
- bench "AsciiPack.unpack" do AsciiPack.unpack ap end
20
- bench "JSON.generate" do obj.to_json end
21
- bench "JSON.parse" do JSON.parse json end
22
- bench "Marshal.dump" do Marshal.dump obj end
23
- bench "Marshal.load" do Marshal.load ms end
24
- bench "MessagePack.pack" do MessagePack.pack obj end
25
- bench "MessagePack.unpack" do MessagePack.unpack msg end
26
- p({
27
- :ap => ap.length,
28
- :json => json.length,
29
- :marshal => ms.length,
30
- :msgpack => msg.length
31
- })
21
+ {
22
+ "AsciiPack.pack" => lambda { AsciiPack.pack obj },
23
+ "AsciiPack.unpack" => lambda { AsciiPack.unpack ap },
24
+ "MessagePack.pack" => lambda { MessagePack.pack obj },
25
+ "MessagePack.unpack" => lambda { MessagePack.unpack msg },
26
+ "JSON.generate" => lambda { obj.to_json },
27
+ "JSON.parse" => lambda { JSON.parse json },
28
+ "Marshal.dump" => lambda { Marshal.dump obj },
29
+ "Marshal.load" => lambda { Marshal.load ms },
30
+ }
32
31
  end
33
32
 
34
- def bench(name)
35
- t = Time.now
36
- 100000.times {
37
- yield
33
+ def json_asciipack(name, obj)
34
+ print("|" + name + "|")
35
+
36
+ results = []
37
+ reports(obj).each { |_, func|
38
+ t = Time.now
39
+ count.times {
40
+ func.call
41
+ }
42
+ results << "%.1f" % ((Time.now - t) * 1000)
38
43
  }
39
- p name + ': ' + (Time.now - t).to_s + 's'
44
+ puts results.join("|") + "|"
40
45
  end
41
46
 
47
+ puts("|object|" + reports(0).keys.join("|") + "|")
48
+ puts("|---|" + reports(0).keys.map{"---"}.join("|") + "|")
49
+
50
+ map16 = {}
51
+ a = '@'
52
+ 0x100.times {|i| map16[i.to_s] = 0 }
53
+
42
54
  tt = Time.now
43
55
  {
44
56
  "positive fixint" => 0,
45
- "uint 4" => 16,
57
+ "uint 64" => 0xffffffffffffffff,
46
58
  "int 4" => -1,
47
- "fixstr" => "",
48
- "str 8" => '0123456789abcdef',
59
+ "int 64" => -0x8000000000000000,
49
60
  "float 64" => 1/3,
61
+ "fixstr" => "a",
62
+ "str 32" => 'a' * 0x10000,
50
63
  "map 4" => {},
64
+ "map 16" => map16,
51
65
  "array 4" => [],
66
+ "array 16" => Array.new(0x100,0),
52
67
  "nil" => nil,
53
68
  }.each { |key, value|
54
69
  json_asciipack key, value
55
70
  }
56
71
 
57
- p 'total: ' + (Time.now - tt).to_s + 's'
72
+ puts "\n"
73
+ puts "count:#{count}"
74
+ puts "unit:/ms"
75
+ puts 'total:' + (Time.now - tt).to_s + 's'
76
+
data/spec/format_spec.rb CHANGED
@@ -64,39 +64,41 @@ describe AsciiPack do
64
64
  format 1.0, T.float64, 17
65
65
  format 0.1, T.float64, 17
66
66
  format -0.1, T.float64, 17
67
- expect(AsciiPack.pack(-0.1)).to eq(T.float64 + 'bfb999999999999a')
68
- expect(AsciiPack.pack(1.0)).to eq(T.float64 + '3ff0000000000000')
69
- expect(AsciiPack.pack(1.0000000000000002)).to eq(T.float64 + '3ff0000000000001')
70
- expect(AsciiPack.pack(1.0000000000000004)).to eq(T.float64 + '3ff0000000000002')
71
- expect(AsciiPack.pack(1/3.0)).to eq(T.float64 + '3fd5555555555555')
72
- expect(AsciiPack.pack(Float::NAN)).to eq(T.float64 + '7fffffffffffffff')
73
- expect(AsciiPack.pack(1 / 0.0)).to eq(T.float64 + '7ff0000000000000')
74
- expect(AsciiPack.pack(-1 / 0.0)).to eq(T.float64 + 'fff0000000000000')
67
+ check_each_other(-0.1, T.float64 + 'bfb999999999999a')
68
+ check_each_other(1.0, T.float64 + '3ff0000000000000')
69
+ check_each_other(1.0000000000000002, T.float64 + '3ff0000000000001')
70
+ check_each_other(1.0000000000000004, T.float64 + '3ff0000000000002')
71
+ check_each_other(1/3.0, T.float64 + '3fd5555555555555')
72
+ expect(AsciiPack.pack(Float::NAN)).to eq(T.float64 + '7ff8000000000000')
73
+ check_each_other(1 / 0.0, T.float64 + '7ff0000000000000')
74
+ check_each_other(-1 / 0.0, T.float64 + 'fff0000000000000')
75
75
  end
76
76
 
77
- it "fixbin" do
78
- format "", T.fixbin_0, 1
79
- format " ", T.fixbin_1, 2
80
- format "あ", T.fixbin_1, 2
81
- format "漢字", T.fixbin_2, 3
82
- format " " * 0xe, T.fixbin_E, 15
83
- format " " * 0xf, T.fixbin_F, 16
77
+ it "fixstr" do
78
+ format "", T.fixstr_0, 1
79
+ format " ", T.fixstr_1, 2
80
+ format "あ", T.fixstr_3, 4
81
+ format "漢字", T.fixstr_6, 7
82
+ format " " * 0xe, T.fixstr_E, 15
83
+ format " " * 0xf, T.fixstr_F, 16
84
84
  end
85
85
 
86
- it "bin 8" do
87
- format "a" * 0x10, T.bin8, 3 + 0x10
88
- format "a" * 0xff, T.bin8, 3 + 0xff
86
+ it "str 8" do
87
+ format "a" * 0x10, T.str8, 3 + 0x10
88
+ format "a" * 0xff, T.str8, 3 + 0xff
89
89
  end
90
90
 
91
- it "bin 16" do
92
- format "a" * 0x100, T.bin16, 5 + 0x100
93
- format "a" * 0xffff, T.bin16, 5 + 0xffff
91
+ it "str 16" do
92
+ 100.times {
93
+ format "a" * 0x100, T.str16, 5 + 0x100
94
+ format "a" * 0xffff, T.str16, 5 + 0xffff
95
+ }
94
96
  end
95
97
 
96
- it "bin 32" do
97
- format "a" * 0x10000, T.bin32, 9 + 0x10000
98
+ it "str 32" do
99
+ format "a" * 0x10000, T.str32, 9 + 0x10000
98
100
  # FIXME too late
99
- # format "a" * 0xffffffff, T.bin32, 9 + 0xffffffff
101
+ # format "a" * 0xffffffff, T.str32, 9 + 0xffffffff
100
102
  end
101
103
 
102
104
  it "map 4" do
@@ -141,6 +143,15 @@ describe AsciiPack do
141
143
  # format_array 0xffffffff, T.array32
142
144
  end
143
145
 
146
+ it "recursive array" do
147
+ array = [0]
148
+ 1000.times {
149
+ array = [array]
150
+ }
151
+ ap = AsciiPack.pack(array)
152
+ expect(AsciiPack.unpack(ap)).to eq(array)
153
+ end
154
+
144
155
  it "nil" do
145
156
  format nil, T.nil, 1
146
157
  end
@@ -161,6 +172,11 @@ def format (object, first, length)
161
172
  expect(AsciiPack.unpack(ap)).to eq(object)
162
173
  end
163
174
 
175
+ def check_each_other (object, ap)
176
+ expect(AsciiPack.pack(object)).to eq(ap)
177
+ expect(AsciiPack.unpack(ap)).to eq(object)
178
+ end
179
+
164
180
  def format_map (count, first)
165
181
  map = {}
166
182
  count.times{ |i| map[i] = 0 }
data/spec/spec_helper.rb CHANGED
@@ -15,9 +15,9 @@ T = OpenStruct.new({
15
15
  :float32 => 'k',
16
16
  :float64 => 'l',
17
17
  # (blank) => 'm',
18
- :bin8 => 'n',
19
- :bin16 => 'o',
20
- :bin32 => 'p',
18
+ :str8 => 'n',
19
+ :str16 => 'o',
20
+ :str32 => 'p',
21
21
  # (blank) => 'q',
22
22
  :map4 => 'r',
23
23
  :map8 => 's',
@@ -44,22 +44,22 @@ T = OpenStruct.new({
44
44
  :positive_fixint_D => 'D',
45
45
  :positive_fixint_E => 'E',
46
46
  :positive_fixint_F => 'F',
47
- :fixbin_0 => 'G',
48
- :fixbin_1 => 'H',
49
- :fixbin_2 => 'I',
50
- :fixbin_3 => 'J',
51
- :fixbin_4 => 'K',
52
- :fixbin_5 => 'L',
53
- :fixbin_6 => 'M',
54
- :fixbin_7 => 'N',
55
- :fixbin_8 => 'O',
56
- :fixbin_9 => 'P',
57
- :fixbin_A => 'Q',
58
- :fixbin_B => 'R',
59
- :fixbin_C => 'S',
60
- :fixbin_D => 'T',
61
- :fixbin_E => 'U',
62
- :fixbin_F => 'V',
47
+ :fixstr_0 => 'G',
48
+ :fixstr_1 => 'H',
49
+ :fixstr_2 => 'I',
50
+ :fixstr_3 => 'J',
51
+ :fixstr_4 => 'K',
52
+ :fixstr_5 => 'L',
53
+ :fixstr_6 => 'M',
54
+ :fixstr_7 => 'N',
55
+ :fixstr_8 => 'O',
56
+ :fixstr_9 => 'P',
57
+ :fixstr_A => 'Q',
58
+ :fixstr_B => 'R',
59
+ :fixstr_C => 'S',
60
+ :fixstr_D => 'T',
61
+ :fixstr_E => 'U',
62
+ :fixstr_F => 'V',
63
63
  :nil => 'W',
64
64
  :false => 'X',
65
65
  :true => 'Y',
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciipack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ksss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-16 00:00:00.000000000 Z
11
+ date: 2013-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,10 +52,25 @@ dependencies:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake-compiler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.8.3
55
69
  description: AsciiPack is an object serialization inspired by MessagePack.
56
70
  email: co000ri@gmail.com
57
71
  executables: []
58
- extensions: []
72
+ extensions:
73
+ - ext/asciipack/extconf.rb
59
74
  extra_rdoc_files: []
60
75
  files:
61
76
  - .gitignore
@@ -64,9 +79,13 @@ files:
64
79
  - README.md
65
80
  - Rakefile
66
81
  - asciipack.gemspec
82
+ - ext/asciipack/extconf.rb
83
+ - ext/asciipack/init.c
84
+ - ext/asciipack/packer.c
85
+ - ext/asciipack/packer.h
86
+ - ext/asciipack/unpacker.c
87
+ - ext/asciipack/unpacker.h
67
88
  - lib/asciipack.rb
68
- - lib/asciipack/packer.rb
69
- - lib/asciipack/unpacker.rb
70
89
  - lib/asciipack/version.rb
71
90
  - spec/bench.rb
72
91
  - spec/format_spec.rb