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.
Files changed (41) hide show
  1. data/.document +3 -0
  2. data/.gitignore +11 -0
  3. data/LICENSE +22 -0
  4. data/README +82 -0
  5. data/Rakefile +56 -0
  6. data/VERSION +1 -0
  7. data/bin/ruby-protoc +55 -0
  8. data/debian/changelog +105 -0
  9. data/debian/compatability +1 -0
  10. data/debian/control +12 -0
  11. data/debian/libprotobuf-ruby1.8.install +7 -0
  12. data/debian/protocol_buffers.rb +3 -0
  13. data/debian/rules +13 -0
  14. data/examples/json_protobuf.rb +90 -0
  15. data/ext/extconf.disabled.rb +3 -0
  16. data/ext/varint.c +65 -0
  17. data/lib/protocol_buffers.rb +5 -0
  18. data/lib/protocol_buffers/compiler.rb +48 -0
  19. data/lib/protocol_buffers/compiler/descriptor.pb.rb +236 -0
  20. data/lib/protocol_buffers/compiler/descriptor.proto +393 -0
  21. data/lib/protocol_buffers/compiler/file_descriptor_to_d.rb +195 -0
  22. data/lib/protocol_buffers/compiler/file_descriptor_to_ruby.rb +173 -0
  23. data/lib/protocol_buffers/limited_io.rb +33 -0
  24. data/lib/protocol_buffers/runtime/decoder.rb +65 -0
  25. data/lib/protocol_buffers/runtime/encoder.rb +53 -0
  26. data/lib/protocol_buffers/runtime/enum.rb +4 -0
  27. data/lib/protocol_buffers/runtime/extend.rb +1 -0
  28. data/lib/protocol_buffers/runtime/field.rb +550 -0
  29. data/lib/protocol_buffers/runtime/message.rb +494 -0
  30. data/lib/protocol_buffers/runtime/service.rb +1 -0
  31. data/lib/protocol_buffers/runtime/varint.rb +62 -0
  32. data/spec/compiler_spec.rb +19 -0
  33. data/spec/fields_spec.rb +49 -0
  34. data/spec/proto_files/depends.proto +5 -0
  35. data/spec/proto_files/featureful.proto +54 -0
  36. data/spec/proto_files/no_package.proto +10 -0
  37. data/spec/proto_files/simple.proto +5 -0
  38. data/spec/runtime_spec.rb +430 -0
  39. data/spec/spec.opts +1 -0
  40. data/spec/spec_helper.rb +8 -0
  41. 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