Module: Arf::Proto
- Defined in:
- lib/arf/proto/map.rb,
lib/arf/proto/array.rb,
lib/arf/proto/bytes.rb,
lib/arf/proto/float.rb,
lib/arf/proto/types.rb,
lib/arf/proto/union.rb,
lib/arf/proto/scalar.rb,
lib/arf/proto/string.rb,
lib/arf/proto/struct.rb,
lib/arf/proto/boolean.rb,
lib/arf/proto/decoder.rb,
lib/arf/proto/encoder.rb,
lib/arf/proto/registry.rb
Defined Under Namespace
Modules: Registry
Constant Summary collapse
- EMPTY_MAP_MASK =
0x01 << 4
- ARRAY_EMPTY_MASK =
0x01 << 4
- BYTES_EMPTY_MASK =
0x01 << 4
- FLOAT64_MASK =
0x01 << 4
- FLOAT_EMPTY_MASK =
0x01 << 5
- FLOAT32_MIN_VALUE =
-3.4028235e38
- FLOAT32_MAX_VALUE =
3.4028235e38
- TYPE_VOID =
0b0000
- TYPE_SCALAR =
0b0001
- TYPE_BOOLEAN =
0b0010
- TYPE_FLOAT =
0b0011
- TYPE_STRING =
0b0100
- TYPE_BYTES =
0b0101
- TYPE_ARRAY =
0b0110
- TYPE_MAP =
0b0111
- TYPE_STRUCT =
0b1000
- TYPE_UNION =
0b1001
- ALL_PRIMITIVES =
[ TYPE_VOID, TYPE_SCALAR, TYPE_BOOLEAN, TYPE_FLOAT, TYPE_STRING, TYPE_BYTES, TYPE_ARRAY, TYPE_MAP, TYPE_STRUCT, TYPE_UNION ].freeze
- TYPE_NAME =
{ TYPE_VOID => "Void", TYPE_SCALAR => "Scalar", TYPE_BOOLEAN => "Boolean", TYPE_FLOAT => "Float", TYPE_STRING => "String", TYPE_BYTES => "Bytes", TYPE_ARRAY => "Array", TYPE_MAP => "Map", TYPE_STRUCT => "Struct", TYPE_UNION => "Union" }.freeze
- SIMPLE_PRIMITIVES =
{ void: TYPE_VOID, uint8: TYPE_SCALAR, uint16: TYPE_SCALAR, uint32: TYPE_SCALAR, uint64: TYPE_SCALAR, int8: TYPE_SCALAR, int16: TYPE_SCALAR, int32: TYPE_SCALAR, int64: TYPE_SCALAR, bool: TYPE_BOOLEAN, float32: TYPE_FLOAT, float64: TYPE_FLOAT, string: TYPE_STRING, bytes: TYPE_BYTES }.freeze
- NUMERIC_SIGNED_MASK =
0x01 << 4
- NUMERIC_ZERO_MASK =
0x01 << 5
- NUMERIC_NEGATIVE_MASK =
0x01 << 6
- STRING_EMPTY_MASK =
0x01 << 4
- BOOL_FLAG_MASK =
0x01 << 4
Class Method Summary collapse
- .decode(io) ⇒ Object
- .decode_array(header, io) ⇒ Object
- .decode_boolean(header, _io) ⇒ Object
- .decode_bytes(header, io) ⇒ Object
- .decode_float(header, io) ⇒ Object
- .decode_map(header, io) ⇒ Object
- .decode_scalar(header, io) ⇒ Object
- .decode_string(header, io) ⇒ Object
- .decode_struct(_header, io) ⇒ Object
- .decode_uint64(io) ⇒ Object
- .decode_union(_header, io) ⇒ Object
- .encode(value) ⇒ Object
- .encode_array(v) ⇒ Object
- .encode_as(value, type) ⇒ Object
- .encode_boolean(b) ⇒ Object
- .encode_bytes(b) ⇒ Object
- .encode_float32(value) ⇒ Object
- .encode_float64(value) ⇒ Object
- .encode_map(v) ⇒ Object
- .encode_scalar(v, signed: false) ⇒ Object
- .encode_string(s) ⇒ Object
- .encode_struct(v) ⇒ Object
- .encode_uint64(v) ⇒ Object
- .encode_union(v) ⇒ Object
- .fields_from_struct(v) ⇒ Object
- .read_type(io) ⇒ Object
Class Method Details
.decode(io) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/arf/proto/decoder.rb', line 5 def self.decode(io) type, header = read_type(io) case type when TYPE_VOID nil when TYPE_SCALAR decode_scalar(header, io) when TYPE_BOOLEAN decode_boolean(header, io) when TYPE_FLOAT decode_float(header, io) when TYPE_STRING decode_string(header, io) when TYPE_BYTES decode_bytes(header, io) when TYPE_ARRAY decode_array(header, io) when TYPE_MAP decode_map(header, io) when TYPE_STRUCT decode_struct(header, io) when TYPE_UNION decode_union(header, io) end end |
.decode_array(header, io) ⇒ Object
20 21 22 23 24 25 26 27 28 29 |
# File 'lib/arf/proto/array.rb', line 20 def self.decode_array(header, io) return [] if header.anybits?(ARRAY_EMPTY_MASK) len = decode_uint64(io) arr = [] len.times do arr << decode(io) end arr end |
.decode_boolean(header, _io) ⇒ Object
13 |
# File 'lib/arf/proto/boolean.rb', line 13 def self.decode_boolean(header, _io) = header.allbits?(BOOL_FLAG_MASK) |
.decode_bytes(header, io) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/arf/proto/bytes.rb', line 22 def self.decode_bytes(header, io) return "" if header.anybits?(BYTES_EMPTY_MASK) size = decode_uint64(io) data = StringIO.new until size.zero? read = io.readpartial(size) data.write(read) size -= read.length end data.string end |
.decode_float(header, io) ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/arf/proto/float.rb', line 36 def self.decode_float(header, io) return 0.0 if header.anybits?(FLOAT_EMPTY_MASK) bits = header.nobits?(FLOAT64_MASK) ? 32 : 64 data = io.read(bits / 8) if bits == 32 data.unpack("L>").pack("L>").unpack1("g") else data.unpack("Q>").pack("Q>").unpack1("G") end end |
.decode_map(header, io) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/arf/proto/map.rb', line 34 def self.decode_map(header, io) return {} if header.anybits?(EMPTY_MAP_MASK) decode_uint64(io) # Discard full length pairs_len = decode_uint64(io) keys = [] values = [] pairs_len.times { keys << decode(io) } pairs_len.times { values << decode(io) } keys.zip(values).to_h end |
.decode_scalar(header, io) ⇒ Object
24 25 26 27 28 29 30 |
# File 'lib/arf/proto/scalar.rb', line 24 def self.decode_scalar(header, io) return 0 if header.anybits?(NUMERIC_ZERO_MASK) v = decode_uint64(io) v *= -1 if header.anybits?(NUMERIC_NEGATIVE_MASK) v end |
.decode_string(header, io) ⇒ Object
23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/arf/proto/string.rb', line 23 def self.decode_string(header, io) return "" if header.anybits?(STRING_EMPTY_MASK) size = decode_uint64(io) data = StringIO.new until size.zero? read = io.readpartial(size) data.write(read) size -= read.length end data.string.encode("UTF-8") end |
.decode_struct(_header, io) ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/arf/proto/struct.rb', line 58 def self.decode_struct(_header, io) id_type, id_header = read_type(io) if id_type != TYPE_STRING # :nocov: raise DecodeFailedError, "cannot decode struct: expected String, found #{TYPE_NAME[id_type]}" # :nocov: end struct_id = decode_string(id_header, io) bytes_len = decode_uint64(io) reader = IO::LimitReader.new(io, bytes_len) fields = {} loop do id = decode_uint64(reader) fields[id] = decode(reader) rescue EOFError break end = Registry.find(struct_id) raise UnknownMeessageError, "Unknown message ID #{struct_id}" unless inst = [:type].new inst.decode_fields(fields) end |
.decode_uint64(io) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/arf/proto/scalar.rb', line 42 def self.decode_uint64(io) x = 0 s = 0 b = 0 loop do b = io.read(1).getbyte(0) return (x | (b << s)) if b < 0x80 x |= ((b & 0x7f) << s) s += 7 end end |
.decode_union(_header, io) ⇒ Object
17 18 19 20 21 22 23 |
# File 'lib/arf/proto/union.rb', line 17 def self.decode_union(_header, io) { union: true, id: decode_uint64(io), value: decode(io) } end |
.encode(value) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/arf/proto/encoder.rb', line 5 def self.encode(value) case value when NilClass [TYPE_VOID].pack("C*") when String, Symbol encode_string(value.to_s) when TrueClass, FalseClass encode_boolean(value) when Float if value.between?(FLOAT32_MIN_VALUE, FLOAT32_MAX_VALUE) encode_float32(value) else encode_float64(value) end when Integer encode_scalar(value, signed: value.negative?) when Array encode_array(value) when Hash encode_map(value) else unless value.class.ancestors.include? Arf::RPC::Struct raise InvalidEncodingTypeError, "Unable to encode value of type #{value.class.name}" end encode_struct(value) end end |
.encode_array(v) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/arf/proto/array.rb', line 7 def self.encode_array(v) b = IO::Buffer.new t = TYPE_ARRAY if v.empty? t |= ARRAY_EMPTY_MASK return b.write(t).string if v.empty? end b.write(t).write_raw(encode_uint64(v.length)) v.each { b.write_raw(encode(_1)) } b.string end |
.encode_as(value, type) ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/arf/proto/encoder.rb', line 35 def self.encode_as(value, type) return [TYPE_VOID].pack("C*") if value.nil? case type when :uint8, :uint16, :uint32, :uint64 encode_scalar(value, signed: false) when :int8, :int16, :int32, :int64 encode_scalar(value, signed: true) when :float32 encode_float32(value) when :float64 encode_float64(value) when :bool encode_boolean(value) when :string encode_string(value) when :bytes encode_bytes(value) else if type.is_a?(Arf::Types::MapType) encode_map(value) elsif type.is_a?(Arf::Types::ArrayType) encode_array(value) elsif type.is_a?(String) && value.class.ancestors.include?(Arf::RPC::Struct) encode_struct(value) elsif type.is_a?(Class) && type.ancestors.include?(Arf::RPC::Struct) encode_struct(value) elsif type.is_a?(Class) && type.ancestors.include?(Arf::RPC::Enum) encode_scalar(type.to_i(value), signed: false) else raise InvalidEncodingTypeError, "Unable to encode value of type #{value.class.name} (reported type is #{type.inspect})" end end end |
.encode_boolean(b) ⇒ Object
7 8 9 10 11 |
# File 'lib/arf/proto/boolean.rb', line 7 def self.encode_boolean(b) v = TYPE_BOOLEAN v |= BOOL_FLAG_MASK if b [v].pack("C*") end |
.encode_bytes(b) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/arf/proto/bytes.rb', line 7 def self.encode_bytes(b) v = TYPE_BYTES if b.empty? v |= BYTES_EMPTY_MASK return [v].pack("C*") end IO::Buffer.new .write(v) .write_raw(encode_uint64(b.length)) .write_raw(b) .string end |
.encode_float32(value) ⇒ Object
10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/arf/proto/float.rb', line 10 def self.encode_float32(value) t = TYPE_FLOAT if value.zero? t |= FLOAT_EMPTY_MASK return [t].pack("C*") end [ [t].pack("C*"), [value].pack("g").unpack("N").pack("L>") ].join end |
.encode_float64(value) ⇒ Object
23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/arf/proto/float.rb', line 23 def self.encode_float64(value) t = TYPE_FLOAT | FLOAT64_MASK if value.zero? t |= FLOAT_EMPTY_MASK return [t].pack("C*") end [ [t].pack("C*"), [value].pack("G").unpack("Q>").pack("Q>") ].join end |
.encode_map(v) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/arf/proto/map.rb', line 7 def self.encode_map(v) t = TYPE_MAP if v.empty? t |= EMPTY_MAP_MASK return [t].pack("C*") end keys = [] values = [] v.each_pair do |key, value| keys << encode(key) values << encode(value) end encoded_len = encode_uint64(v.length) keys = keys.join values = values.join IO::Buffer.new .write(t) .write_raw(encode_uint64(keys.length + values.length + encoded_len.length)) .write_raw(encoded_len) .write_raw(keys) .write_raw(values) .string end |
.encode_scalar(v, signed: false) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/arf/proto/scalar.rb', line 9 def self.encode_scalar(v, signed: false) type = TYPE_SCALAR type |= NUMERIC_SIGNED_MASK if signed type |= NUMERIC_ZERO_MASK if v.zero? if v.negative? type |= NUMERIC_NEGATIVE_MASK v *= -1 end [ [type].pack("C*"), v.zero? ? nil : encode_uint64(v) ].compact.join end |
.encode_string(s) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/arf/proto/string.rb', line 7 def self.encode_string(s) t = TYPE_STRING if s.nil? || s.empty? t |= STRING_EMPTY_MASK return [t].pack("C*") end s = s.to_s.encode("UTF-8") [ [t].pack("C*"), encode_uint64(s.bytesize), s ].join end |
.encode_struct(v) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/arf/proto/struct.rb', line 37 def self.encode_struct(v) return encode_union(v) if v.union? struct_id = v.arf_struct_id fields = fields_from_struct(v) data = [] fields.each do |f| data << encode_uint64(f[:id]) data << encode_as(v.instance_variable_get("@#{f[:name]}"), f[:type]) end payload = data.join [ [TYPE_STRUCT].pack("C*"), encode_string(struct_id), encode_uint64(payload.length), payload ].join end |
.encode_uint64(v) ⇒ Object
32 33 34 35 36 37 38 39 40 |
# File 'lib/arf/proto/scalar.rb', line 32 def self.encode_uint64(v) bytes = [] while v >= 0x80 bytes << ((v & 0xFF) | 0x80) v >>= 7 end bytes << v bytes.pack("C*") end |
.encode_union(v) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 |
# File 'lib/arf/proto/union.rb', line 5 def self.encode_union(v) selected = v.__arf_union_set_id f = fields_from_struct(v).find { _1[:id] == selected } payload = encode_as(v.instance_variable_get("@#{f[:name]}"), f[:type]) [ [TYPE_UNION].pack("C*"), encode_uint64(selected), payload ].join end |
.fields_from_struct(v) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/arf/proto/struct.rb', line 5 def self.fields_from_struct(v) base = v.is_a?(Class) ? v : v.class fields = [] base.fields.each do |f| if f[:id].is_a? Integer fields << if f[:type].is_a?(Symbol) || f[:type].is_a?(Arf::Types::ArrayType) || f[:type].is_a?(Arf::Types::MapType) f else { id: f[:id], name: f[:name], type: base.find_type(f[:type]) } end else raise UnsupportedNestedUnionError, "Nested unions are not supported" if base.union? union_type = base.find_type(f[:type]) fields << { id: union_type.fields.first[:id], type: union_type, name: f[:name] } end end fields.sort_by! { _1[:id] } fields end |
.read_type(io) ⇒ Object
59 60 61 62 63 64 65 |
# File 'lib/arf/proto/types.rb', line 59 def self.read_type(io) b = io.read(1).getbyte(0) decoded = b & 0xF raise UnknownTypeError, format("Unknown type 0x%02x", b) unless ALL_PRIMITIVES.include? decoded [decoded, b] end |