protobug 0.1.0 → 0.2.0
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 +4 -4
- data/CHANGELOG.md +25 -0
- data/lib/protobug/binary_encoding.rb +88 -20
- data/lib/protobug/enum.rb +8 -3
- data/lib/protobug/field.rb +283 -91
- data/lib/protobug/message.rb +37 -47
- data/lib/protobug/version.rb +1 -1
- data/lib/protobug.rb +1 -3
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 04bd54e56a0b5d9f3bdc255e2b134257f7d2c35710e718cd50555834e9df482d
|
|
4
|
+
data.tar.gz: e6fbdee91e29f5f6c498004ad8137c2e59fc6d743933f22d834d923245632c6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a417bfdfca7b8f9429f252cc698313141c8e3b0c2de5a40f4358a91f6c0834e692376cd5e57bb205c2c978b818f5ea4cabe752c7c04e807073d2d96252525b57
|
|
7
|
+
data.tar.gz: 1b766a9b88e937e6615573ef0dc1842e2341b81d020c28ee47aec99aa29729fb9bae351c227d81797eae80ac41b4ac7d7145b066ff6f0237410d963569077589
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-06-18
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- New compiled proto gems: `protobug_fulcio_protos`, `protobug_googleapis_annotations_protos`,
|
|
8
|
+
`protobug_in_toto_attestation_protos`, and `protobug_protoc_gen_openapiv2_protos`.
|
|
9
|
+
- Bump sigstore protobuf-specs to v0.5.1, adding `SigningConfig` v0.2 along with the
|
|
10
|
+
`Service`, `ServiceSelector`, and `ServiceConfiguration` messages.
|
|
11
|
+
- Significantly expanded test coverage.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Decode a truncated varint as an error (`EOFError`) instead of silently returning `nil`,
|
|
16
|
+
and reject varints whose 10th byte overflows 64 bits.
|
|
17
|
+
- Encode fixed-width integers (`fixed64`/`sfixed64`/`sfixed32`) in little-endian order,
|
|
18
|
+
correcting the binary wire format on big-endian hosts.
|
|
19
|
+
- Re-encode unknown fields of wire types 1 and 5 as raw bytes rather than varints.
|
|
20
|
+
- Raise a structured `InvalidValueError` on invalid boolean values.
|
|
21
|
+
- Accept decimal and exponent notation when parsing floats from JSON.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Generate message field accessors via `class_eval` rather than `define_method` closures.
|
|
26
|
+
- `Enum#==` returns `false` for unrecognized operand types instead of raising.
|
|
27
|
+
|
|
3
28
|
## [0.1.0] - 2024-02-21
|
|
4
29
|
|
|
5
30
|
- Initial release
|
|
@@ -10,19 +10,27 @@ module Protobug
|
|
|
10
10
|
raise EncodeError, "expected integer, got #{value.inspect}" unless value.is_a? Integer
|
|
11
11
|
raise RangeError, "expected 64-bit integer" if value > (2**64) - 1 || value < -2**63
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
value = (2**64) + value if negative
|
|
15
|
-
out = []
|
|
13
|
+
value += 2**64 if value < 0
|
|
16
14
|
loop do
|
|
17
15
|
if value.bit_length > 7
|
|
18
|
-
|
|
16
|
+
outbuf << (0b1000_0000 | (value & 0b0111_1111))
|
|
19
17
|
value >>= 7
|
|
20
18
|
else
|
|
21
|
-
|
|
19
|
+
outbuf << (value & 0b0111_1111)
|
|
22
20
|
break
|
|
23
21
|
end
|
|
24
22
|
end
|
|
25
|
-
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if RUBY_ENGINE == "truffleruby"
|
|
26
|
+
# see https://github.com/oracle/truffleruby/issues/3559
|
|
27
|
+
def pack(ary, format, buffer:)
|
|
28
|
+
buffer.concat ary.pack(format)
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
def pack(ary, format, buffer:)
|
|
32
|
+
ary.pack(format, buffer: buffer)
|
|
33
|
+
end
|
|
26
34
|
end
|
|
27
35
|
|
|
28
36
|
def encode_zigzag(size, value, outbuf)
|
|
@@ -34,7 +42,7 @@ module Protobug
|
|
|
34
42
|
end
|
|
35
43
|
|
|
36
44
|
encoded = 2 * value.abs
|
|
37
|
-
encoded -= 1 if value
|
|
45
|
+
encoded -= 1 if value < 0
|
|
38
46
|
encode_varint encoded, outbuf
|
|
39
47
|
end
|
|
40
48
|
|
|
@@ -43,20 +51,80 @@ module Protobug
|
|
|
43
51
|
outbuf << contents
|
|
44
52
|
end
|
|
45
53
|
|
|
46
|
-
def
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
value = 0
|
|
51
|
-
bl = 0
|
|
52
|
-
loop do
|
|
53
|
-
raise DecodeError, "varint too large" if bl > 63
|
|
54
|
-
return value if byte.nil?
|
|
55
|
-
return value | (byte << bl) if (byte & 0b1000_0000).zero? # no continuation bit set
|
|
54
|
+
def varint_eof!
|
|
55
|
+
raise EOFError, "unexpected EOF in the middle of a varint"
|
|
56
|
+
end
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
def decode_varint(binary)
|
|
59
|
+
# Only the first byte may legitimately be absent (clean EOF, signaled by
|
|
60
|
+
# returning nil). Once a continuation bit is set, any subsequent missing
|
|
61
|
+
# byte is a truncated varint and must be rejected.
|
|
62
|
+
if (byte0 = binary.getbyte || return) < 0x80
|
|
63
|
+
byte0
|
|
64
|
+
elsif (byte1 = binary.getbyte || varint_eof!) < 0x80
|
|
65
|
+
(byte1 << 7) | (byte0 & 0x7F)
|
|
66
|
+
elsif (byte2 = binary.getbyte || varint_eof!) < 0x80
|
|
67
|
+
(byte2 << 14) |
|
|
68
|
+
((byte1 & 0x7F) << 7) |
|
|
69
|
+
(byte0 & 0x7F)
|
|
70
|
+
elsif (byte3 = binary.getbyte || varint_eof!) < 0x80
|
|
71
|
+
(byte3 << 21) |
|
|
72
|
+
((byte2 & 0x7F) << 14) |
|
|
73
|
+
((byte1 & 0x7F) << 7) |
|
|
74
|
+
(byte0 & 0x7F)
|
|
75
|
+
elsif (byte4 = binary.getbyte || varint_eof!) < 0x80
|
|
76
|
+
(byte4 << 28) |
|
|
77
|
+
((byte3 & 0x7F) << 21) |
|
|
78
|
+
((byte2 & 0x7F) << 14) |
|
|
79
|
+
((byte1 & 0x7F) << 7) |
|
|
80
|
+
(byte0 & 0x7F)
|
|
81
|
+
elsif (byte5 = binary.getbyte || varint_eof!) < 0x80
|
|
82
|
+
(byte5 << 35) |
|
|
83
|
+
((byte4 & 0x7F) << 28) |
|
|
84
|
+
((byte3 & 0x7F) << 21) |
|
|
85
|
+
((byte2 & 0x7F) << 14) |
|
|
86
|
+
((byte1 & 0x7F) << 7) |
|
|
87
|
+
(byte0 & 0x7F)
|
|
88
|
+
elsif (byte6 = binary.getbyte || varint_eof!) < 0x80
|
|
89
|
+
(byte6 << 42) |
|
|
90
|
+
((byte5 & 0x7F) << 35) |
|
|
91
|
+
((byte4 & 0x7F) << 28) |
|
|
92
|
+
((byte3 & 0x7F) << 21) |
|
|
93
|
+
((byte2 & 0x7F) << 14) |
|
|
94
|
+
((byte1 & 0x7F) << 7) |
|
|
95
|
+
(byte0 & 0x7F)
|
|
96
|
+
elsif (byte7 = binary.getbyte || varint_eof!) < 0x80
|
|
97
|
+
(byte7 << 49) |
|
|
98
|
+
((byte6 & 0x7F) << 42) |
|
|
99
|
+
((byte5 & 0x7F) << 35) |
|
|
100
|
+
((byte4 & 0x7F) << 28) |
|
|
101
|
+
((byte3 & 0x7F) << 21) |
|
|
102
|
+
((byte2 & 0x7F) << 14) |
|
|
103
|
+
((byte1 & 0x7F) << 7) |
|
|
104
|
+
(byte0 & 0x7F)
|
|
105
|
+
elsif (byte8 = binary.getbyte || varint_eof!) < 0x80
|
|
106
|
+
(byte8 << 56) |
|
|
107
|
+
((byte7 & 0x7F) << 49) |
|
|
108
|
+
((byte6 & 0x7F) << 42) |
|
|
109
|
+
((byte5 & 0x7F) << 35) |
|
|
110
|
+
((byte4 & 0x7F) << 28) |
|
|
111
|
+
((byte3 & 0x7F) << 21) |
|
|
112
|
+
((byte2 & 0x7F) << 14) |
|
|
113
|
+
((byte1 & 0x7F) << 7) |
|
|
114
|
+
(byte0 & 0x7F)
|
|
115
|
+
elsif (byte9 = binary.getbyte || varint_eof!) < 0x80
|
|
116
|
+
raise DecodeError, "varint overflow: 10th byte #{byte9} exceeds 64 bits" if byte9 > 1
|
|
117
|
+
|
|
118
|
+
(byte9 << 63) |
|
|
119
|
+
((byte8 & 0x7F) << 56) |
|
|
120
|
+
((byte7 & 0x7F) << 49) |
|
|
121
|
+
((byte6 & 0x7F) << 42) |
|
|
122
|
+
((byte5 & 0x7F) << 35) |
|
|
123
|
+
((byte4 & 0x7F) << 28) |
|
|
124
|
+
((byte3 & 0x7F) << 21) |
|
|
125
|
+
((byte2 & 0x7F) << 14) |
|
|
126
|
+
((byte1 & 0x7F) << 7) |
|
|
127
|
+
(byte0 & 0x7F)
|
|
60
128
|
end
|
|
61
129
|
end
|
|
62
130
|
|
data/lib/protobug/enum.rb
CHANGED
|
@@ -123,10 +123,15 @@ module Protobug
|
|
|
123
123
|
value == other.value
|
|
124
124
|
when Integer
|
|
125
125
|
value == other
|
|
126
|
-
when String
|
|
127
|
-
name
|
|
126
|
+
when String
|
|
127
|
+
# name is already a frozen String (see #initialize), so no allocation here.
|
|
128
|
+
name == other
|
|
129
|
+
when Symbol
|
|
130
|
+
# Symbol#name returns the frozen interned string, avoiding the allocation
|
|
131
|
+
# that Symbol#to_s would incur.
|
|
132
|
+
name == other.name
|
|
128
133
|
else
|
|
129
|
-
|
|
134
|
+
false
|
|
130
135
|
end
|
|
131
136
|
end
|
|
132
137
|
|
data/lib/protobug/field.rb
CHANGED
|
@@ -4,6 +4,8 @@ require_relative "binary_encoding"
|
|
|
4
4
|
|
|
5
5
|
module Protobug
|
|
6
6
|
class Field
|
|
7
|
+
PACKABLE_WIRE_TYPES = [0, 1, 5].freeze
|
|
8
|
+
|
|
7
9
|
attr_accessor :number, :name, :json_name, :cardinality, :oneof, :ivar, :setter,
|
|
8
10
|
:adder, :haser, :clearer
|
|
9
11
|
|
|
@@ -61,19 +63,55 @@ module Protobug
|
|
|
61
63
|
@proto3_optional
|
|
62
64
|
end
|
|
63
65
|
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
message.define_method(adder) do |value|
|
|
67
|
-
field.validate!(value, self)
|
|
66
|
+
def method_definitions
|
|
67
|
+
str = +""
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
str << "def #{setter}(value)\n"
|
|
70
|
+
str << " return #{ivar} = ::Protobug::UNSET if value.nil?\n" if optional? && proto3_optional?
|
|
71
|
+
str << " field = self.class.fields_by_name.fetch(#{name.to_s.dump})\n"
|
|
72
|
+
str << " field.validate!(value, self)\n"
|
|
73
|
+
str << " #{ivar} = value\n"
|
|
74
|
+
str << "end\n"
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
str << "def #{name}\n"
|
|
77
|
+
str << " value = #{ivar}\n"
|
|
78
|
+
str << " ::Protobug::UNSET == value ? self.class.fields_by_name.fetch(#{name.to_s.dump}).default : value\n"
|
|
79
|
+
str << "end\n"
|
|
80
|
+
|
|
81
|
+
str << "def #{haser}\n"
|
|
82
|
+
str << " value = #{ivar}\n"
|
|
83
|
+
str << " return false if ::Protobug::UNSET == value\n"
|
|
84
|
+
if (!optional? || !proto3_optional?) && !oneof
|
|
85
|
+
str << " field = self.class.fields_by_name.fetch(#{name.to_s.dump})\n"
|
|
86
|
+
str << " return false if field.default == value\n"
|
|
76
87
|
end
|
|
88
|
+
str << if repeated?
|
|
89
|
+
" !value.empty?\n"
|
|
90
|
+
else
|
|
91
|
+
" true\n"
|
|
92
|
+
end
|
|
93
|
+
str << "end\n"
|
|
94
|
+
|
|
95
|
+
str << "def #{clearer}\n"
|
|
96
|
+
str << " #{ivar} = ::Protobug::UNSET\n"
|
|
97
|
+
str << "end\n"
|
|
98
|
+
|
|
99
|
+
adder_method_definition(str) if repeated?
|
|
100
|
+
|
|
101
|
+
str
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def adder_method_definition(str)
|
|
105
|
+
str << "def #{adder}(value)\n"
|
|
106
|
+
str << " existing = #{ivar}\n"
|
|
107
|
+
str << " field = self.class.fields_by_name.fetch(#{name.to_s.dump})\n"
|
|
108
|
+
str << " if ::Protobug::UNSET == existing\n"
|
|
109
|
+
str << " existing = field.default\n"
|
|
110
|
+
str << " #{ivar} = existing\n"
|
|
111
|
+
str << " end\n"
|
|
112
|
+
str << " field.validate!(value, self)\n"
|
|
113
|
+
str << " existing << value\n"
|
|
114
|
+
str << "end\n"
|
|
77
115
|
end
|
|
78
116
|
|
|
79
117
|
def to_text(value)
|
|
@@ -108,12 +146,13 @@ module Protobug
|
|
|
108
146
|
end
|
|
109
147
|
|
|
110
148
|
def binary_decode(binary, message, registry, wire_type)
|
|
111
|
-
|
|
149
|
+
own_wire_type = self.wire_type
|
|
150
|
+
if repeated? && wire_type == 2 && PACKABLE_WIRE_TYPES.include?(own_wire_type)
|
|
112
151
|
len = StringIO.new(BinaryEncoding.decode_length(binary))
|
|
113
152
|
len.binmode
|
|
114
153
|
|
|
115
|
-
message.send(adder, binary_decode_one(len, message, registry,
|
|
116
|
-
elsif wire_type !=
|
|
154
|
+
message.send(adder, binary_decode_one(len, message, registry, own_wire_type)) until len.eof?
|
|
155
|
+
elsif wire_type != own_wire_type
|
|
117
156
|
raise DecodeError, "wrong wire type for #{self}: #{wire_type.inspect}"
|
|
118
157
|
else
|
|
119
158
|
message.send(adder || setter, binary_decode_one(binary, message, registry, wire_type))
|
|
@@ -226,7 +265,9 @@ module Protobug
|
|
|
226
265
|
nil
|
|
227
266
|
end
|
|
228
267
|
|
|
229
|
-
def wire_type
|
|
268
|
+
def wire_type
|
|
269
|
+
2
|
|
270
|
+
end
|
|
230
271
|
end
|
|
231
272
|
|
|
232
273
|
class MapField < MessageField
|
|
@@ -250,9 +291,17 @@ module Protobug
|
|
|
250
291
|
end
|
|
251
292
|
end
|
|
252
293
|
|
|
253
|
-
def repeated
|
|
254
|
-
|
|
255
|
-
|
|
294
|
+
def repeated
|
|
295
|
+
true
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def default
|
|
299
|
+
{}
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def repeated?
|
|
303
|
+
true
|
|
304
|
+
end
|
|
256
305
|
|
|
257
306
|
def binary_encode(value, outbuf)
|
|
258
307
|
value.each_with_object(@map_class.new) do |(k, v), entry|
|
|
@@ -295,24 +344,26 @@ module Protobug
|
|
|
295
344
|
end
|
|
296
345
|
end
|
|
297
346
|
|
|
298
|
-
def type_lookup(_registry)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
field = self
|
|
302
|
-
message.define_method(adder) do |msg|
|
|
303
|
-
existing = instance_variable_get(field.ivar)
|
|
304
|
-
if UNSET == existing
|
|
305
|
-
existing = field.default
|
|
306
|
-
instance_variable_set(field.ivar, existing)
|
|
307
|
-
end
|
|
347
|
+
def type_lookup(_registry)
|
|
348
|
+
@map_class
|
|
349
|
+
end
|
|
308
350
|
|
|
309
|
-
|
|
310
|
-
|
|
351
|
+
def adder_method_definition(str)
|
|
352
|
+
str << "def #{adder}(msg)\n"
|
|
353
|
+
str << " existing = #{ivar}\n"
|
|
354
|
+
str << " if ::Protobug::UNSET == existing\n"
|
|
355
|
+
str << " existing = {}\n"
|
|
356
|
+
str << " #{ivar} = existing\n"
|
|
357
|
+
str << " end\n"
|
|
358
|
+
str << " existing[msg.key] = msg.value\n"
|
|
359
|
+
str << "end\n"
|
|
311
360
|
end
|
|
312
361
|
end
|
|
313
362
|
|
|
314
363
|
class BytesField < Field
|
|
315
|
-
def self.type
|
|
364
|
+
def self.type
|
|
365
|
+
:bytes
|
|
366
|
+
end
|
|
316
367
|
|
|
317
368
|
def initialize(number, name, cardinality:, json_name: name, oneof: nil,
|
|
318
369
|
proto3_optional: cardinality == :optional)
|
|
@@ -352,11 +403,15 @@ module Protobug
|
|
|
352
403
|
"".b
|
|
353
404
|
end
|
|
354
405
|
|
|
355
|
-
def wire_type
|
|
406
|
+
def wire_type
|
|
407
|
+
2
|
|
408
|
+
end
|
|
356
409
|
end
|
|
357
410
|
|
|
358
411
|
class StringField < BytesField
|
|
359
|
-
def self.type
|
|
412
|
+
def self.type
|
|
413
|
+
:string
|
|
414
|
+
end
|
|
360
415
|
|
|
361
416
|
def initialize(number, name, cardinality:, json_name: name, oneof: nil,
|
|
362
417
|
proto3_optional: cardinality == :optional)
|
|
@@ -414,15 +469,12 @@ module Protobug
|
|
|
414
469
|
when :varint
|
|
415
470
|
length_mask = (2**bit_length) - 1
|
|
416
471
|
negative = signed && value & (2**bit_length.pred) != 0
|
|
417
|
-
# warn negative
|
|
418
|
-
length_mask >> 1 if signed
|
|
419
472
|
if negative
|
|
420
473
|
value &= length_mask # remove sign bit
|
|
421
474
|
|
|
422
475
|
# 2's complement
|
|
423
476
|
value ^= length_mask
|
|
424
477
|
value += 1
|
|
425
|
-
# value &= length_mask
|
|
426
478
|
-value
|
|
427
479
|
else
|
|
428
480
|
value & length_mask
|
|
@@ -439,7 +491,7 @@ module Protobug
|
|
|
439
491
|
when :varint
|
|
440
492
|
BinaryEncoding.encode_varint value, outbuf
|
|
441
493
|
when :fixed
|
|
442
|
-
[value]
|
|
494
|
+
BinaryEncoding.pack([value], binary_pack, buffer: outbuf)
|
|
443
495
|
end
|
|
444
496
|
end
|
|
445
497
|
|
|
@@ -452,8 +504,10 @@ module Protobug
|
|
|
452
504
|
when /\A-?\d+\z/
|
|
453
505
|
value = Integer(value)
|
|
454
506
|
when Float
|
|
455
|
-
|
|
456
|
-
raise DecodeError, "expected integer for #{inspect}, got #{value.inspect}" unless remainder
|
|
507
|
+
int, remainder = value.divmod(1)
|
|
508
|
+
raise DecodeError, "expected integer for #{inspect}, got #{value.inspect}" unless remainder == 0
|
|
509
|
+
|
|
510
|
+
value = int
|
|
457
511
|
else
|
|
458
512
|
raise DecodeError, "expected integer for #{inspect}, got #{value.inspect}"
|
|
459
513
|
end
|
|
@@ -494,77 +548,199 @@ module Protobug
|
|
|
494
548
|
# signed: true, false
|
|
495
549
|
# EXCEPT: no unsigned zigzag
|
|
496
550
|
class Int64Field < IntegerField
|
|
497
|
-
def encoding
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
551
|
+
def encoding
|
|
552
|
+
:varint
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def bit_length
|
|
556
|
+
64
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
def signed
|
|
560
|
+
true
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
def wire_type
|
|
564
|
+
0
|
|
565
|
+
end
|
|
501
566
|
end
|
|
502
567
|
|
|
503
568
|
class UInt64Field < IntegerField
|
|
504
|
-
def encoding
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
569
|
+
def encoding
|
|
570
|
+
:varint
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
def bit_length
|
|
574
|
+
64
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def signed
|
|
578
|
+
false
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
def wire_type
|
|
582
|
+
0
|
|
583
|
+
end
|
|
508
584
|
end
|
|
509
585
|
|
|
510
586
|
class SInt64Field < IntegerField
|
|
511
|
-
def encoding
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
587
|
+
def encoding
|
|
588
|
+
:zigzag
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def bit_length
|
|
592
|
+
64
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
def signed
|
|
596
|
+
true
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
def wire_type
|
|
600
|
+
0
|
|
601
|
+
end
|
|
515
602
|
end
|
|
516
603
|
|
|
517
604
|
class Fixed64Field < IntegerField
|
|
518
|
-
def encoding
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
def
|
|
605
|
+
def encoding
|
|
606
|
+
:fixed
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
def bit_length
|
|
610
|
+
64
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def signed
|
|
614
|
+
false
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def wire_type
|
|
618
|
+
1
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
def binary_pack
|
|
622
|
+
"Q<"
|
|
623
|
+
end
|
|
523
624
|
end
|
|
524
625
|
|
|
525
626
|
class SFixed64Field < IntegerField
|
|
526
|
-
def encoding
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
def
|
|
627
|
+
def encoding
|
|
628
|
+
:fixed
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def bit_length
|
|
632
|
+
64
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def signed
|
|
636
|
+
true
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
def wire_type
|
|
640
|
+
1
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def binary_pack
|
|
644
|
+
"q<"
|
|
645
|
+
end
|
|
531
646
|
end
|
|
532
647
|
|
|
533
648
|
class Int32Field < IntegerField
|
|
534
|
-
def encoding
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
649
|
+
def encoding
|
|
650
|
+
:varint
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
def bit_length
|
|
654
|
+
32
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
def signed
|
|
658
|
+
true
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
def wire_type
|
|
662
|
+
0
|
|
663
|
+
end
|
|
538
664
|
end
|
|
539
665
|
|
|
540
666
|
class UInt32Field < IntegerField
|
|
541
|
-
def encoding
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
667
|
+
def encoding
|
|
668
|
+
:varint
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
def bit_length
|
|
672
|
+
32
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
def signed
|
|
676
|
+
false
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
def wire_type
|
|
680
|
+
0
|
|
681
|
+
end
|
|
545
682
|
end
|
|
546
683
|
|
|
547
684
|
class SInt32Field < IntegerField
|
|
548
|
-
def encoding
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
685
|
+
def encoding
|
|
686
|
+
:zigzag
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
def bit_length
|
|
690
|
+
32
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
def signed
|
|
694
|
+
true
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def wire_type
|
|
698
|
+
0
|
|
699
|
+
end
|
|
552
700
|
end
|
|
553
701
|
|
|
554
702
|
class Fixed32Field < IntegerField
|
|
555
|
-
def encoding
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
def
|
|
703
|
+
def encoding
|
|
704
|
+
:fixed
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def bit_length
|
|
708
|
+
32
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
def signed
|
|
712
|
+
false
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
def wire_type
|
|
716
|
+
5
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
def binary_pack
|
|
720
|
+
"V"
|
|
721
|
+
end
|
|
560
722
|
end
|
|
561
723
|
|
|
562
724
|
class SFixed32Field < IntegerField
|
|
563
|
-
def encoding
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
def
|
|
725
|
+
def encoding
|
|
726
|
+
:fixed
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
def bit_length
|
|
730
|
+
32
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
def signed
|
|
734
|
+
true
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def wire_type
|
|
738
|
+
5
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
def binary_pack
|
|
742
|
+
"l<"
|
|
743
|
+
end
|
|
568
744
|
end
|
|
569
745
|
|
|
570
746
|
class BoolField < UInt64Field
|
|
@@ -592,7 +768,7 @@ module Protobug
|
|
|
592
768
|
end
|
|
593
769
|
|
|
594
770
|
def validate!(value, message)
|
|
595
|
-
raise
|
|
771
|
+
raise InvalidValueError.new(message, self, value, "expected boolean") unless [true, false].include?(value)
|
|
596
772
|
|
|
597
773
|
super(value ? 1 : 0, message)
|
|
598
774
|
end
|
|
@@ -669,9 +845,17 @@ module Protobug
|
|
|
669
845
|
end
|
|
670
846
|
|
|
671
847
|
class DoubleField < Field
|
|
672
|
-
def type
|
|
673
|
-
|
|
674
|
-
|
|
848
|
+
def type
|
|
849
|
+
:double
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
def binary_pack
|
|
853
|
+
"E"
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
def wire_type
|
|
857
|
+
1
|
|
858
|
+
end
|
|
675
859
|
|
|
676
860
|
def initialize(number, name, cardinality:, json_name: name, oneof: nil, packed: false,
|
|
677
861
|
proto3_optional: cardinality == :optional)
|
|
@@ -680,7 +864,7 @@ module Protobug
|
|
|
680
864
|
end
|
|
681
865
|
|
|
682
866
|
def binary_encode_one(value, outbuf)
|
|
683
|
-
[value]
|
|
867
|
+
BinaryEncoding.pack([value], binary_pack, buffer: outbuf)
|
|
684
868
|
end
|
|
685
869
|
|
|
686
870
|
def binary_decode_one(io, _message, _registry, wire_type)
|
|
@@ -700,7 +884,7 @@ module Protobug
|
|
|
700
884
|
-Float::INFINITY
|
|
701
885
|
when "NaN"
|
|
702
886
|
Float::NAN
|
|
703
|
-
when /\A
|
|
887
|
+
when /\A-?(?:\d+(?:\.\d+)?|\.\d+)(?:[eE][-+]?\d+)?\z/
|
|
704
888
|
Float(value)
|
|
705
889
|
when NilClass
|
|
706
890
|
UNSET
|
|
@@ -731,9 +915,17 @@ module Protobug
|
|
|
731
915
|
end
|
|
732
916
|
|
|
733
917
|
class FloatField < DoubleField
|
|
734
|
-
def type
|
|
735
|
-
|
|
736
|
-
|
|
918
|
+
def type
|
|
919
|
+
:float
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
def binary_pack
|
|
923
|
+
"e"
|
|
924
|
+
end
|
|
925
|
+
|
|
926
|
+
def wire_type
|
|
927
|
+
5
|
|
928
|
+
end
|
|
737
929
|
end
|
|
738
930
|
|
|
739
931
|
class GroupField < Field
|
data/lib/protobug/message.rb
CHANGED
|
@@ -11,6 +11,10 @@ module Protobug
|
|
|
11
11
|
"<UNSET>"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
def UNSET.to_s
|
|
15
|
+
"<UNSET>"
|
|
16
|
+
end
|
|
17
|
+
|
|
14
18
|
def UNSET.===(other)
|
|
15
19
|
other.equal? UNSET
|
|
16
20
|
end
|
|
@@ -120,14 +124,11 @@ module Protobug
|
|
|
120
124
|
|
|
121
125
|
def decode(binary, registry:, object: new)
|
|
122
126
|
binary.binmode
|
|
123
|
-
|
|
124
|
-
header = BinaryEncoding.decode_varint(binary)
|
|
125
|
-
break if header.nil?
|
|
126
|
-
|
|
127
|
+
while (header = BinaryEncoding.decode_varint(binary))
|
|
127
128
|
wire_type = header & 0b111
|
|
128
|
-
number =
|
|
129
|
+
number = header >> 3
|
|
129
130
|
|
|
130
|
-
unless number
|
|
131
|
+
unless number > 0
|
|
131
132
|
raise DecodeError,
|
|
132
133
|
"unexpected field number #{number} in #{full_name || fields_by_name.inspect}"
|
|
133
134
|
end
|
|
@@ -156,8 +157,10 @@ module Protobug
|
|
|
156
157
|
message.unknown_fields.each_with_object(buf) do |(number, wire_type, value), outbuf|
|
|
157
158
|
BinaryEncoding.encode_varint((number << 3) | wire_type, outbuf)
|
|
158
159
|
case wire_type
|
|
159
|
-
when 0
|
|
160
|
+
when 0
|
|
160
161
|
BinaryEncoding.encode_varint(value, outbuf)
|
|
162
|
+
when 1, 5
|
|
163
|
+
outbuf << value
|
|
161
164
|
when 2
|
|
162
165
|
BinaryEncoding.encode_length(value, outbuf)
|
|
163
166
|
else
|
|
@@ -167,6 +170,17 @@ module Protobug
|
|
|
167
170
|
end
|
|
168
171
|
|
|
169
172
|
def field(number, name, type:, **kwargs)
|
|
173
|
+
raise ArgumentError unless number.is_a? Integer
|
|
174
|
+
|
|
175
|
+
case name
|
|
176
|
+
when String
|
|
177
|
+
# OK
|
|
178
|
+
when Symbol
|
|
179
|
+
name = name.name
|
|
180
|
+
else
|
|
181
|
+
raise ArgumentError
|
|
182
|
+
end
|
|
183
|
+
|
|
170
184
|
field =
|
|
171
185
|
case type
|
|
172
186
|
when :message
|
|
@@ -222,54 +236,26 @@ module Protobug
|
|
|
222
236
|
fields_by_json_name[name] = field
|
|
223
237
|
fields_by_json_name[field.json_name] = field
|
|
224
238
|
|
|
225
|
-
|
|
226
|
-
return instance_variable_set(field.ivar, UNSET) if value.nil? && field.optional? && field.proto3_optional?
|
|
227
|
-
|
|
228
|
-
field.validate!(value, self)
|
|
229
|
-
instance_variable_set(field.ivar, value)
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
define_method(name) do
|
|
233
|
-
value = instance_variable_get(field.ivar)
|
|
234
|
-
UNSET == value ? field.default : value
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
define_method(field.haser) do
|
|
238
|
-
value = instance_variable_get(field.ivar)
|
|
239
|
-
return false if UNSET == value
|
|
240
|
-
|
|
241
|
-
return false if (!field.optional? || !field.proto3_optional?) && !field.oneof && field.default == value
|
|
242
|
-
|
|
243
|
-
if field.repeated?
|
|
244
|
-
!value.empty?
|
|
245
|
-
else
|
|
246
|
-
true
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
define_method(field.clearer) do
|
|
251
|
-
instance_variable_set(field.ivar, UNSET)
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
field.define_adder(self) if field.repeated?
|
|
239
|
+
class_eval(field.method_definitions, __FILE__, __LINE__)
|
|
255
240
|
|
|
256
241
|
return unless field.oneof
|
|
257
242
|
|
|
258
|
-
unless oneofs[field.oneof]
|
|
259
|
-
oneofs[field.oneof] =
|
|
243
|
+
unless (oneof = oneofs[field.oneof])
|
|
244
|
+
oneofs[field.oneof] = oneof = []
|
|
260
245
|
define_method(field.oneof) do
|
|
261
|
-
|
|
246
|
+
oneof.find { |f| send(f.haser) }&.name
|
|
262
247
|
end
|
|
263
248
|
end
|
|
264
|
-
|
|
249
|
+
oneof << field
|
|
265
250
|
end
|
|
266
251
|
|
|
267
252
|
module InstanceMethods
|
|
268
253
|
def ==(other)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
254
|
+
return false unless other.instance_of?(self.class)
|
|
255
|
+
|
|
256
|
+
self.class.fields_by_name.all? do |name, _|
|
|
257
|
+
send(name) == other.send(name)
|
|
258
|
+
end
|
|
273
259
|
end
|
|
274
260
|
alias eql? ==
|
|
275
261
|
|
|
@@ -291,7 +277,7 @@ module Protobug
|
|
|
291
277
|
pp.breakable
|
|
292
278
|
fields_with_values.each_with_index do |(name, field), idx|
|
|
293
279
|
pp.nest 2 do
|
|
294
|
-
unless idx
|
|
280
|
+
unless idx == 0
|
|
295
281
|
pp.text ","
|
|
296
282
|
pp.breakable " "
|
|
297
283
|
end
|
|
@@ -303,7 +289,11 @@ module Protobug
|
|
|
303
289
|
end
|
|
304
290
|
|
|
305
291
|
def hash
|
|
306
|
-
|
|
292
|
+
# Build a single array (map result is mutated in place via unshift) rather
|
|
293
|
+
# than allocating both the mapped array and a second spread literal.
|
|
294
|
+
values = self.class.fields_by_name.map { |name, _| send(name) }
|
|
295
|
+
values.unshift(self.class)
|
|
296
|
+
values.hash
|
|
307
297
|
end
|
|
308
298
|
|
|
309
299
|
def to_text
|
data/lib/protobug/version.rb
CHANGED
data/lib/protobug.rb
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "protobug/version"
|
|
4
|
-
|
|
5
3
|
module Protobug
|
|
6
4
|
class Error < StandardError; end
|
|
7
5
|
# Your code goes here...
|
|
8
6
|
end
|
|
9
7
|
|
|
8
|
+
require_relative "protobug/version"
|
|
10
9
|
require_relative "protobug/base_descriptor"
|
|
11
10
|
require_relative "protobug/enum"
|
|
12
11
|
require_relative "protobug/message"
|
|
13
12
|
require_relative "protobug/registry"
|
|
14
|
-
require "date"
|
metadata
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: protobug
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Giddins
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-06-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
|
-
description:
|
|
13
|
+
description:
|
|
14
14
|
email:
|
|
15
15
|
- segiddins@segiddins.me
|
|
16
16
|
executables: []
|
|
@@ -38,7 +38,7 @@ metadata:
|
|
|
38
38
|
homepage_uri: https://github.com/segiddins/protobug
|
|
39
39
|
source_code_uri: https://github.com/segiddins/protobug
|
|
40
40
|
rubygems_mfa_required: 'true'
|
|
41
|
-
post_install_message:
|
|
41
|
+
post_install_message:
|
|
42
42
|
rdoc_options: []
|
|
43
43
|
require_paths:
|
|
44
44
|
- lib
|
|
@@ -54,7 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
54
54
|
version: '0'
|
|
55
55
|
requirements: []
|
|
56
56
|
rubygems_version: 3.5.9
|
|
57
|
-
signing_key:
|
|
57
|
+
signing_key:
|
|
58
58
|
specification_version: 4
|
|
59
59
|
summary: An embeddable protobuf compiler & runtime for Ruby
|
|
60
60
|
test_files: []
|