protostar 0.1.0.beta1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +60 -0
- data/LICENSE.txt +21 -0
- data/README.md +647 -0
- data/THIRD_PARTY_NOTICES.md +61 -0
- data/exe/protoc-gen-protostar +7 -0
- data/exe/protostar +7 -0
- data/lib/protostar/builder/generator.rb +71 -0
- data/lib/protostar/builder/sink.rb +35 -0
- data/lib/protostar/builder.rb +965 -0
- data/lib/protostar/cli.rb +267 -0
- data/lib/protostar/codec/decode_generator.rb +199 -0
- data/lib/protostar/codec/decoder.rb +296 -0
- data/lib/protostar/codec/encode_generator.rb +224 -0
- data/lib/protostar/codec/encoder.rb +121 -0
- data/lib/protostar/codec/sizer.rb +91 -0
- data/lib/protostar/codec.rb +15 -0
- data/lib/protostar/compiler/ast.rb +193 -0
- data/lib/protostar/compiler/emitters/descriptor_set.rb +57 -0
- data/lib/protostar/compiler/emitters/google_ruby.rb +103 -0
- data/lib/protostar/compiler/emitters/grpc.rb +175 -0
- data/lib/protostar/compiler/emitters/protostar_rbs.rb +252 -0
- data/lib/protostar/compiler/emitters/rbs.rb +394 -0
- data/lib/protostar/compiler/emitters/ruby.rb +503 -0
- data/lib/protostar/compiler/emitters.rb +268 -0
- data/lib/protostar/compiler/include/LICENSE +32 -0
- data/lib/protostar/compiler/include/google/protobuf/any.proto +106 -0
- data/lib/protostar/compiler/include/google/protobuf/api.proto +229 -0
- data/lib/protostar/compiler/include/google/protobuf/compiler/plugin.proto +180 -0
- data/lib/protostar/compiler/include/google/protobuf/cpp_features.proto +87 -0
- data/lib/protostar/compiler/include/google/protobuf/descriptor.proto +1532 -0
- data/lib/protostar/compiler/include/google/protobuf/duration.proto +115 -0
- data/lib/protostar/compiler/include/google/protobuf/empty.proto +51 -0
- data/lib/protostar/compiler/include/google/protobuf/field_mask.proto +243 -0
- data/lib/protostar/compiler/include/google/protobuf/java_features.proto +132 -0
- data/lib/protostar/compiler/include/google/protobuf/source_context.proto +48 -0
- data/lib/protostar/compiler/include/google/protobuf/struct.proto +111 -0
- data/lib/protostar/compiler/include/google/protobuf/timestamp.proto +145 -0
- data/lib/protostar/compiler/include/google/protobuf/type.proto +217 -0
- data/lib/protostar/compiler/include/google/protobuf/wrappers.proto +157 -0
- data/lib/protostar/compiler/lexer.rb +659 -0
- data/lib/protostar/compiler/linker.rb +894 -0
- data/lib/protostar/compiler/option_interpreter.rb +363 -0
- data/lib/protostar/compiler/parser.rb +1696 -0
- data/lib/protostar/compiler/retention.rb +104 -0
- data/lib/protostar/compiler/run.rb +184 -0
- data/lib/protostar/compiler/source_info.rb +161 -0
- data/lib/protostar/compiler/symbol_table.rb +223 -0
- data/lib/protostar/compiler/text_format.rb +340 -0
- data/lib/protostar/compiler/validator.rb +944 -0
- data/lib/protostar/compiler.rb +304 -0
- data/lib/protostar/diagnostics/codes.rb +1307 -0
- data/lib/protostar/diagnostics/formats.rb +64 -0
- data/lib/protostar/diagnostics/renderer.rb +240 -0
- data/lib/protostar/diagnostics/source.rb +153 -0
- data/lib/protostar/diagnostics.rb +117 -0
- data/lib/protostar/dsl.rb +367 -0
- data/lib/protostar/enum.rb +70 -0
- data/lib/protostar/errors.rb +23 -0
- data/lib/protostar/google/protobuf/any_pb.rb +90 -0
- data/lib/protostar/google/protobuf/api_pb.rb +199 -0
- data/lib/protostar/google/protobuf/compiler/plugin_pb.rb +168 -0
- data/lib/protostar/google/protobuf/descriptor_pb.rb +1239 -0
- data/lib/protostar/google/protobuf/duration_pb.rb +99 -0
- data/lib/protostar/google/protobuf/empty_pb.rb +36 -0
- data/lib/protostar/google/protobuf/field_mask_pb.rb +228 -0
- data/lib/protostar/google/protobuf/source_context_pb.rb +33 -0
- data/lib/protostar/google/protobuf/struct_pb.rb +91 -0
- data/lib/protostar/google/protobuf/timestamp_pb.rb +129 -0
- data/lib/protostar/google/protobuf/type_pb.rb +202 -0
- data/lib/protostar/google/protobuf/wrappers_pb.rb +125 -0
- data/lib/protostar/google_protobuf_shim.rb +24 -0
- data/lib/protostar/json/encoder.rb +340 -0
- data/lib/protostar/json/errors.rb +34 -0
- data/lib/protostar/json/parser.rb +437 -0
- data/lib/protostar/json/path.rb +39 -0
- data/lib/protostar/json.rb +95 -0
- data/lib/protostar/lint/command.rb +197 -0
- data/lib/protostar/lint/config.rb +98 -0
- data/lib/protostar/lint/context.rb +126 -0
- data/lib/protostar/lint/directives.rb +56 -0
- data/lib/protostar/lint/fixer.rb +165 -0
- data/lib/protostar/lint/naming.rb +54 -0
- data/lib/protostar/lint/rule.rb +120 -0
- data/lib/protostar/lint/rules/comment_enum.rb +31 -0
- data/lib/protostar/lint/rules/comment_enum_value.rb +32 -0
- data/lib/protostar/lint/rules/comment_field.rb +33 -0
- data/lib/protostar/lint/rules/comment_message.rb +31 -0
- data/lib/protostar/lint/rules/comment_rpc.rb +31 -0
- data/lib/protostar/lint/rules/comment_service.rb +31 -0
- data/lib/protostar/lint/rules/edition_recommended.rb +31 -0
- data/lib/protostar/lint/rules/enum_first_value_zero.rb +34 -0
- data/lib/protostar/lint/rules/enum_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/enum_value_prefix.rb +38 -0
- data/lib/protostar/lint/rules/enum_value_upper_snake_case.rb +28 -0
- data/lib/protostar/lint/rules/enum_zero_value_suffix.rb +41 -0
- data/lib/protostar/lint/rules/field_lower_snake_case.rb +34 -0
- data/lib/protostar/lint/rules/field_not_required.rb +39 -0
- data/lib/protostar/lint/rules/field_number_reuse_risk.rb +45 -0
- data/lib/protostar/lint/rules/field_numbers_hot_range.rb +44 -0
- data/lib/protostar/lint/rules/file_lower_snake_case.rb +27 -0
- data/lib/protostar/lint/rules/import_no_public.rb +29 -0
- data/lib/protostar/lint/rules/import_no_weak.rb +28 -0
- data/lib/protostar/lint/rules/import_used.rb +33 -0
- data/lib/protostar/lint/rules/message_pascal_case.rb +27 -0
- data/lib/protostar/lint/rules/no_deprecated_features.rb +50 -0
- data/lib/protostar/lint/rules/no_groups.rb +34 -0
- data/lib/protostar/lint/rules/oneof_lower_snake_case.rb +27 -0
- data/lib/protostar/lint/rules/package_defined.rb +28 -0
- data/lib/protostar/lint/rules/package_directory_match.rb +33 -0
- data/lib/protostar/lint/rules/package_lower_snake_case.rb +28 -0
- data/lib/protostar/lint/rules/package_same_directory.rb +39 -0
- data/lib/protostar/lint/rules/package_version_suffix.rb +31 -0
- data/lib/protostar/lint/rules/reserved_names_with_numbers.rb +44 -0
- data/lib/protostar/lint/rules/rpc_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/rpc_request_response_unique.rb +40 -0
- data/lib/protostar/lint/rules/rpc_request_standard_name.rb +32 -0
- data/lib/protostar/lint/rules/rpc_response_standard_name.rb +31 -0
- data/lib/protostar/lint/rules/service_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/service_suffix.rb +28 -0
- data/lib/protostar/lint/rules/syntax_specified.rb +35 -0
- data/lib/protostar/lint/runner.rb +135 -0
- data/lib/protostar/lint.rb +122 -0
- data/lib/protostar/message/accessors.rb +86 -0
- data/lib/protostar/message/coercion.rb +171 -0
- data/lib/protostar/message/field_info.rb +145 -0
- data/lib/protostar/message/reflection.rb +149 -0
- data/lib/protostar/message.rb +572 -0
- data/lib/protostar/plugin.rb +94 -0
- data/lib/protostar/schema/default_value.rb +113 -0
- data/lib/protostar/schema/descriptor.rb +41 -0
- data/lib/protostar/schema/draft/copy.rb +88 -0
- data/lib/protostar/schema/draft/resolution.rb +177 -0
- data/lib/protostar/schema/draft.rb +779 -0
- data/lib/protostar/schema/enum.rb +106 -0
- data/lib/protostar/schema/extension_range.rb +37 -0
- data/lib/protostar/schema/features.rb +153 -0
- data/lib/protostar/schema/field.rb +201 -0
- data/lib/protostar/schema/file.rb +217 -0
- data/lib/protostar/schema/freezer.rb +61 -0
- data/lib/protostar/schema/message.rb +104 -0
- data/lib/protostar/schema/oneof.rb +38 -0
- data/lib/protostar/schema/options.rb +367 -0
- data/lib/protostar/schema/pool.rb +179 -0
- data/lib/protostar/schema/proto.rb +576 -0
- data/lib/protostar/schema/resolver.rb +118 -0
- data/lib/protostar/schema/service.rb +89 -0
- data/lib/protostar/schema/source_info.rb +53 -0
- data/lib/protostar/schema.rb +65 -0
- data/lib/protostar/short_names/any.rb +7 -0
- data/lib/protostar/short_names/duration.rb +7 -0
- data/lib/protostar/short_names/empty.rb +7 -0
- data/lib/protostar/short_names/field_mask.rb +7 -0
- data/lib/protostar/short_names/struct.rb +9 -0
- data/lib/protostar/short_names/timestamp.rb +7 -0
- data/lib/protostar/short_names/wrappers.rb +15 -0
- data/lib/protostar/text/extensions.rb +114 -0
- data/lib/protostar/text/parser.rb +439 -0
- data/lib/protostar/text/printer.rb +304 -0
- data/lib/protostar/text/tokenizer.rb +116 -0
- data/lib/protostar/text.rb +67 -0
- data/lib/protostar/types.rb +152 -0
- data/lib/protostar/unknown_field.rb +58 -0
- data/lib/protostar/version.rb +5 -0
- data/lib/protostar/view/generator.rb +298 -0
- data/lib/protostar/view/map.rb +194 -0
- data/lib/protostar/view/repeated.rb +143 -0
- data/lib/protostar/view/scan.rb +183 -0
- data/lib/protostar/view.rb +283 -0
- data/lib/protostar/well_known/any.rb +52 -0
- data/lib/protostar/well_known/duration.rb +53 -0
- data/lib/protostar/well_known/list_value.rb +63 -0
- data/lib/protostar/well_known/struct.rb +55 -0
- data/lib/protostar/well_known/timestamp.rb +64 -0
- data/lib/protostar/well_known/value.rb +60 -0
- data/lib/protostar/well_known.rb +38 -0
- data/lib/protostar/wire/io_source.rb +40 -0
- data/lib/protostar/wire/reader.rb +254 -0
- data/lib/protostar/wire/writer.rb +201 -0
- data/lib/protostar/wire.rb +96 -0
- data/lib/protostar.rb +95 -0
- data/shims/google-protobuf/lib/google/protobuf/any_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/api_pb.rb +22 -0
- data/shims/google-protobuf/lib/google/protobuf/compiler/plugin_pb.rb +25 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptor_pb.rb +72 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptor_pool.rb +164 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptors.rb +553 -0
- data/shims/google-protobuf/lib/google/protobuf/duration_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/empty_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/field_mask_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/convert.rb +180 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/extensions.rb +154 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/json.rb +104 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/well_known.rb +140 -0
- data/shims/google-protobuf/lib/google/protobuf/map.rb +216 -0
- data/shims/google-protobuf/lib/google/protobuf/message.rb +544 -0
- data/shims/google-protobuf/lib/google/protobuf/message_exts.rb +41 -0
- data/shims/google-protobuf/lib/google/protobuf/plugin_pb.rb +4 -0
- data/shims/google-protobuf/lib/google/protobuf/repeated_field.rb +366 -0
- data/shims/google-protobuf/lib/google/protobuf/source_context_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/struct_pb.rb +20 -0
- data/shims/google-protobuf/lib/google/protobuf/timestamp_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/type_pb.rb +27 -0
- data/shims/google-protobuf/lib/google/protobuf/well_known_types.rb +37 -0
- data/shims/google-protobuf/lib/google/protobuf/wrappers_pb.rb +25 -0
- data/shims/google-protobuf/lib/google/protobuf.rb +107 -0
- data/shims/google-protobuf/lib/google/protobuf_ffi.rb +5 -0
- data/shims/google-protobuf/lib/google/protobuf_native.rb +5 -0
- data/sig/protostar/google/protobuf/any_pb.rbs +77 -0
- data/sig/protostar/google/protobuf/api_pb.rbs +242 -0
- data/sig/protostar/google/protobuf/compiler/plugin_pb.rbs +264 -0
- data/sig/protostar/google/protobuf/descriptor_pb.rbs +2220 -0
- data/sig/protostar/google/protobuf/duration_pb.rbs +82 -0
- data/sig/protostar/google/protobuf/empty_pb.rbs +18 -0
- data/sig/protostar/google/protobuf/field_mask_pb.rbs +170 -0
- data/sig/protostar/google/protobuf/source_context_pb.rbs +24 -0
- data/sig/protostar/google/protobuf/struct_pb.rbs +135 -0
- data/sig/protostar/google/protobuf/timestamp_pb.rbs +102 -0
- data/sig/protostar/google/protobuf/type_pb.rbs +304 -0
- data/sig/protostar/google/protobuf/wrappers_pb.rbs +169 -0
- data/sig/protostar/lint.rbs +216 -0
- data/sig/protostar.rbs +326 -0
- metadata +366 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
module Codec
|
|
5
|
+
# Decodes the binary wire format into message objects, eagerly.
|
|
6
|
+
# @api private
|
|
7
|
+
module Decoder
|
|
8
|
+
RECURSION_LIMIT = 100
|
|
9
|
+
|
|
10
|
+
F = Message::FieldInfo
|
|
11
|
+
UTF_8 = Encoding::UTF_8
|
|
12
|
+
BINARY = Encoding::BINARY
|
|
13
|
+
# unpack directive of a packed run of fixed-width values
|
|
14
|
+
PACKED = F::FIXED.transform_values { |(_, d)| "#{d}*".freeze }.freeze
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# @return [Message]
|
|
19
|
+
def decode(klass, bytes, recursion_limit: RECURSION_LIMIT, frozen: false)
|
|
20
|
+
r = Wire::Reader.new(bytes)
|
|
21
|
+
msg = klass.__empty
|
|
22
|
+
msg.__decode_fields(r, recursion_limit, 0, frozen)
|
|
23
|
+
msg.freeze if frozen
|
|
24
|
+
msg
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [Message, nil]
|
|
28
|
+
def decode_delimited(klass, source, **options)
|
|
29
|
+
if source.is_a?(String) || (defined?(IO::Buffer) && source.is_a?(IO::Buffer))
|
|
30
|
+
r = Wire::Reader.new(source)
|
|
31
|
+
length = r.read_varint
|
|
32
|
+
r.skip(length)
|
|
33
|
+
return decode(klass, r.buffer.byteslice(r.pos - length, length), **options)
|
|
34
|
+
end
|
|
35
|
+
length = read_io_varint(source)
|
|
36
|
+
return nil if length.nil?
|
|
37
|
+
data = length.zero? ? +"" : source.read(length)
|
|
38
|
+
if data.nil? || data.bytesize < length
|
|
39
|
+
raise DecodeError, "truncated delimited message: expected #{length} bytes, got #{data ? data.bytesize : 0}"
|
|
40
|
+
end
|
|
41
|
+
decode(klass, data, **options)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# A varint from an IO, byte by byte; nil at a clean end of input
|
|
45
|
+
def read_io_varint(io)
|
|
46
|
+
result = 0
|
|
47
|
+
shift = 0
|
|
48
|
+
loop do
|
|
49
|
+
byte = io.getbyte
|
|
50
|
+
if byte.nil?
|
|
51
|
+
return nil if shift == 0
|
|
52
|
+
raise DecodeError, "truncated length prefix"
|
|
53
|
+
end
|
|
54
|
+
result |= (byte & 0x7F) << shift
|
|
55
|
+
return result & Wire::MASK64 if byte < 0x80
|
|
56
|
+
shift += 7
|
|
57
|
+
raise DecodeError, "length prefix longer than 10 bytes" if shift >= 70
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Decodes one field whose tag was just read from offset `at`. The
|
|
62
|
+
# decoders generated for each class ({DecodeGenerator}) handle a class's
|
|
63
|
+
# own fields inline and come here for everything else.
|
|
64
|
+
# @return [Boolean] true when the tag ends the group being read
|
|
65
|
+
def field(msg, r, tag, at, depth, end_group, frozen)
|
|
66
|
+
number = tag >> 3
|
|
67
|
+
wt = tag & 7
|
|
68
|
+
if wt == Wire::END_GROUP
|
|
69
|
+
return true if number == end_group && end_group != 0
|
|
70
|
+
raise DecodeError.new("unmatched end of group #{number}", offset: at)
|
|
71
|
+
end
|
|
72
|
+
Wire::Reader.check_tag(tag, at)
|
|
73
|
+
info = msg.class.__by_number[number]
|
|
74
|
+
values = msg.__values
|
|
75
|
+
if info.nil?
|
|
76
|
+
unknown(msg, r, number, wt, depth)
|
|
77
|
+
elsif wt == info.wire_type
|
|
78
|
+
if info.map
|
|
79
|
+
map_entry(msg, values, info, r, depth, frozen)
|
|
80
|
+
elsif info.message?
|
|
81
|
+
if info.repeated
|
|
82
|
+
repeated_message(values, info, r, depth, frozen, at)
|
|
83
|
+
else
|
|
84
|
+
singular_message(values, info, r, depth, frozen, at)
|
|
85
|
+
end
|
|
86
|
+
elsif info.kind == F::ENUM
|
|
87
|
+
enum(msg, values, info, r)
|
|
88
|
+
elsif info.repeated
|
|
89
|
+
(values[info.index] ||= []) << scalar(r, info)
|
|
90
|
+
else
|
|
91
|
+
siblings = info.siblings
|
|
92
|
+
siblings&.each { |s| values[s] = nil }
|
|
93
|
+
values[info.index] = scalar(r, info)
|
|
94
|
+
end
|
|
95
|
+
elsif wt == Wire::LENGTH_DELIMITED && info.packable
|
|
96
|
+
packed(msg, values, info, r)
|
|
97
|
+
else
|
|
98
|
+
# a wire type that does not match the field makes it unknown, as in protoc
|
|
99
|
+
unknown(msg, r, number, wt, depth)
|
|
100
|
+
end
|
|
101
|
+
false
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Checks the end of a message's fields: a group must have seen its end tag
|
|
105
|
+
def finish(msg, r, end_group)
|
|
106
|
+
raise DecodeError.new("missing end of group #{end_group}", offset: r.pos) if end_group != 0
|
|
107
|
+
msg.__seal_unknown
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def repeated_message(values, info, r, depth, frozen, at)
|
|
111
|
+
sub = info.message_class.__empty
|
|
112
|
+
nested(sub, info, r, depth, frozen, at)
|
|
113
|
+
(values[info.index] ||= []) << sub
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def singular_message(values, info, r, depth, frozen, at)
|
|
117
|
+
i = info.index
|
|
118
|
+
sub = values[i]
|
|
119
|
+
if sub.nil?
|
|
120
|
+
siblings = info.siblings
|
|
121
|
+
siblings&.each { |s| values[s] = nil }
|
|
122
|
+
sub = values[i] = info.message_class.__empty
|
|
123
|
+
elsif sub.frozen?
|
|
124
|
+
sub = values[i] = sub.dup
|
|
125
|
+
end
|
|
126
|
+
nested(sub, info, r, depth, frozen, at)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Reads a message or group value into `sub`
|
|
130
|
+
def nested(sub, info, r, depth, frozen, at)
|
|
131
|
+
raise DecodeError.new("recursion limit exceeded", offset: at) if depth <= 0
|
|
132
|
+
if info.kind == F::GROUP
|
|
133
|
+
sub.__decode_fields(r, depth - 1, info.number, frozen)
|
|
134
|
+
else
|
|
135
|
+
old = r.push_limit(r.read_varint)
|
|
136
|
+
sub.__decode_fields(r, depth - 1, 0, frozen)
|
|
137
|
+
r.pop_limit(old)
|
|
138
|
+
end
|
|
139
|
+
sub.freeze if frozen
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def unknown(msg, r, number, wt, depth)
|
|
143
|
+
start = r.pos
|
|
144
|
+
r.skip_field(number, wt, depth)
|
|
145
|
+
bytes = r.buffer.byteslice(start, r.pos - start).force_encoding(BINARY).freeze
|
|
146
|
+
msg.__add_unknown(UnknownField.new(number, wt, bytes))
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def enum(msg, values, info, r)
|
|
150
|
+
start = r.pos
|
|
151
|
+
n = r.read_int32
|
|
152
|
+
v = info.enum_names[n]
|
|
153
|
+
if v.nil?
|
|
154
|
+
if info.closed_enum
|
|
155
|
+
bytes = r.buffer.byteslice(start, r.pos - start).force_encoding(BINARY).freeze
|
|
156
|
+
msg.__add_unknown(UnknownField.new(info.number, Wire::VARINT, bytes))
|
|
157
|
+
return
|
|
158
|
+
end
|
|
159
|
+
v = n
|
|
160
|
+
end
|
|
161
|
+
if info.repeated
|
|
162
|
+
(values[info.index] ||= []) << v
|
|
163
|
+
else
|
|
164
|
+
siblings = info.siblings
|
|
165
|
+
siblings&.each { |s| values[s] = nil }
|
|
166
|
+
values[info.index] = v
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# One value of a scalar field other than an enum
|
|
171
|
+
def scalar(r, info)
|
|
172
|
+
case info.kind
|
|
173
|
+
when F::INT32 then r.read_int32
|
|
174
|
+
when F::INT64 then r.read_int64
|
|
175
|
+
when F::UINT32 then r.read_uint32
|
|
176
|
+
when F::UINT64 then r.read_varint
|
|
177
|
+
when F::SINT32 then r.read_sint32
|
|
178
|
+
when F::SINT64 then r.read_sint64
|
|
179
|
+
when F::BOOL then r.read_varint != 0
|
|
180
|
+
when F::STRING then string(r, info)
|
|
181
|
+
when F::BYTES then r.read_bytes.freeze
|
|
182
|
+
when F::DOUBLE then r.read_double
|
|
183
|
+
when F::FLOAT then r.read_float
|
|
184
|
+
when F::FIXED32 then r.read_fixed32
|
|
185
|
+
when F::FIXED64 then r.read_fixed64
|
|
186
|
+
when F::SFIXED32 then r.read_sfixed32
|
|
187
|
+
when F::SFIXED64 then r.read_sfixed64
|
|
188
|
+
when F::ENUM then r.read_int32
|
|
189
|
+
else raise DecodeError, "#{info.descriptor.full_name} is not a scalar field"
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def string(r, info)
|
|
194
|
+
s = r.read_bytes.force_encoding(UTF_8)
|
|
195
|
+
if info.utf8_validation && !s.valid_encoding?
|
|
196
|
+
raise DecodeError.new("invalid UTF-8 in #{info.descriptor.full_name}", offset: r.pos - s.bytesize)
|
|
197
|
+
end
|
|
198
|
+
s.freeze
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Keeps a closed enum's number that has no name as an unknown varint field
|
|
202
|
+
def closed_enum_value(msg, number, buf, start, pos)
|
|
203
|
+
bytes = buf.byteslice(start, pos - start).force_encoding(BINARY).freeze
|
|
204
|
+
msg.__add_unknown(UnknownField.new(number, Wire::VARINT, bytes))
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def packed(msg, values, info, r)
|
|
208
|
+
old = r.push_limit(r.read_varint)
|
|
209
|
+
list = (values[info.index] ||= [])
|
|
210
|
+
kind = info.kind
|
|
211
|
+
if (fixed = F::FIXED[kind])
|
|
212
|
+
width = fixed[0]
|
|
213
|
+
length = r.limit - r.pos
|
|
214
|
+
raise DecodeError.new("packed #{info.descriptor.type} length #{length} is not a multiple of #{width}", offset: r.pos) unless (length % width).zero?
|
|
215
|
+
# one unpack for the whole run instead of a read per element
|
|
216
|
+
list.concat(r.read_raw(length).unpack(PACKED.fetch(kind)))
|
|
217
|
+
elsif kind == F::ENUM
|
|
218
|
+
names = info.enum_names
|
|
219
|
+
closed = info.closed_enum
|
|
220
|
+
while r.pos < r.limit
|
|
221
|
+
start = r.pos
|
|
222
|
+
n = r.read_int32
|
|
223
|
+
v = names[n]
|
|
224
|
+
if v
|
|
225
|
+
list << v
|
|
226
|
+
elsif closed
|
|
227
|
+
bytes = r.buffer.byteslice(start, r.pos - start).force_encoding(BINARY).freeze
|
|
228
|
+
msg.__add_unknown(UnknownField.new(info.number, Wire::VARINT, bytes))
|
|
229
|
+
else
|
|
230
|
+
list << n
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
else
|
|
234
|
+
list << scalar(r, info) while r.pos < r.limit
|
|
235
|
+
end
|
|
236
|
+
r.pop_limit(old)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def map_entry(msg, values, info, r, depth, frozen)
|
|
240
|
+
start = r.pos
|
|
241
|
+
old = r.push_limit(r.read_varint)
|
|
242
|
+
key_info = info.key
|
|
243
|
+
value_info = info.value
|
|
244
|
+
key = nil
|
|
245
|
+
value = nil
|
|
246
|
+
unknown_enum = false
|
|
247
|
+
while r.pos < r.limit
|
|
248
|
+
at = r.pos
|
|
249
|
+
tag = r.read_tag_varint
|
|
250
|
+
Wire::Reader.check_tag(tag, at)
|
|
251
|
+
number = tag >> 3
|
|
252
|
+
wt = tag & 7
|
|
253
|
+
if number == 1 && wt == key_info.wire_type
|
|
254
|
+
key = scalar(r, key_info)
|
|
255
|
+
elsif number == 2 && wt == value_info.wire_type
|
|
256
|
+
if value_info.message?
|
|
257
|
+
if value.nil?
|
|
258
|
+
value = value_info.message_class.__empty
|
|
259
|
+
elsif value.frozen?
|
|
260
|
+
value = value.dup
|
|
261
|
+
end
|
|
262
|
+
nested(value, value_info, r, depth, frozen, at)
|
|
263
|
+
elsif value_info.kind == F::ENUM
|
|
264
|
+
n = r.read_int32
|
|
265
|
+
value = value_info.enum_names[n]
|
|
266
|
+
if value.nil?
|
|
267
|
+
unknown_enum = value_info.closed_enum
|
|
268
|
+
value = n
|
|
269
|
+
else
|
|
270
|
+
unknown_enum = false
|
|
271
|
+
end
|
|
272
|
+
else
|
|
273
|
+
value = scalar(r, value_info)
|
|
274
|
+
end
|
|
275
|
+
else
|
|
276
|
+
raise DecodeError.new("unmatched end of group #{number}", offset: at) if wt == Wire::END_GROUP
|
|
277
|
+
r.skip_field(number, wt, depth)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
r.pop_limit(old)
|
|
281
|
+
if unknown_enum
|
|
282
|
+
# protoc keeps the whole entry as an unknown field of the map
|
|
283
|
+
bytes = r.buffer.byteslice(start, r.pos - start).force_encoding(BINARY).freeze
|
|
284
|
+
msg.__add_unknown(UnknownField.new(info.number, Wire::LENGTH_DELIMITED, bytes))
|
|
285
|
+
return
|
|
286
|
+
end
|
|
287
|
+
key = key_info.default if key.nil?
|
|
288
|
+
if value.nil?
|
|
289
|
+
value = value_info.message? ? value_info.message_class.__empty : value_info.default
|
|
290
|
+
end
|
|
291
|
+
value.freeze if frozen
|
|
292
|
+
(values[info.index] ||= {})[key] = value
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
module Codec
|
|
5
|
+
# Writes the two encoding passes of one message class as Ruby source and
|
|
6
|
+
# evals them into the class: `__size(sizes)`, which returns the encoded
|
|
7
|
+
# size and records it (and every packed run's) in `sizes`, and
|
|
8
|
+
# `__write(w, buf, sizes)`, which appends the fields to the Writer's
|
|
9
|
+
# buffer. Fields come in ascending number order, tags are literal bytes,
|
|
10
|
+
# one-byte varints are appended inline, and enum names map to numbers
|
|
11
|
+
# through a literal `case`. Maps, packed runs and unknown fields go to
|
|
12
|
+
# {Sizer} and {Encoder}.
|
|
13
|
+
#
|
|
14
|
+
# The generic loop over field descriptors this replaces spent most of
|
|
15
|
+
# its time on method calls into the field table.
|
|
16
|
+
# @api private
|
|
17
|
+
module EncodeGenerator
|
|
18
|
+
F = Message::FieldInfo
|
|
19
|
+
|
|
20
|
+
INT_BOUNDS = {
|
|
21
|
+
F::INT32 => [-0x8000_0000, 0x7FFF_FFFF], F::SINT32 => [-0x8000_0000, 0x7FFF_FFFF],
|
|
22
|
+
F::SFIXED32 => [-0x8000_0000, 0x7FFF_FFFF], F::UINT32 => [0, 0xFFFF_FFFF], F::FIXED32 => [0, 0xFFFF_FFFF],
|
|
23
|
+
F::INT64 => [-0x8000_0000_0000_0000, 0x7FFF_FFFF_FFFF_FFFF],
|
|
24
|
+
F::SINT64 => [-0x8000_0000_0000_0000, 0x7FFF_FFFF_FFFF_FFFF],
|
|
25
|
+
F::SFIXED64 => [-0x8000_0000_0000_0000, 0x7FFF_FFFF_FFFF_FFFF],
|
|
26
|
+
F::UINT64 => [0, 0xFFFF_FFFF_FFFF_FFFF], F::FIXED64 => [0, 0xFFFF_FFFF_FFFF_FFFF]
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
VARINT_KINDS = [F::INT32, F::INT64, F::UINT32, F::UINT64].freeze
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
# @param klass [Class]
|
|
34
|
+
# @param fields [Array<Message::FieldInfo>]
|
|
35
|
+
def install(klass, fields)
|
|
36
|
+
ordered = fields.sort_by(&:number)
|
|
37
|
+
klass.class_eval(source(ordered), "(protostar encoder #{klass.descriptor.full_name})", 1)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def source(fields)
|
|
41
|
+
<<~RUBY
|
|
42
|
+
# frozen_string_literal: true
|
|
43
|
+
def __size(sizes)
|
|
44
|
+
#{"values = @values" unless fields.empty?}
|
|
45
|
+
total = 0
|
|
46
|
+
#{fields.map { |f| size_field(f) }.join}
|
|
47
|
+
total += Protostar::Codec::Sizer.unknown(@unknown) if @unknown
|
|
48
|
+
sizes[self] = total
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def __write(w, buf, sizes)
|
|
52
|
+
#{"values = @values" unless fields.empty?}
|
|
53
|
+
#{fields.map { |f| write_field(f) }.join}
|
|
54
|
+
Protostar::Codec::Encoder.unknown(w, @unknown) if @unknown
|
|
55
|
+
w.flush_if_full
|
|
56
|
+
end
|
|
57
|
+
RUBY
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def info(f)
|
|
61
|
+
"self.class.__fields[#{f.index}]"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# The condition under which a field is written, with its value in `v`
|
|
65
|
+
def present(f)
|
|
66
|
+
i = f.index
|
|
67
|
+
if f.repeated || f.map
|
|
68
|
+
"unless (v = values[#{i}]).nil? || v.empty?"
|
|
69
|
+
elsif f.presence
|
|
70
|
+
"unless (v = values[#{i}]).nil?"
|
|
71
|
+
else
|
|
72
|
+
"v = values[#{i}]\nif #{nonzero(f)}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Negative zero is written, as protoc compares the bits
|
|
77
|
+
def nonzero(f)
|
|
78
|
+
case f.kind
|
|
79
|
+
when F::DOUBLE, F::FLOAT then "v != 0 || (1.0 / v) < 0"
|
|
80
|
+
when F::BOOL then "v"
|
|
81
|
+
when F::STRING, F::BYTES then "!v.empty?"
|
|
82
|
+
when F::ENUM
|
|
83
|
+
zero = f.enum_names[0]
|
|
84
|
+
zero ? "!v.equal?(#{zero.inspect}) && v != 0" : "v != 0"
|
|
85
|
+
else "v != 0"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def tag_bytes(f, wire_type = f.wire_type)
|
|
90
|
+
Wire.tag_bytes(f.number, wire_type).bytes
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def size_field(f)
|
|
94
|
+
tag = tag_bytes(f).size
|
|
95
|
+
body = if f.map
|
|
96
|
+
"total += Protostar::Codec::Sizer.map(#{info(f)}, v, sizes)\n"
|
|
97
|
+
elsif f.packed
|
|
98
|
+
"#{packed_size(f)}sizes[v] = run\n" \
|
|
99
|
+
"total += #{tag_bytes(f, Wire::LENGTH_DELIMITED).size} + #{varint_size("run")} + run\n"
|
|
100
|
+
elsif f.repeated
|
|
101
|
+
repeated_size(f, tag)
|
|
102
|
+
else
|
|
103
|
+
tags = (f.kind == F::GROUP) ? tag * 2 : tag
|
|
104
|
+
"#{value_size(f, "v", "n")}total += #{tags} + n\n"
|
|
105
|
+
end
|
|
106
|
+
"# #{f.name}\n#{present(f)}\n#{body}end\n"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Statements leaving the payload size of packed run `v` in `run`,
|
|
110
|
+
# checking each element as {Sizer.packed} does, without a call per element
|
|
111
|
+
def packed_size(f)
|
|
112
|
+
check = (f.kind == F::ENUM) ? "" : "Protostar::Codec::Sizer.invalid(#{info(f)}, e) unless #{valid(f, "e")}\n"
|
|
113
|
+
case f.kind
|
|
114
|
+
when *VARINT_KINDS, F::SINT32, F::SINT64, F::ENUM
|
|
115
|
+
"run = 0\n#{each_element("#{check}#{value_size(f, "e", "n")}run += n\n")}"
|
|
116
|
+
else
|
|
117
|
+
width = (f.kind == F::BOOL) ? 1 : F::FIXED.fetch(f.kind).first
|
|
118
|
+
"#{each_element(check)}run = #{width} * v.size\n"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Statements appending packed run `v`'s payload
|
|
123
|
+
def packed_write(f)
|
|
124
|
+
case f.kind
|
|
125
|
+
when *VARINT_KINDS, F::SINT32, F::SINT64, F::ENUM then each_element(write_value(f, "e"))
|
|
126
|
+
when F::BOOL then each_element("buf << (e ? 1 : 0)\n")
|
|
127
|
+
when F::FLOAT then "buf << Protostar::Wire.pack_floats(v)\n"
|
|
128
|
+
else "v.pack(#{"#{F::FIXED.fetch(f.kind).last}*".inspect}, buffer: buf)\n"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def repeated_size(f, tag)
|
|
133
|
+
tag *= 2 if f.kind == F::GROUP
|
|
134
|
+
src = "total += #{tag} * v.size\n"
|
|
135
|
+
src << "k = #{info(f)}.message_class\n" if f.message?
|
|
136
|
+
check = (f.kind == F::ENUM) ? "" : "Protostar::Codec::Sizer.invalid(#{info(f)}, e) unless #{valid(f, "e")}\n"
|
|
137
|
+
src << each_element("#{check}#{value_size(f, "e", "n")}total += n\n")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# A loop running `body` with each element of `v` in `e`
|
|
141
|
+
def each_element(body)
|
|
142
|
+
"v.each do |e|\n#{body}end\n"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Whether an element of a repeated field can be written; elements may
|
|
146
|
+
# have been pushed into the plain Array past the writers' checks
|
|
147
|
+
def valid(f, var)
|
|
148
|
+
case f.kind
|
|
149
|
+
when F::DOUBLE, F::FLOAT then "(#{var}.is_a?(Float) || #{var}.is_a?(Integer))"
|
|
150
|
+
when F::BOOL then "(#{var} == true || #{var} == false)"
|
|
151
|
+
when F::STRING, F::BYTES then "#{var}.is_a?(String)"
|
|
152
|
+
when F::MESSAGE, F::GROUP then "#{var}.instance_of?(k)"
|
|
153
|
+
else
|
|
154
|
+
min, max = INT_BOUNDS.fetch(f.kind)
|
|
155
|
+
"(#{var}.is_a?(Integer) && #{var} >= #{min} && #{var} <= #{max})"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def varint_size(var)
|
|
160
|
+
"(#{var} < 0x80 && #{var} >= 0 ? 1 : (#{var} < 0x4000 && #{var} >= 0 ? 2 : Protostar::Wire.varint_size(#{var})))"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Statements leaving the size of value `var`, tag excluded, in `out`
|
|
164
|
+
def value_size(f, var, out)
|
|
165
|
+
case f.kind
|
|
166
|
+
when *VARINT_KINDS then "#{out} = #{varint_size(var)}\n"
|
|
167
|
+
when F::SINT32, F::SINT64 then "z = (#{var} << 1) ^ (#{var} >> 63)\n#{out} = #{varint_size("z")}\n"
|
|
168
|
+
when F::BOOL then "#{out} = 1\n"
|
|
169
|
+
when F::ENUM then "z = #{enum_number(f, var)}\n#{out} = #{varint_size("z")}\n"
|
|
170
|
+
when F::STRING, F::BYTES then "#{out} = #{var}.bytesize\n#{out} += #{varint_size(out)}\n"
|
|
171
|
+
when F::MESSAGE then "#{out} = #{var}.__size(sizes)\n#{out} += #{varint_size(out)}\n"
|
|
172
|
+
when F::GROUP then "#{out} = #{var}.__size(sizes)\n"
|
|
173
|
+
else "#{out} = #{F::FIXED.fetch(f.kind).first}\n"
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# An expression for the number of enum value `var`
|
|
178
|
+
def enum_number(f, var)
|
|
179
|
+
src = "case #{var}\n"
|
|
180
|
+
f.enum_numbers.each { |name, number| src << "when #{name.inspect} then #{number}\n" }
|
|
181
|
+
src << "when Integer then (#{var} >= -0x8000_0000 && #{var} <= 0x7FFF_FFFF) ? #{var} : " \
|
|
182
|
+
"Protostar::Codec::Encoder.enum_number(#{info(f)}, #{var})\n"
|
|
183
|
+
src << "else Protostar::Codec::Encoder.enum_number(#{info(f)}, #{var})\nend"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def write_field(f)
|
|
187
|
+
body = if f.map
|
|
188
|
+
"Protostar::Codec::Encoder.map(#{info(f)}, v, w, sizes)\n"
|
|
189
|
+
elsif f.packed
|
|
190
|
+
"#{append(tag_bytes(f, Wire::LENGTH_DELIMITED))}#{write_varint("sizes[v]")}#{packed_write(f)}"
|
|
191
|
+
elsif f.repeated
|
|
192
|
+
each_element("#{append(tag_bytes(f))}#{write_value(f, "e")}")
|
|
193
|
+
else
|
|
194
|
+
"#{append(tag_bytes(f))}#{write_value(f, "v")}"
|
|
195
|
+
end
|
|
196
|
+
"# #{f.name}\n#{present(f)}\n#{body}end\n"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Appending an Integer to a binary String appends that byte
|
|
200
|
+
def append(bytes)
|
|
201
|
+
"buf#{bytes.map { |b| " << #{b}" }.join}\n"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def write_varint(expr)
|
|
205
|
+
"if (x = #{expr}) < 0x80 && x >= 0\nbuf << x\nelsif x < 0x4000 && x >= 0\nbuf << ((x & 0x7F) | 0x80) << (x >> 7)\nelse\nw.write_varint(x)\nend\n"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def write_value(f, var)
|
|
209
|
+
case f.kind
|
|
210
|
+
when *VARINT_KINDS then write_varint(var)
|
|
211
|
+
when F::SINT32, F::SINT64 then write_varint("(#{var} << 1) ^ (#{var} >> 63)")
|
|
212
|
+
when F::BOOL then "buf << (#{var} ? 1 : 0)\n"
|
|
213
|
+
when F::ENUM then write_varint(enum_number(f, var))
|
|
214
|
+
when F::STRING, F::BYTES then "#{write_varint("#{var}.bytesize")}w.write_raw(#{var})\n"
|
|
215
|
+
when F::MESSAGE then "#{write_varint("sizes[#{var}]")}#{var}.__write(w, buf, sizes)\n"
|
|
216
|
+
when F::GROUP then "#{var}.__write(w, buf, sizes)\n#{append(tag_bytes(f, Wire::END_GROUP))}"
|
|
217
|
+
when F::FLOAT
|
|
218
|
+
"if #{var} == #{var}\n[#{var}].pack(\"e\", buffer: buf)\nelse\nbuf << Protostar::Wire.float_nan(#{var})\nend\n"
|
|
219
|
+
else "[#{var}].pack(#{F::FIXED.fetch(f.kind).last.inspect}, buffer: buf)\n"
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
module Codec
|
|
5
|
+
# The second encoding pass writes fields in ascending number order, then
|
|
6
|
+
# unknown fields verbatim, using the sizes the first pass recorded. The
|
|
7
|
+
# per-class `__write` methods ({EncodeGenerator}) do the work; this
|
|
8
|
+
# module has the entry points and the parts they share.
|
|
9
|
+
# @api private
|
|
10
|
+
module Encoder
|
|
11
|
+
F = Message::FieldInfo
|
|
12
|
+
BINARY = Encoding::BINARY
|
|
13
|
+
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
# @return [String] binary
|
|
17
|
+
def encode(msg)
|
|
18
|
+
sizes = {}.compare_by_identity
|
|
19
|
+
size = msg.__size(sizes)
|
|
20
|
+
w = Wire::Writer.new(String.new(capacity: size, encoding: BINARY))
|
|
21
|
+
msg.__write(w, w.string, sizes)
|
|
22
|
+
w.string
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def encode_to(msg, io)
|
|
26
|
+
sizes = {}.compare_by_identity
|
|
27
|
+
msg.__size(sizes)
|
|
28
|
+
w = Wire::Writer.new(io)
|
|
29
|
+
msg.__write(w, w.string, sizes)
|
|
30
|
+
w.flush
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [String, IO]
|
|
34
|
+
def encode_delimited(msg, io)
|
|
35
|
+
sizes = {}.compare_by_identity
|
|
36
|
+
size = msg.__size(sizes)
|
|
37
|
+
w = Wire::Writer.new(io || String.new(capacity: size + 10, encoding: BINARY))
|
|
38
|
+
w.write_varint(size)
|
|
39
|
+
msg.__write(w, w.string, sizes)
|
|
40
|
+
return w.string unless io
|
|
41
|
+
w.flush
|
|
42
|
+
io
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [Integer] the number of an enum value as stored (Symbol or Integer)
|
|
46
|
+
# @raise [EncodeError] for a name the enum does not have or an Integer out of int32 range
|
|
47
|
+
def enum_number(info, v)
|
|
48
|
+
if v.is_a?(Integer)
|
|
49
|
+
return v if Message::Coercion::ENUM_RANGE.cover?(v)
|
|
50
|
+
elsif (n = info.enum_numbers[v])
|
|
51
|
+
return n
|
|
52
|
+
end
|
|
53
|
+
raise EncodeError, "#{info.descriptor.full_name} holds #{v.inspect[0, 64]}, which is not a value of its enum"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# One value, without its tag; a group's end tag is written here
|
|
57
|
+
def value(info, v, w, sizes)
|
|
58
|
+
case info.kind
|
|
59
|
+
when F::INT32, F::INT64, F::UINT32, F::UINT64 then w.write_varint(v)
|
|
60
|
+
when F::ENUM then w.write_varint(enum_number(info, v))
|
|
61
|
+
when F::SINT32, F::SINT64 then w.write_varint((v << 1) ^ (v >> 63))
|
|
62
|
+
when F::BOOL then w.write_varint(v ? 1 : 0)
|
|
63
|
+
when F::STRING, F::BYTES then w.write_bytes(v)
|
|
64
|
+
when F::MESSAGE
|
|
65
|
+
w.write_varint(sizes.fetch(v))
|
|
66
|
+
v.__write(w, w.string, sizes)
|
|
67
|
+
when F::GROUP
|
|
68
|
+
v.__write(w, w.string, sizes)
|
|
69
|
+
w.write_raw(info.end_tag)
|
|
70
|
+
when F::DOUBLE then w.write_double(v)
|
|
71
|
+
when F::FLOAT then w.write_float(v)
|
|
72
|
+
when F::FIXED64 then w.write_fixed64(v)
|
|
73
|
+
when F::FIXED32 then w.write_fixed32(v)
|
|
74
|
+
when F::SFIXED64 then w.write_sfixed64(v)
|
|
75
|
+
when F::SFIXED32 then w.write_sfixed32(v)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# The payload of a packed run; tag and length are already written
|
|
80
|
+
def packed(info, list, w)
|
|
81
|
+
kind = info.kind
|
|
82
|
+
if kind == F::FLOAT
|
|
83
|
+
w.write_raw(Wire.pack_floats(list))
|
|
84
|
+
elsif (fixed = F::FIXED[kind])
|
|
85
|
+
# one pack for the whole run
|
|
86
|
+
w.write_raw(list.pack("#{fixed[1]}*"))
|
|
87
|
+
elsif kind == F::ENUM
|
|
88
|
+
list.each { |v| w.write_varint(enum_number(info, v)) }
|
|
89
|
+
elsif kind == F::BOOL
|
|
90
|
+
list.each { |v| w.write_varint(v ? 1 : 0) }
|
|
91
|
+
elsif kind == F::SINT32 || kind == F::SINT64
|
|
92
|
+
list.each { |v| w.write_varint((v << 1) ^ (v >> 63)) }
|
|
93
|
+
else
|
|
94
|
+
list.each { |v| w.write_varint(v) }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Every entry of a map field, in insertion order, key then value
|
|
99
|
+
def map(info, hash, w, sizes)
|
|
100
|
+
key_info = info.key
|
|
101
|
+
value_info = info.value
|
|
102
|
+
hash.each do |key, val|
|
|
103
|
+
w.write_raw(info.tag)
|
|
104
|
+
w.write_varint(Sizer.entry(info, key, val, sizes))
|
|
105
|
+
w.write_raw(key_info.tag)
|
|
106
|
+
value(key_info, key, w, sizes)
|
|
107
|
+
w.write_raw(value_info.tag)
|
|
108
|
+
value(value_info, val, w, sizes)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @param list [Array<UnknownField>]
|
|
113
|
+
def unknown(w, list)
|
|
114
|
+
list.each do |u|
|
|
115
|
+
w.write_varint((u.number << 3) | u.wire_type)
|
|
116
|
+
w.write_raw(u.bytes)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|