ruby-protocol-buffers 0.8.4
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/.document +3 -0
- data/.gitignore +11 -0
- data/LICENSE +22 -0
- data/README +82 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/bin/ruby-protoc +55 -0
- data/debian/changelog +105 -0
- data/debian/compatability +1 -0
- data/debian/control +12 -0
- data/debian/libprotobuf-ruby1.8.install +7 -0
- data/debian/protocol_buffers.rb +3 -0
- data/debian/rules +13 -0
- data/examples/json_protobuf.rb +90 -0
- data/ext/extconf.disabled.rb +3 -0
- data/ext/varint.c +65 -0
- data/lib/protocol_buffers.rb +5 -0
- data/lib/protocol_buffers/compiler.rb +48 -0
- data/lib/protocol_buffers/compiler/descriptor.pb.rb +236 -0
- data/lib/protocol_buffers/compiler/descriptor.proto +393 -0
- data/lib/protocol_buffers/compiler/file_descriptor_to_d.rb +195 -0
- data/lib/protocol_buffers/compiler/file_descriptor_to_ruby.rb +173 -0
- data/lib/protocol_buffers/limited_io.rb +33 -0
- data/lib/protocol_buffers/runtime/decoder.rb +65 -0
- data/lib/protocol_buffers/runtime/encoder.rb +53 -0
- data/lib/protocol_buffers/runtime/enum.rb +4 -0
- data/lib/protocol_buffers/runtime/extend.rb +1 -0
- data/lib/protocol_buffers/runtime/field.rb +550 -0
- data/lib/protocol_buffers/runtime/message.rb +494 -0
- data/lib/protocol_buffers/runtime/service.rb +1 -0
- data/lib/protocol_buffers/runtime/varint.rb +62 -0
- data/spec/compiler_spec.rb +19 -0
- data/spec/fields_spec.rb +49 -0
- data/spec/proto_files/depends.proto +5 -0
- data/spec/proto_files/featureful.proto +54 -0
- data/spec/proto_files/no_package.proto +10 -0
- data/spec/proto_files/simple.proto +5 -0
- data/spec/runtime_spec.rb +430 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +8 -0
- metadata +128 -0
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'protocol_buffers/compiler/descriptor.pb'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class FileDescriptorToD < Struct.new(:descriptor)
|
5
|
+
|
6
|
+
include FieldDescriptorProto::Type
|
7
|
+
include FieldDescriptorProto::Label
|
8
|
+
|
9
|
+
def initialize(descriptor)
|
10
|
+
super
|
11
|
+
@module = descriptor.package_
|
12
|
+
@ns = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def module_name
|
16
|
+
@module
|
17
|
+
end
|
18
|
+
|
19
|
+
def class_name(klass)
|
20
|
+
klass
|
21
|
+
end
|
22
|
+
|
23
|
+
def write(io)
|
24
|
+
@io = io
|
25
|
+
|
26
|
+
@io.write <<HEADER
|
27
|
+
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
28
|
+
|
29
|
+
static import std.string;
|
30
|
+
import protocol_buffers.message;
|
31
|
+
|
32
|
+
HEADER
|
33
|
+
|
34
|
+
descriptor.message_type.each do |message|
|
35
|
+
dump_message(message)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def dump_message(message)
|
42
|
+
in_namespace("final class", class_name(message.name), " : Message") do
|
43
|
+
|
44
|
+
line %{static const #{class_name(message.name)} default_instance;}
|
45
|
+
line %{string typeName() { return "#{class_name(message.name)}"; }}
|
46
|
+
|
47
|
+
line %{// nested messages} unless message.nested_type.empty?
|
48
|
+
message.nested_type.each { |inner| dump_message(inner) }
|
49
|
+
|
50
|
+
line %{// nested enums} unless message.enum_type.empty?
|
51
|
+
message.enum_type.each { |inner| dump_enum(inner) }
|
52
|
+
|
53
|
+
singular_fields = message.field.find_all { |f|
|
54
|
+
f.label == LABEL_OPTIONAL || f.label == LABEL_REQUIRED }
|
55
|
+
repeated_fields = message.field.find_all { |f| f.label == LABEL_REPEATED }
|
56
|
+
|
57
|
+
line
|
58
|
+
|
59
|
+
singular_fields.each { |f| define_field(f, false) }
|
60
|
+
repeated_fields.each { |f| define_field(f, true) }
|
61
|
+
|
62
|
+
line %{enum __tags { #{message.field.map { |f| "#{f.name} = #{f.number}" }.join(", ")} }}
|
63
|
+
|
64
|
+
unless singular_fields.empty?
|
65
|
+
in_namespace("private", "") do
|
66
|
+
line "ubyte[(#{singular_fields.size}/32)+1] _tags;"
|
67
|
+
line %{void _set_bit(uint idx) { _tags[idx / 32] |= (1u << (idx % 32)); }}
|
68
|
+
line %{void _clear_bit(uint idx) { _tags[idx / 32] &= ~(1u << (idx % 32)); }}
|
69
|
+
line %{bool _check_bit(uint idx) { return (_tags[idx / 32] & (1u << (idx % 32))) != 0; }}
|
70
|
+
end
|
71
|
+
line %{enum __bits { #{m=0; singular_fields.map { |f| "#{f.name} = #{m+=1}" }.join(", ")} }} unless singular_fields.empty?
|
72
|
+
end
|
73
|
+
|
74
|
+
in_namespace("void clear()", "") do
|
75
|
+
message.field.each { |f| line %{clear_#{f.name}();} }
|
76
|
+
end
|
77
|
+
|
78
|
+
in_namespace("bool mergeFromString(string s)", "") do
|
79
|
+
body = <<-BODY
|
80
|
+
uint i;
|
81
|
+
while(i < s.length) {
|
82
|
+
long nextTag = decodeVarint(s[i++]);
|
83
|
+
long tag = nextTag >> 3;
|
84
|
+
uint wire_type = nextTag & 0b111;
|
85
|
+
switch(tag) {
|
86
|
+
}
|
87
|
+
}
|
88
|
+
return true;
|
89
|
+
BODY
|
90
|
+
|
91
|
+
body.each_line { |l| line(l.sub(" ", '').chomp) }
|
92
|
+
end
|
93
|
+
|
94
|
+
line
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def field_typename(field)
|
99
|
+
(TYPE_MAPPING[field.type] || field.type_name).sub(/^.#{module_name}/, '')
|
100
|
+
end
|
101
|
+
|
102
|
+
def define_field(field, repeated)
|
103
|
+
typename = field_typename(field)
|
104
|
+
if repeated
|
105
|
+
line "private #{typename}[] _#{field.name};"
|
106
|
+
line "#{typename}[] #{field.name}() { return _#{field.name}; }"
|
107
|
+
else
|
108
|
+
line "private #{typename} _#{field.name};"
|
109
|
+
line "#{typename} #{field.name}() { return _#{field.name}; }"
|
110
|
+
end
|
111
|
+
|
112
|
+
if !repeated
|
113
|
+
line "static const #{typename} default_#{field.name} = #{default_value(field)};"
|
114
|
+
line "bool has_#{field.name}() { return _check_bit(__bits.#{field.name}); }"
|
115
|
+
line "void #{field.name}(#{typename} val) { _set_bit(__bits.#{field.name}); _#{field.name} = val; }"
|
116
|
+
end
|
117
|
+
|
118
|
+
if repeated
|
119
|
+
line "void clear_#{field.name}() { _#{field.name} = []; }"
|
120
|
+
else
|
121
|
+
line "void clear_#{field.name}() { _clear_bit(__bits.#{field.name}); _#{field.name} = default_#{field.name}; }"
|
122
|
+
end
|
123
|
+
line
|
124
|
+
end
|
125
|
+
|
126
|
+
def dump_enum(enum)
|
127
|
+
in_namespace("enum", enum.name) do
|
128
|
+
enum.value.each do |value|
|
129
|
+
line %{#{value.name} = #{value.number},}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
TYPE_MAPPING = {
|
135
|
+
TYPE_DOUBLE => "double",
|
136
|
+
TYPE_FLOAT => "float",
|
137
|
+
TYPE_INT64 => "long",
|
138
|
+
TYPE_UINT64 => "ulong",
|
139
|
+
TYPE_INT32 => "int",
|
140
|
+
TYPE_FIXED64 => "long",
|
141
|
+
TYPE_FIXED32 => "int",
|
142
|
+
TYPE_BOOL => "bool",
|
143
|
+
TYPE_STRING => "string",
|
144
|
+
TYPE_BYTES => "ubyte[]",
|
145
|
+
TYPE_UINT32 => "uint",
|
146
|
+
TYPE_SFIXED32 => "int",
|
147
|
+
TYPE_SFIXED64 => "long",
|
148
|
+
TYPE_SINT32 => "int",
|
149
|
+
TYPE_SINT64 => "long",
|
150
|
+
}
|
151
|
+
|
152
|
+
def default_value(field)
|
153
|
+
typename = field_typename(field)
|
154
|
+
|
155
|
+
if field.default_value && field.default_value != ""
|
156
|
+
case field.type
|
157
|
+
when TYPE_STRING, TYPE_BYTES
|
158
|
+
%{"#{field.default_value}"}
|
159
|
+
when TYPE_BOOL
|
160
|
+
field.default_value || "false"
|
161
|
+
when TYPE_ENUM
|
162
|
+
%{#{typename}.#{field.default_value}}
|
163
|
+
else
|
164
|
+
field.default_value || "0"
|
165
|
+
end
|
166
|
+
else
|
167
|
+
%{(#{typename}).init}
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def in_namespace(type, name, rest = "")
|
172
|
+
if !name
|
173
|
+
yield
|
174
|
+
else
|
175
|
+
line "#{type} #{capfirst(name)}#{rest} {"
|
176
|
+
@ns.push name
|
177
|
+
yield
|
178
|
+
@ns.pop
|
179
|
+
line "}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def capfirst(s)
|
184
|
+
"#{s[0,1].capitalize}#{s[1..-1]}" if s
|
185
|
+
end
|
186
|
+
|
187
|
+
def line(str = nil)
|
188
|
+
if str
|
189
|
+
@ns.size.times { @io.write(" ") }
|
190
|
+
@io.write(str)
|
191
|
+
end
|
192
|
+
@io.write("\n")
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'protocol_buffers/compiler/descriptor.pb'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class FileDescriptorToRuby < Struct.new(:descriptor)
|
5
|
+
|
6
|
+
include FieldDescriptorProto::Type
|
7
|
+
include FieldDescriptorProto::Label
|
8
|
+
|
9
|
+
def initialize(descriptor)
|
10
|
+
super
|
11
|
+
@package = capfirst(descriptor.package_)
|
12
|
+
@ns = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def write(io)
|
16
|
+
@io = io
|
17
|
+
|
18
|
+
@io.write <<HEADER
|
19
|
+
#!/usr/bin/env ruby
|
20
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
21
|
+
|
22
|
+
require 'protocol_buffers'
|
23
|
+
|
24
|
+
HEADER
|
25
|
+
descriptor.dependency.each do |dep|
|
26
|
+
path = File.basename(dep, ".proto") + ".pb"
|
27
|
+
@io.write("begin; require '#{path}'; rescue LoadError; end\n")
|
28
|
+
end
|
29
|
+
@io.write("\n") unless descriptor.dependency.empty?
|
30
|
+
|
31
|
+
# in_namespace correctly handles the case where @package.nil?
|
32
|
+
unless @package.empty?
|
33
|
+
@io.write("# Reload support\nObject.__send__(:remove_const, :#{@package}) if defined?(#{@package})\n\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
in_namespace("module", @package) do
|
37
|
+
declare(descriptor.message_type, descriptor.enum_type)
|
38
|
+
|
39
|
+
descriptor.message_type.each do |message|
|
40
|
+
dump_message(message)
|
41
|
+
end
|
42
|
+
|
43
|
+
descriptor.enum_type.each do |enum|
|
44
|
+
dump_enum(enum)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def declare(messages, enums)
|
53
|
+
return if messages.empty? && enums.empty?
|
54
|
+
|
55
|
+
line %{# forward declarations}
|
56
|
+
messages.each do |message|
|
57
|
+
line %{class #{name([@package, message.name])} < ::ProtocolBuffers::Message; end}
|
58
|
+
end
|
59
|
+
enums.each do |enum|
|
60
|
+
line %{module #{name([@package, enum.name])}; end}
|
61
|
+
end
|
62
|
+
line
|
63
|
+
end
|
64
|
+
|
65
|
+
def line(str = nil)
|
66
|
+
if str
|
67
|
+
@ns.size.times { @io.write(" ") }
|
68
|
+
@io.write(str)
|
69
|
+
end
|
70
|
+
@io.write("\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
def in_namespace(type, name, rest = "")
|
74
|
+
if !name || name == ""
|
75
|
+
yield
|
76
|
+
else
|
77
|
+
line "#{type} #{capfirst(name)}#{rest}"
|
78
|
+
@ns.push name
|
79
|
+
yield
|
80
|
+
@ns.pop
|
81
|
+
line "end"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def name(parts)
|
86
|
+
ns = @ns.dup
|
87
|
+
(parts.shift; ns.shift) while !parts.empty? && parts.first == ns.first
|
88
|
+
parts.map { |p| capfirst(p) }.join("::")
|
89
|
+
end
|
90
|
+
|
91
|
+
LABEL_MAPPING = {
|
92
|
+
LABEL_OPTIONAL => "optional",
|
93
|
+
LABEL_REQUIRED => "required",
|
94
|
+
LABEL_REPEATED => "repeated",
|
95
|
+
}
|
96
|
+
|
97
|
+
TYPE_MAPPING = {
|
98
|
+
TYPE_DOUBLE => ":double",
|
99
|
+
TYPE_FLOAT => ":float",
|
100
|
+
TYPE_INT64 => ":int64",
|
101
|
+
TYPE_UINT64 => ":uint64",
|
102
|
+
TYPE_INT32 => ":int32",
|
103
|
+
TYPE_FIXED64 => ":fixed64",
|
104
|
+
TYPE_FIXED32 => ":fixed32",
|
105
|
+
TYPE_BOOL => ":bool",
|
106
|
+
TYPE_STRING => ":string",
|
107
|
+
TYPE_BYTES => ":bytes",
|
108
|
+
TYPE_UINT32 => ":uint32",
|
109
|
+
TYPE_SFIXED32 => ":sfixed32",
|
110
|
+
TYPE_SFIXED64 => ":sfixed64",
|
111
|
+
TYPE_SINT32 => ":sint32",
|
112
|
+
TYPE_SINT64 => ":sint64",
|
113
|
+
}
|
114
|
+
|
115
|
+
def dump_message(message)
|
116
|
+
in_namespace("class", message.name, " < ::ProtocolBuffers::Message") do
|
117
|
+
declare(message.nested_type, message.enum_type)
|
118
|
+
|
119
|
+
line %{# nested messages} unless message.nested_type.empty?
|
120
|
+
message.nested_type.each { |inner| dump_message(inner) }
|
121
|
+
|
122
|
+
line %{# nested enums} unless message.enum_type.empty?
|
123
|
+
message.enum_type.each { |inner| dump_enum(inner) }
|
124
|
+
|
125
|
+
message.field.each do |field|
|
126
|
+
typename = field_typename(field)
|
127
|
+
fieldline = %{#{LABEL_MAPPING[field.label]} #{typename}, :#{field.name}, #{field.number}}
|
128
|
+
if field.default_value && field.default_value != ""
|
129
|
+
fieldline << %{, :default => #{default_value(field)}}
|
130
|
+
end
|
131
|
+
line fieldline
|
132
|
+
end
|
133
|
+
|
134
|
+
line
|
135
|
+
line "gen_methods! # new fields ignored after this point"
|
136
|
+
end
|
137
|
+
line
|
138
|
+
end
|
139
|
+
|
140
|
+
def dump_enum(enum)
|
141
|
+
in_namespace("module", enum.name) do
|
142
|
+
line %{include ::ProtocolBuffers::Enum}
|
143
|
+
enum.value.each do |value|
|
144
|
+
line %{#{capfirst(value.name)} = #{value.number}}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
line
|
148
|
+
end
|
149
|
+
|
150
|
+
def field_typename(field)
|
151
|
+
TYPE_MAPPING[field.type] || field.type_name.split(".").map { |t| capfirst(t) }.join("::")
|
152
|
+
end
|
153
|
+
|
154
|
+
# TODO: this probably doesn't work for all default values, expand
|
155
|
+
def default_value(field)
|
156
|
+
case field.type
|
157
|
+
when TYPE_STRING, TYPE_BYTES
|
158
|
+
%{"#{field.default_value}"}
|
159
|
+
when TYPE_BOOL
|
160
|
+
field.default_value
|
161
|
+
when TYPE_ENUM
|
162
|
+
typename = field_typename(field)
|
163
|
+
%{#{typename}::#{field.default_value}}
|
164
|
+
else
|
165
|
+
field.default_value
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def capfirst(s)
|
170
|
+
"#{s[0,1].capitalize}#{s[1..-1]}" if s
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class LimitedIO < Struct.new(:parent, :limit)
|
2
|
+
|
3
|
+
def read(length = nil, buffer = nil)
|
4
|
+
length = length || limit
|
5
|
+
length = limit if length > limit
|
6
|
+
self.limit -= length
|
7
|
+
# seems silly to check for buffer, but some IO#read methods implemented in C
|
8
|
+
# barf if you pass nil, rather than treat it as an argument that wasn't
|
9
|
+
# passed at all.
|
10
|
+
if buffer
|
11
|
+
parent.read(length, buffer)
|
12
|
+
else
|
13
|
+
parent.read(length)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def eof?
|
18
|
+
limit == 0 || parent.eof?
|
19
|
+
end
|
20
|
+
|
21
|
+
def getbyte
|
22
|
+
return nil if limit == 0
|
23
|
+
self.limit -= 1
|
24
|
+
parent.getbyte
|
25
|
+
end
|
26
|
+
|
27
|
+
def getc
|
28
|
+
return nil if limit == 0
|
29
|
+
self.limit -= 1
|
30
|
+
parent.getc
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'protocol_buffers/limited_io'
|
2
|
+
|
3
|
+
module ProtocolBuffers
|
4
|
+
|
5
|
+
class DecodeError < StandardError; end
|
6
|
+
|
7
|
+
module Decoder # :nodoc: all
|
8
|
+
def self.decode(io, message)
|
9
|
+
fields = message.fields
|
10
|
+
|
11
|
+
until io.eof?
|
12
|
+
tag_int = Varint.decode(io)
|
13
|
+
tag = tag_int >> 3
|
14
|
+
wire_type = tag_int & 0b111
|
15
|
+
field = fields[tag]
|
16
|
+
|
17
|
+
if field && wire_type != field.wire_type
|
18
|
+
raise(DecodeError, "incorrect wire type for tag: #{field.tag}")
|
19
|
+
end
|
20
|
+
|
21
|
+
# replacing const lookups with hard-coded ints removed an entire 10%
|
22
|
+
# from an earlier decoding benchmark. these values can't change without
|
23
|
+
# breaking the protocol anyway, so we decided it was worth it.
|
24
|
+
case wire_type
|
25
|
+
when 0 # VARINT
|
26
|
+
value = Varint.decode(io)
|
27
|
+
when 1 # FIXED64
|
28
|
+
value = io.read(8)
|
29
|
+
when 2 # LENGTH_DELIMITED
|
30
|
+
length = Varint.decode(io)
|
31
|
+
value = LimitedIO.new(io, length)
|
32
|
+
when 5 # FIXED32
|
33
|
+
value = io.read(4)
|
34
|
+
when 3, 4 # deprecated START_GROUP/END_GROUP types
|
35
|
+
raise(DecodeError, "groups are deprecated and unsupported")
|
36
|
+
else
|
37
|
+
raise(DecodeError, "unknown wire type: #{wire_type}")
|
38
|
+
end
|
39
|
+
|
40
|
+
if field
|
41
|
+
deserialized = field.deserialize(value)
|
42
|
+
# merge_field handles repeated field logic
|
43
|
+
message.merge_field(tag, deserialized, field)
|
44
|
+
else
|
45
|
+
# ignore unknown fields, pass them on when re-serializing this message
|
46
|
+
|
47
|
+
# special handling -- if it's a LENGTH_DELIMITED field, we need to
|
48
|
+
# actually read the IO so that extra bytes aren't left on the wire
|
49
|
+
value = value.read if wire_type == 2 # LENGTH_DELIMITED
|
50
|
+
|
51
|
+
message.remember_unknown_field(tag_int, value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
unless message.valid?
|
56
|
+
raise(DecodeError, "invalid message")
|
57
|
+
end
|
58
|
+
|
59
|
+
return message
|
60
|
+
rescue TypeError, ArgumentError
|
61
|
+
raise(DecodeError, "error parsing message")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|