beefcake-spanx 0.3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +16 -0
- data/LICENSE +22 -0
- data/README.md +116 -0
- data/Rakefile +2 -0
- data/beefcake.gemspec +22 -0
- data/bench/simple.rb +90 -0
- data/bin/protoc-gen-beefcake +11 -0
- data/dat/code_generator_request.dat +0 -0
- data/lib/beefcake.rb +246 -0
- data/lib/beefcake/buffer.rb +2 -0
- data/lib/beefcake/buffer/base.rb +98 -0
- data/lib/beefcake/buffer/decode.rb +107 -0
- data/lib/beefcake/buffer/encode.rb +116 -0
- data/lib/beefcake/generator.rb +288 -0
- data/lib/beefcake/version.rb +3 -0
- data/test/buffer_decode_test.rb +114 -0
- data/test/buffer_encode_test.rb +222 -0
- data/test/buffer_test.rb +44 -0
- data/test/generator_test.rb +51 -0
- data/test/message_test.rb +371 -0
- metadata +92 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
module Beefcake
|
2
|
+
|
3
|
+
class Buffer
|
4
|
+
|
5
|
+
MinUint32 = 0
|
6
|
+
MaxUint32 = (1<<32)-1
|
7
|
+
MinInt32 = -(1<<31)
|
8
|
+
MaxInt32 = (1<<31)-1
|
9
|
+
|
10
|
+
MinUint64 = 0
|
11
|
+
MaxUint64 = (1<<64)-1
|
12
|
+
MinInt64 = -(1<<63)
|
13
|
+
MaxInt64 = (1<<63)-1
|
14
|
+
|
15
|
+
def self.wire_for(type)
|
16
|
+
case type
|
17
|
+
when Class
|
18
|
+
if encodable?(type)
|
19
|
+
2
|
20
|
+
else
|
21
|
+
raise UnknownType, type
|
22
|
+
end
|
23
|
+
when :int32, :uint32, :sint32, :int64, :uint64, :sint64, :bool, Module
|
24
|
+
0
|
25
|
+
when :fixed64, :sfixed64, :double
|
26
|
+
1
|
27
|
+
when :string, :bytes
|
28
|
+
2
|
29
|
+
when :fixed32, :sfixed32, :float
|
30
|
+
5
|
31
|
+
else
|
32
|
+
raise UnknownType, type
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.encodable?(type)
|
37
|
+
return false if ! type.is_a?(Class)
|
38
|
+
pims = type.public_instance_methods
|
39
|
+
pims.include?(:encode) || pims.include?("encode")
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_accessor :buf
|
43
|
+
|
44
|
+
alias :to_s :buf
|
45
|
+
alias :to_str :buf
|
46
|
+
|
47
|
+
class OutOfRangeError < StandardError
|
48
|
+
def initialize(n)
|
49
|
+
super("Value of of range: %d" % [n])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class BufferOverflowError < StandardError
|
54
|
+
def initialize(s)
|
55
|
+
super("Too many bytes read for %s" % [s])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class UnknownType < StandardError
|
60
|
+
def initialize(s)
|
61
|
+
super("Unknown type '%s'" % [s])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def initialize(buf="")
|
66
|
+
self.buf = buf
|
67
|
+
end
|
68
|
+
|
69
|
+
if ''.respond_to?(:force_encoding)
|
70
|
+
def buf=(buf)
|
71
|
+
@buf = buf.force_encoding('BINARY')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def length
|
76
|
+
@buf.respond_to?(:bytesize) ? @buf.bytesize : @buf.length
|
77
|
+
end
|
78
|
+
|
79
|
+
def <<(bytes)
|
80
|
+
buf << bytes
|
81
|
+
end
|
82
|
+
|
83
|
+
def read(n)
|
84
|
+
case n
|
85
|
+
when Class
|
86
|
+
n.decode(read_string)
|
87
|
+
when Symbol
|
88
|
+
__send__("read_#{n}")
|
89
|
+
when Module
|
90
|
+
read_uint64
|
91
|
+
else
|
92
|
+
buf.slice!(0, n)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'beefcake/buffer/base'
|
2
|
+
|
3
|
+
module Beefcake
|
4
|
+
|
5
|
+
class Buffer
|
6
|
+
|
7
|
+
def read_info
|
8
|
+
n = read_uint64
|
9
|
+
fn = n >> 3
|
10
|
+
wire = n & 0x7
|
11
|
+
|
12
|
+
[fn, wire]
|
13
|
+
end
|
14
|
+
|
15
|
+
def read_string
|
16
|
+
read(read_uint64)
|
17
|
+
end
|
18
|
+
alias :read_bytes :read_string
|
19
|
+
|
20
|
+
def read_fixed32
|
21
|
+
bytes = read(4)
|
22
|
+
bytes.unpack("V").first
|
23
|
+
end
|
24
|
+
|
25
|
+
def read_fixed64
|
26
|
+
bytes = read(8)
|
27
|
+
x, y = bytes.unpack("VV")
|
28
|
+
x + (y << 32)
|
29
|
+
end
|
30
|
+
|
31
|
+
def read_int64
|
32
|
+
n = read_uint64
|
33
|
+
if n > MaxInt64
|
34
|
+
n -= (1 << 64)
|
35
|
+
end
|
36
|
+
n
|
37
|
+
end
|
38
|
+
alias :read_int32 :read_int64
|
39
|
+
|
40
|
+
def read_uint64
|
41
|
+
n = shift = 0
|
42
|
+
while true
|
43
|
+
if shift >= 64
|
44
|
+
raise BufferOverflowError, "varint"
|
45
|
+
end
|
46
|
+
b = buf.slice!(0)
|
47
|
+
|
48
|
+
## 1.8.6 to 1.9 Compat
|
49
|
+
if b.respond_to?(:ord)
|
50
|
+
b = b.ord
|
51
|
+
end
|
52
|
+
|
53
|
+
n |= ((b & 0x7F) << shift)
|
54
|
+
shift += 7
|
55
|
+
if (b & 0x80) == 0
|
56
|
+
return n
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
alias :read_uint32 :read_uint64
|
61
|
+
|
62
|
+
def read_sint64
|
63
|
+
decode_zigzag(read_uint64)
|
64
|
+
end
|
65
|
+
alias :read_sint32 :read_sint64
|
66
|
+
|
67
|
+
def read_sfixed32
|
68
|
+
decode_zigzag(read_fixed32)
|
69
|
+
end
|
70
|
+
|
71
|
+
def read_sfixed64
|
72
|
+
decode_zigzag(read_fixed64)
|
73
|
+
end
|
74
|
+
|
75
|
+
def read_float
|
76
|
+
bytes = read(4)
|
77
|
+
bytes.unpack("e").first
|
78
|
+
end
|
79
|
+
|
80
|
+
def read_double
|
81
|
+
bytes = read(8)
|
82
|
+
bytes.unpack("E").first
|
83
|
+
end
|
84
|
+
|
85
|
+
def read_bool
|
86
|
+
read_int32 != 0
|
87
|
+
end
|
88
|
+
|
89
|
+
def skip(wire)
|
90
|
+
case wire
|
91
|
+
when 0 then read_uint64
|
92
|
+
when 1 then read_fixed64
|
93
|
+
when 2 then read_string
|
94
|
+
when 5 then read_fixed32
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def decode_zigzag(n)
|
102
|
+
(n >> 1) ^ -(n & 1)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'beefcake/buffer/base'
|
2
|
+
|
3
|
+
module Beefcake
|
4
|
+
|
5
|
+
class Buffer
|
6
|
+
|
7
|
+
def append(type, val, fn)
|
8
|
+
if fn != 0
|
9
|
+
wire = Buffer.wire_for(type)
|
10
|
+
append_info(fn, wire)
|
11
|
+
end
|
12
|
+
|
13
|
+
__send__("append_#{type}", val)
|
14
|
+
end
|
15
|
+
|
16
|
+
def append_info(fn, wire)
|
17
|
+
#self << ((fn << 3) | wire)
|
18
|
+
append_uint32((fn << 3) | wire)
|
19
|
+
end
|
20
|
+
|
21
|
+
def append_fixed32(n, tag=false)
|
22
|
+
if n < MinUint32 || n > MaxUint32
|
23
|
+
raise OutOfRangeError, n
|
24
|
+
end
|
25
|
+
|
26
|
+
self << [n].pack("V")
|
27
|
+
end
|
28
|
+
|
29
|
+
def append_fixed64(n)
|
30
|
+
if n < MinUint64 || n > MaxUint64
|
31
|
+
raise OutOfRangeError, n
|
32
|
+
end
|
33
|
+
|
34
|
+
self << [n & 0xFFFFFFFF, n >> 32].pack("VV")
|
35
|
+
end
|
36
|
+
|
37
|
+
def append_int32(n)
|
38
|
+
if n < MinInt32 || n > MaxInt32
|
39
|
+
raise OutOfRangeError, n
|
40
|
+
end
|
41
|
+
|
42
|
+
append_int64(n)
|
43
|
+
end
|
44
|
+
|
45
|
+
def append_uint32(n)
|
46
|
+
if n < MinUint32 || n > MaxUint32
|
47
|
+
raise OutOfRangeError, n
|
48
|
+
end
|
49
|
+
|
50
|
+
append_uint64(n)
|
51
|
+
end
|
52
|
+
|
53
|
+
def append_int64(n)
|
54
|
+
if n < MinInt64 || n > MaxInt64
|
55
|
+
raise OutOfRangeError, n
|
56
|
+
end
|
57
|
+
|
58
|
+
if n < 0
|
59
|
+
n += (1 << 64)
|
60
|
+
end
|
61
|
+
|
62
|
+
append_uint64(n)
|
63
|
+
end
|
64
|
+
|
65
|
+
def append_sint32(n)
|
66
|
+
append_uint32((n << 1) ^ (n >> 31))
|
67
|
+
end
|
68
|
+
|
69
|
+
def append_sfixed32(n)
|
70
|
+
append_fixed32((n << 1) ^ (n >> 31))
|
71
|
+
end
|
72
|
+
|
73
|
+
def append_sint64(n)
|
74
|
+
append_uint64((n << 1) ^ (n >> 63))
|
75
|
+
end
|
76
|
+
|
77
|
+
def append_sfixed64(n)
|
78
|
+
append_fixed64((n << 1) ^ (n >> 63))
|
79
|
+
end
|
80
|
+
|
81
|
+
def append_uint64(n)
|
82
|
+
if n < MinUint64 || n > MaxUint64
|
83
|
+
raise OutOfRangeError, n
|
84
|
+
end
|
85
|
+
|
86
|
+
while true
|
87
|
+
bits = n & 0x7F
|
88
|
+
n >>= 7
|
89
|
+
if n == 0
|
90
|
+
return self << bits
|
91
|
+
end
|
92
|
+
self << (bits | 0x80)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def append_float(n)
|
97
|
+
self << [n].pack("e")
|
98
|
+
end
|
99
|
+
|
100
|
+
def append_double(n)
|
101
|
+
self << [n].pack("E")
|
102
|
+
end
|
103
|
+
|
104
|
+
def append_bool(n)
|
105
|
+
append_int64(n ? 1 : 0)
|
106
|
+
end
|
107
|
+
|
108
|
+
def append_string(s)
|
109
|
+
append_uint64(s.length)
|
110
|
+
self << s
|
111
|
+
end
|
112
|
+
alias :append_bytes :append_string
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,288 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
# The above line allows concatenation of constant strings like ".pb.rb" to
|
3
|
+
# maintain the internal format of the buffers, rather than converting the
|
4
|
+
# buffer to US-ASCII
|
5
|
+
|
6
|
+
require 'beefcake'
|
7
|
+
require 'stringio'
|
8
|
+
|
9
|
+
class CodeGeneratorRequest
|
10
|
+
include Beefcake::Message
|
11
|
+
|
12
|
+
|
13
|
+
class FieldDescriptorProto
|
14
|
+
include Beefcake::Message
|
15
|
+
|
16
|
+
module Type
|
17
|
+
## 0 is reserved for errors.
|
18
|
+
## Order is weird for historical reasons.
|
19
|
+
TYPE_DOUBLE = 1
|
20
|
+
TYPE_FLOAT = 2
|
21
|
+
TYPE_INT64 = 3 ## Not ZigZag encoded. Negative numbers
|
22
|
+
## take 10 bytes. Use TYPE_SINT64 if negative
|
23
|
+
## values are likely.
|
24
|
+
TYPE_UINT64 = 4
|
25
|
+
TYPE_INT32 = 5 ## Not ZigZag encoded. Negative numbers
|
26
|
+
## take 10 bytes. Use TYPE_SINT32 if negative
|
27
|
+
## values are likely.
|
28
|
+
TYPE_FIXED64 = 6
|
29
|
+
TYPE_FIXED32 = 7
|
30
|
+
TYPE_BOOL = 8
|
31
|
+
TYPE_STRING = 9
|
32
|
+
TYPE_GROUP = 10 ## Tag-delimited aggregate.
|
33
|
+
TYPE_MESSAGE = 11 ## Length-delimited aggregate.
|
34
|
+
|
35
|
+
## New in version 2.
|
36
|
+
TYPE_BYTES = 12
|
37
|
+
TYPE_UINT32 = 13
|
38
|
+
TYPE_ENUM = 14
|
39
|
+
TYPE_SFIXED32 = 15
|
40
|
+
TYPE_SFIXED64 = 16
|
41
|
+
TYPE_SINT32 = 17 ## Uses ZigZag encoding.
|
42
|
+
TYPE_SINT64 = 18 ## Uses ZigZag encoding.
|
43
|
+
end
|
44
|
+
|
45
|
+
module Label
|
46
|
+
LABEL_OPTIONAL = 1
|
47
|
+
LABEL_REQUIRED = 2
|
48
|
+
LABEL_REPEATED = 3
|
49
|
+
end
|
50
|
+
|
51
|
+
optional :name, :string, 1
|
52
|
+
optional :number, :int32, 3
|
53
|
+
optional :label, Label, 4
|
54
|
+
|
55
|
+
## If type_name is set, this need not be set. If both this and type_name
|
56
|
+
## are set, this must be either TYPE_ENUM or TYPE_MESSAGE.
|
57
|
+
optional :type, Type, 5
|
58
|
+
|
59
|
+
## For message and enum types, this is the name of the type. If the name
|
60
|
+
## starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
|
61
|
+
## rules are used to find the type (i.e. first the nested types within this
|
62
|
+
## message are searched, then within the parent, on up to the root
|
63
|
+
## namespace).
|
64
|
+
optional :type_name, :string, 6
|
65
|
+
|
66
|
+
## For extensions, this is the name of the type being extended. It is
|
67
|
+
## resolved in the same manner as type_name.
|
68
|
+
optional :extended, :string, 2
|
69
|
+
|
70
|
+
## For numeric types, contains the original text representation of the value.
|
71
|
+
## For booleans, "true" or "false".
|
72
|
+
## For strings, contains the default text contents (not escaped in any way).
|
73
|
+
## For bytes, contains the C escaped value. All bytes >= 128 are escaped.
|
74
|
+
optional :default_value, :string, 7
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
class EnumValueDescriptorProto
|
79
|
+
include Beefcake::Message
|
80
|
+
|
81
|
+
optional :name, :string, 1
|
82
|
+
optional :number, :int32, 2
|
83
|
+
# optional EnumValueOptions options = 3;
|
84
|
+
end
|
85
|
+
|
86
|
+
class EnumDescriptorProto
|
87
|
+
include Beefcake::Message
|
88
|
+
|
89
|
+
optional :name, :string, 1
|
90
|
+
repeated :value, EnumValueDescriptorProto, 2
|
91
|
+
# optional :options, EnumOptions, 3
|
92
|
+
end
|
93
|
+
|
94
|
+
class DescriptorProto
|
95
|
+
include Beefcake::Message
|
96
|
+
|
97
|
+
optional :name, :string, 1
|
98
|
+
|
99
|
+
repeated :field, FieldDescriptorProto, 2
|
100
|
+
repeated :extended, FieldDescriptorProto, 6
|
101
|
+
repeated :nested_type, DescriptorProto, 3
|
102
|
+
repeated :enum_type, EnumDescriptorProto, 4
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
class FileDescriptorProto
|
107
|
+
include Beefcake::Message
|
108
|
+
|
109
|
+
optional :name, :string, 1 # file name, relative to root of source tree
|
110
|
+
optional :package, :string, 2 # e.g. "foo", "foo.bar", etc.
|
111
|
+
|
112
|
+
repeated :message_type, DescriptorProto, 4;
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
repeated :file_to_generate, :string, 1
|
117
|
+
optional :parameter, :string, 2
|
118
|
+
|
119
|
+
repeated :proto_file, FileDescriptorProto, 15
|
120
|
+
end
|
121
|
+
|
122
|
+
class CodeGeneratorResponse
|
123
|
+
include Beefcake::Message
|
124
|
+
|
125
|
+
class File
|
126
|
+
include Beefcake::Message
|
127
|
+
|
128
|
+
optional :name, :string, 1
|
129
|
+
optional :content, :string, 15
|
130
|
+
end
|
131
|
+
|
132
|
+
repeated :file, File, 15
|
133
|
+
end
|
134
|
+
|
135
|
+
module Beefcake
|
136
|
+
class Generator
|
137
|
+
|
138
|
+
L = CodeGeneratorRequest::FieldDescriptorProto::Label
|
139
|
+
T = CodeGeneratorRequest::FieldDescriptorProto::Type
|
140
|
+
|
141
|
+
|
142
|
+
def self.compile(ns, req)
|
143
|
+
file = req.proto_file.map do |file|
|
144
|
+
g = new(StringIO.new)
|
145
|
+
g.compile(ns, file)
|
146
|
+
|
147
|
+
g.c.rewind
|
148
|
+
CodeGeneratorResponse::File.new(
|
149
|
+
:name => File.basename(file.name, ".proto") + ".pb.rb",
|
150
|
+
:content => g.c.read
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
CodeGeneratorResponse.new(:file => file)
|
155
|
+
end
|
156
|
+
|
157
|
+
attr_reader :c
|
158
|
+
|
159
|
+
def initialize(c)
|
160
|
+
@c = c
|
161
|
+
@n = 0
|
162
|
+
end
|
163
|
+
|
164
|
+
def file!(file)
|
165
|
+
puts "## Generated from #{file.name} for #{file.package}"
|
166
|
+
|
167
|
+
file.message_type.each do |mt|
|
168
|
+
message!("", mt)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def indent(&blk)
|
173
|
+
@n += 1
|
174
|
+
blk.call
|
175
|
+
@n -= 1
|
176
|
+
end
|
177
|
+
|
178
|
+
def indent!(n)
|
179
|
+
@n = n
|
180
|
+
end
|
181
|
+
|
182
|
+
def message!(pkg, mt)
|
183
|
+
puts
|
184
|
+
puts "class #{mt.name}"
|
185
|
+
|
186
|
+
indent do
|
187
|
+
puts "include Beefcake::Message"
|
188
|
+
puts
|
189
|
+
|
190
|
+
Array(mt.enum_type).each do |et|
|
191
|
+
enum!(et)
|
192
|
+
end
|
193
|
+
|
194
|
+
## Generate Types
|
195
|
+
Array(mt.nested_type).each do |nt|
|
196
|
+
message!(pkg, nt)
|
197
|
+
end
|
198
|
+
puts
|
199
|
+
|
200
|
+
## Generate fields
|
201
|
+
Array(mt.field).each do |f|
|
202
|
+
field!(pkg, f)
|
203
|
+
end
|
204
|
+
puts
|
205
|
+
end
|
206
|
+
|
207
|
+
puts "end"
|
208
|
+
end
|
209
|
+
|
210
|
+
def enum!(et)
|
211
|
+
puts "module #{et.name}"
|
212
|
+
indent do
|
213
|
+
et.value.each do |v|
|
214
|
+
puts "%s = %d" % [v.name, v.number]
|
215
|
+
end
|
216
|
+
end
|
217
|
+
puts "end"
|
218
|
+
end
|
219
|
+
|
220
|
+
def field!(pkg, f)
|
221
|
+
# Turn the label into Ruby
|
222
|
+
label = name_for(f, L, f.label)
|
223
|
+
|
224
|
+
# Turn the name into a Ruby
|
225
|
+
name = ":#{f.name}"
|
226
|
+
|
227
|
+
# Determine the type-name and convert to Ruby
|
228
|
+
type = if f.type_name
|
229
|
+
# We have a type_name so we will use it after converting to a
|
230
|
+
# Ruby friendly version
|
231
|
+
t = f.type_name
|
232
|
+
t = t.gsub(pkg, "") # Remove the leading package name
|
233
|
+
t = t.gsub(/^\.*/, "") # Remove leading `.`s
|
234
|
+
|
235
|
+
t.gsub(".", "::") # Convert to Ruby namespacing syntax
|
236
|
+
else
|
237
|
+
":#{name_for(f, T, f.type)}"
|
238
|
+
end
|
239
|
+
|
240
|
+
# Finally, generate the declaration
|
241
|
+
out = "%s %s, %s, %d" % [label, name, type, f.number]
|
242
|
+
|
243
|
+
if f.default_value
|
244
|
+
out += ", :default => #{f.default_value}"
|
245
|
+
end
|
246
|
+
|
247
|
+
puts out
|
248
|
+
end
|
249
|
+
|
250
|
+
# Determines the name for a
|
251
|
+
def name_for(b, mod, val)
|
252
|
+
b.name_for(mod, val).to_s.gsub(/.*_/, "").downcase
|
253
|
+
end
|
254
|
+
|
255
|
+
def compile(ns, file)
|
256
|
+
puts "## Generated from #{file.name} for #{file.package}"
|
257
|
+
puts "require \"beefcake\""
|
258
|
+
puts
|
259
|
+
|
260
|
+
ns!(ns) do
|
261
|
+
file.message_type.each do |mt|
|
262
|
+
message!(file.package, mt)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def ns!(modules, &blk)
|
268
|
+
if modules.empty?
|
269
|
+
blk.call
|
270
|
+
else
|
271
|
+
puts "module #{modules.first}"
|
272
|
+
indent do
|
273
|
+
ns!(modules[1..-1], &blk)
|
274
|
+
end
|
275
|
+
puts "end"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def puts(msg=nil)
|
280
|
+
if msg
|
281
|
+
c.puts((" " * @n) + msg)
|
282
|
+
else
|
283
|
+
c.puts
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
end
|