arf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/LICENSE.txt +21 -0
  4. data/Rakefile +12 -0
  5. data/lib/arf/configuration.rb +67 -0
  6. data/lib/arf/context.rb +170 -0
  7. data/lib/arf/errors.rb +10 -0
  8. data/lib/arf/io/buffer.rb +25 -0
  9. data/lib/arf/io/compression.rb +44 -0
  10. data/lib/arf/io/limit_reader.rb +30 -0
  11. data/lib/arf/observer.rb +29 -0
  12. data/lib/arf/proto/array.rb +31 -0
  13. data/lib/arf/proto/boolean.rb +15 -0
  14. data/lib/arf/proto/bytes.rb +35 -0
  15. data/lib/arf/proto/decoder.rb +31 -0
  16. data/lib/arf/proto/encoder.rb +71 -0
  17. data/lib/arf/proto/float.rb +48 -0
  18. data/lib/arf/proto/map.rb +49 -0
  19. data/lib/arf/proto/registry.rb +27 -0
  20. data/lib/arf/proto/scalar.rb +55 -0
  21. data/lib/arf/proto/string.rb +36 -0
  22. data/lib/arf/proto/struct.rb +84 -0
  23. data/lib/arf/proto/types.rb +67 -0
  24. data/lib/arf/proto/union.rb +25 -0
  25. data/lib/arf/proto.rb +17 -0
  26. data/lib/arf/reactor.rb +270 -0
  27. data/lib/arf/rpc/base_message.rb +119 -0
  28. data/lib/arf/rpc/client_base.rb +110 -0
  29. data/lib/arf/rpc/end_stream.rb +9 -0
  30. data/lib/arf/rpc/enum.rb +51 -0
  31. data/lib/arf/rpc/message_kind.rb +27 -0
  32. data/lib/arf/rpc/metadata.rb +120 -0
  33. data/lib/arf/rpc/method_meta.rb +42 -0
  34. data/lib/arf/rpc/request.rb +39 -0
  35. data/lib/arf/rpc/responder.rb +186 -0
  36. data/lib/arf/rpc/response.rb +46 -0
  37. data/lib/arf/rpc/service_base.rb +137 -0
  38. data/lib/arf/rpc/start_stream.rb +9 -0
  39. data/lib/arf/rpc/stream_error.rb +23 -0
  40. data/lib/arf/rpc/stream_item.rb +16 -0
  41. data/lib/arf/rpc/stream_metadata.rb +16 -0
  42. data/lib/arf/rpc/struct.rb +255 -0
  43. data/lib/arf/rpc.rb +19 -0
  44. data/lib/arf/server.rb +123 -0
  45. data/lib/arf/status.rb +75 -0
  46. data/lib/arf/types/array_type.rb +24 -0
  47. data/lib/arf/types/base_type.rb +14 -0
  48. data/lib/arf/types/coercion.rb +36 -0
  49. data/lib/arf/types/in_out_stream.rb +28 -0
  50. data/lib/arf/types/input_stream.rb +8 -0
  51. data/lib/arf/types/map_type.rb +32 -0
  52. data/lib/arf/types/mixin.rb +29 -0
  53. data/lib/arf/types/output_stream.rb +8 -0
  54. data/lib/arf/types/streamer.rb +21 -0
  55. data/lib/arf/types.rb +69 -0
  56. data/lib/arf/version.rb +5 -0
  57. data/lib/arf/wire/base_connection.rb +177 -0
  58. data/lib/arf/wire/client.rb +101 -0
  59. data/lib/arf/wire/encoding.rb +49 -0
  60. data/lib/arf/wire/error_code.rb +35 -0
  61. data/lib/arf/wire/errors.rb +88 -0
  62. data/lib/arf/wire/frame.rb +111 -0
  63. data/lib/arf/wire/frame_kind.rb +23 -0
  64. data/lib/arf/wire/frame_reader.rb +104 -0
  65. data/lib/arf/wire/frames/base_frame.rb +108 -0
  66. data/lib/arf/wire/frames/configuration_frame.rb +33 -0
  67. data/lib/arf/wire/frames/data_frame.rb +21 -0
  68. data/lib/arf/wire/frames/go_away_frame.rb +29 -0
  69. data/lib/arf/wire/frames/make_stream_frame.rb +15 -0
  70. data/lib/arf/wire/frames/ping_frame.rb +18 -0
  71. data/lib/arf/wire/frames/reset_stream_frame.rb +19 -0
  72. data/lib/arf/wire/frames.rb +9 -0
  73. data/lib/arf/wire/server/peer.rb +85 -0
  74. data/lib/arf/wire/server.rb +63 -0
  75. data/lib/arf/wire/stream/state.rb +69 -0
  76. data/lib/arf/wire/stream.rb +128 -0
  77. data/lib/arf/wire/wait_signal.rb +24 -0
  78. data/lib/arf/wire.rb +14 -0
  79. data/lib/arf.rb +46 -0
  80. metadata +195 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8268851fe0bf2d0252c65f9a42b58a5e1335f5d5a5bdff839ff3961a1e530e69
4
+ data.tar.gz: 1ba718db7a6a8372c1cb813437c1bd17e6925456eca5c3709d15b48f8a7567d0
5
+ SHA512:
6
+ metadata.gz: fd37152c07e7c8f78c57e5f4dcc7530f18e2d61fc615978992f0fae6c98e2ddd65ac544974da2c153394f724e774f78c3367ca0046b3a387b01e9d2134ec2d73
7
+ data.tar.gz: ff4bc35c94c938c3c0a9b904ec33fbabad4428bd418d922d6f7e12876f8ce1262b5070e9bbde81a1140a9185aceda39dbe1569fed154398120e2a5d55d5cbac4
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.5
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Vito Sartori
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ class Configuration
5
+ TLS_OPTS = {
6
+ private_key_file: :tls_private_key_file,
7
+ private_key: :tls_private_key,
8
+ private_key_pass: :tls_private_key_pass,
9
+ cert_chain_file: :tls_cert_chain_file,
10
+ cert: :tls_cert,
11
+ verify_peer: :tls_verify_peer,
12
+ sni_hostname: :tls_sni_hostname,
13
+ cipher_list: :tls_cipher_list,
14
+ ssl_version: :tls_ssl_version,
15
+ ecdh_curve: :tls_ecdh_curve,
16
+ dhparam: :tls_dhparam,
17
+ fail_if_no_peer_cert: :tls_fail_if_no_peer_cert
18
+ }.freeze
19
+
20
+ attr_accessor :bind_address, :bind_port,
21
+ :enable_tls, :tls_private_key_file, :tls_private_key,
22
+ :tls_private_key_pass, :tls_cert_chain_file, :tls_cert,
23
+ :tls_verify_peer, :tls_sni_hostname, :tls_cipher_list,
24
+ :tls_ssl_version, :tls_ecdh_curve, :tls_dhparam,
25
+ :tls_fail_if_no_peer_cert,
26
+ :client_compression
27
+ attr_writer :logger
28
+
29
+ # Determines the log verbosity level. Valid options are:
30
+ # - :debug
31
+ # - :info (default)
32
+ # - :warn
33
+ # - :fatal
34
+ # - :error
35
+ attr_reader :log_level
36
+
37
+ def initialize
38
+ @bind_address = "127.0.0.1"
39
+ @bind_port = 2730
40
+ @log_level = :info
41
+ end
42
+
43
+ def tls_configuration
44
+ return @tls_configuration unless @tls_configuration.nil?
45
+
46
+ @tls_configuration = TLS_OPTS
47
+ .map { |k, v| [k, send(v)] }
48
+ .compact
49
+ .to_h
50
+ end
51
+
52
+ def enable_tls? = enable_tls
53
+
54
+ def logger
55
+ @logger ||= Logrb.new($stderr, level: @log_level)
56
+ end
57
+
58
+ def log_level=(level)
59
+ @log_level = level
60
+ logger.level = level
61
+ end
62
+
63
+ def self.instance
64
+ @instance ||= new
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ class Context
5
+ attr_accessor :request_id, :log, :stream, :request,
6
+ :has_recv_stream, :recv_stream_error, :recv_stream_started,
7
+ :has_send_stream, :send_stream_error, :send_stream_started, :send_stream_type, :send_stream_finished,
8
+ :has_sent_response, :response, :error
9
+
10
+ def initialize(req_id, log, stream)
11
+ @request_id = req_id
12
+ @log = log
13
+ @stream = stream
14
+
15
+ @request = nil
16
+ @error = nil
17
+
18
+ @has_recv_stream = false
19
+ @recv_stream_error = nil
20
+ @recv_stream_started_lock = Monitor.new
21
+ @recv_stream_started = false
22
+
23
+ @has_send_stream = false
24
+ @send_stream_error = nil
25
+ @send_stream_started = false
26
+ @send_stream_started_lock = Monitor.new
27
+ @send_stream_finished = false
28
+ @send_stream_type = nil
29
+
30
+ @has_sent_response = false
31
+ @response = Arf::RPC::Response.new
32
+ end
33
+
34
+ def prepare
35
+ @has_recv_stream = @request.streaming
36
+ @response.metadata.set("arf-request-id", @request_id)
37
+ end
38
+
39
+ def recv
40
+ raise RPC::NoStreamError, true unless has_recv_stream
41
+ raise @error if @error
42
+ raise @recv_stream_error if @recv_stream_error
43
+
44
+ loop do
45
+ v = _read_stream_item
46
+ return v if v
47
+ end
48
+ end
49
+
50
+ def stream_send(val)
51
+ raise RPC::NoStreamError, false unless @has_send_stream
52
+ raise @error if @error
53
+ raise @send_stream_error if @send_stream_error
54
+
55
+ unless has_sent_response
56
+ # Arf::RPC::Service makes sure this method can be called now. If it
57
+ # allowed calling without a response being sent, it is acceptable to
58
+ # push a response as-is, as the client does not expect parameters as
59
+ # response other than the stream being sent.
60
+ @log.debug("Pushing synthetic response for method without response values")
61
+ @response.status = :ok
62
+ @response.streaming = true
63
+ @response.params = []
64
+ send_response
65
+ end
66
+
67
+ begin
68
+ unless @send_stream_started
69
+ @send_stream_started_lock.synchronize do
70
+ next if @send_stream_started
71
+
72
+ @log.debug("Pushing StartStream frame")
73
+ @stream.write_data(RPC::BaseMessage.encode(RPC::StartStream.new))
74
+ @send_stream_started = true
75
+ end
76
+ end
77
+
78
+ @log.debug("Pushing StreamItem frame")
79
+ @stream.write_data(RPC::BaseMessage.encode(RPC::StreamItem.new(value: val)))
80
+ rescue StandardError => e
81
+ @send_stream_error = e
82
+ raise
83
+ end
84
+ end
85
+
86
+ def send_stream_metadata
87
+ @stream.write_data(RPC::BaseMessage.encode(RPC::StreamMetadata.new(metadata: @response.metadata)))
88
+ end
89
+
90
+ def end_send
91
+ return if @send_stream_finished
92
+ raise RPC::NoStreamError, false unless @has_send_stream
93
+ raise @error if @error
94
+ raise @send_stream_error if @send_stream_error
95
+
96
+ @send_stream_finished = true
97
+
98
+ begin
99
+ @stream.write_data(RPC::BaseMessage.encode(RPC::EndStream.new), end_stream: true)
100
+ rescue StandardError => e
101
+ @log.error("Failed running #end_send", e)
102
+ @error = e
103
+ raise
104
+ end
105
+ end
106
+
107
+ def send_response(end_stream: false)
108
+ @log.debug("Sending response", response: @response)
109
+
110
+ @has_sent_response = true
111
+ @send_stream_started = false
112
+
113
+ @response.status ||= :ok
114
+ @log.debug("Start stream write data")
115
+ @stream.write_data(RPC::BaseMessage.encode(@response), end_stream:)
116
+ @log.debug("Send response done")
117
+ rescue Exception => e
118
+ @log.error("Failed sending response", e)
119
+ @error = e
120
+ raise
121
+ end
122
+
123
+ private
124
+
125
+ def _read_stream_item
126
+ begin
127
+ unless @recv_stream_started
128
+ @recv_stream_started_lock.synchronize do
129
+ next if @recv_stream_started
130
+
131
+ msg = RPC::BaseMessage.initialize_from(@stream.read_blocking)
132
+ if msg.is_a?(RPC::StartStream)
133
+ @recv_stream_started = true
134
+ else
135
+ @error = RPC::StreamFailureError.new("Received unexpected message kind waiting for StartStream")
136
+ raise @error
137
+ end
138
+ end
139
+ end
140
+
141
+ msg = RPC::BaseMessage.initialize_from(@stream.read_blocking)
142
+ rescue StandardError => e
143
+ @log.error("Failed starting recv_stream", e)
144
+ @recv_stream_error = e
145
+ raise
146
+ end
147
+
148
+ case msg
149
+ when RPC::StreamItem
150
+ msg.value
151
+
152
+ when RPC::EndStream
153
+ @recv_stream_error = RPC::StreamEndError.new
154
+ raise @recv_stream_error
155
+
156
+ when RPC::StreamError
157
+ @recv_stream_error = msg
158
+ raise @recv_stream_error
159
+
160
+ when RPC::StreamMetadata
161
+ @request.metadata.replace!(msg.metadata)
162
+ nil
163
+
164
+ else
165
+ @error = RPC::StreamFailureError.new("Received unexpected message kind during stream")
166
+ raise @error
167
+ end
168
+ end
169
+ end
170
+ end
data/lib/arf/errors.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ class Error < StandardError; end
5
+ class UnknownTypeError < Error; end
6
+ class InvalidEncodingTypeError < Error; end
7
+ class UnsupportedNestedUnionError < Error; end
8
+ class DecodeFailedError < Error; end
9
+ class UnknownMeessageError < Error; end
10
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module IO
5
+ class Buffer
6
+ def initialize
7
+ @buf = StringIO.new
8
+ end
9
+
10
+ def write(v) = tap { write_raw([v].pack("C*")) }
11
+ def write_raw(v) = tap { @buf.write(v) }
12
+ def reset = @buf.tap(&:rewind).truncate(0)
13
+ def length = @buf.length
14
+ def rewind = @buf.rewind
15
+ def read(*) = @buf.read(*)
16
+ def string = @buf.tap(&:rewind).string
17
+
18
+ def extract
19
+ buf = @buf
20
+ @buf = StringIO.new
21
+ buf
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module IO
5
+ class BaseCompressor
6
+ def self.compress(value)
7
+ return nil if value.nil?
8
+
9
+ value = value.string if value.is_a? StringIO
10
+ value = do_compress(value)
11
+ value.is_a?(StringIO) ? value : StringIO.new(value)
12
+ end
13
+
14
+ def self.decompress(value)
15
+ return nil if value.nil?
16
+
17
+ value = value.string if value.is_a? StringIO
18
+ value = do_decompress(value)
19
+ value.is_a?(StringIO) ? value : StringIO.new(value)
20
+ end
21
+ end
22
+
23
+ class NoneCompressor
24
+ def self.compress(value) = value
25
+ def self.decompress(value) = value
26
+ end
27
+
28
+ class GzipCompressor < BaseCompressor
29
+ def self.do_compress(value) = Zlib::Deflate.deflate(value)
30
+ def self.do_decompress(value) = Zlib::Inflate.inflate(value)
31
+ end
32
+
33
+ class BrotliCompressor < BaseCompressor
34
+ def self.do_compress(value) = Brotli.deflate(value)
35
+ def self.do_decompress(value) = Brotli.inflate(value)
36
+ end
37
+
38
+ COMPRESSOR = {
39
+ none: NoneCompressor,
40
+ gzip: GzipCompressor,
41
+ brotli: BrotliCompressor
42
+ }.freeze
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module IO
5
+ class LimitReader
6
+ def initialize(io, size)
7
+ @io = io
8
+ @left = size
9
+ end
10
+
11
+ def readpartial(size)
12
+ size = normalize_size(size)
13
+ @io.readpartial(size).tap { @left -= _1.length }
14
+ end
15
+
16
+ def read(size)
17
+ size = normalize_size(size)
18
+ @io.read(size).tap { @left -= _1.length }
19
+ end
20
+
21
+ private
22
+
23
+ def normalize_size(size)
24
+ raise EOFError if @left.zero?
25
+
26
+ [@left, size].min
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ class Observer
5
+ def initialize
6
+ @handler = nil
7
+ @primed = false
8
+ @mutex = Monitor.new
9
+ end
10
+
11
+ def attach_handler(handler)
12
+ @handler = handler
13
+ end
14
+
15
+ def prime
16
+ @primed = true
17
+ end
18
+
19
+ def modify
20
+ @mutex.synchronize do
21
+ yield
22
+ if @primed
23
+ @primed = false
24
+ Arf::Reactor.post { @handler.observer_changed(self) }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ ARRAY_EMPTY_MASK = 0x01 << 4
6
+
7
+ def self.encode_array(v)
8
+ b = IO::Buffer.new
9
+ t = TYPE_ARRAY
10
+ if v.empty?
11
+ t |= ARRAY_EMPTY_MASK
12
+ return b.write(t).string if v.empty?
13
+ end
14
+
15
+ b.write(t).write_raw(encode_uint64(v.length))
16
+ v.each { b.write_raw(encode(_1)) }
17
+ b.string
18
+ end
19
+
20
+ def self.decode_array(header, io)
21
+ return [] if header.anybits?(ARRAY_EMPTY_MASK)
22
+
23
+ len = decode_uint64(io)
24
+ arr = []
25
+ len.times do
26
+ arr << decode(io)
27
+ end
28
+ arr
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ BOOL_FLAG_MASK = 0x01 << 4
6
+
7
+ def self.encode_boolean(b)
8
+ v = TYPE_BOOLEAN
9
+ v |= BOOL_FLAG_MASK if b
10
+ [v].pack("C*")
11
+ end
12
+
13
+ def self.decode_boolean(header, _io) = header.allbits?(BOOL_FLAG_MASK)
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ BYTES_EMPTY_MASK = 0x01 << 4
6
+
7
+ def self.encode_bytes(b)
8
+ v = TYPE_BYTES
9
+
10
+ if b.empty?
11
+ v |= BYTES_EMPTY_MASK
12
+ return [v].pack("C*")
13
+ end
14
+
15
+ IO::Buffer.new
16
+ .write(v)
17
+ .write_raw(encode_uint64(b.length))
18
+ .write_raw(b)
19
+ .string
20
+ end
21
+
22
+ def self.decode_bytes(header, io)
23
+ return "" if header.anybits?(BYTES_EMPTY_MASK)
24
+
25
+ size = decode_uint64(io)
26
+ data = StringIO.new
27
+ until size.zero?
28
+ read = io.readpartial(size)
29
+ data.write(read)
30
+ size -= read.length
31
+ end
32
+ data.string
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ def self.decode(io)
6
+ type, header = read_type(io)
7
+ case type
8
+ when TYPE_VOID
9
+ nil
10
+ when TYPE_SCALAR
11
+ decode_scalar(header, io)
12
+ when TYPE_BOOLEAN
13
+ decode_boolean(header, io)
14
+ when TYPE_FLOAT
15
+ decode_float(header, io)
16
+ when TYPE_STRING
17
+ decode_string(header, io)
18
+ when TYPE_BYTES
19
+ decode_bytes(header, io)
20
+ when TYPE_ARRAY
21
+ decode_array(header, io)
22
+ when TYPE_MAP
23
+ decode_map(header, io)
24
+ when TYPE_STRUCT
25
+ decode_struct(header, io)
26
+ when TYPE_UNION
27
+ decode_union(header, io)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ def self.encode(value)
6
+ case value
7
+ when NilClass
8
+ [TYPE_VOID].pack("C*")
9
+ when String, Symbol
10
+ encode_string(value.to_s)
11
+ when TrueClass, FalseClass
12
+ encode_boolean(value)
13
+ when Float
14
+ if value.between?(FLOAT32_MIN_VALUE, FLOAT32_MAX_VALUE)
15
+ encode_float32(value)
16
+ else
17
+ encode_float64(value)
18
+ end
19
+ when Integer
20
+ encode_scalar(value, signed: value.negative?)
21
+ when Array
22
+ encode_array(value)
23
+ when Hash
24
+ encode_map(value)
25
+ else
26
+ unless value.class.ancestors.include? Arf::RPC::Struct
27
+ raise InvalidEncodingTypeError, "Unable to encode value of type #{value.class.name}"
28
+ end
29
+
30
+ encode_struct(value)
31
+
32
+ end
33
+ end
34
+
35
+ def self.encode_as(value, type)
36
+ return [TYPE_VOID].pack("C*") if value.nil?
37
+
38
+ case type
39
+ when :uint8, :uint16, :uint32, :uint64
40
+ encode_scalar(value, signed: false)
41
+ when :int8, :int16, :int32, :int64
42
+ encode_scalar(value, signed: true)
43
+ when :float32
44
+ encode_float32(value)
45
+ when :float64
46
+ encode_float64(value)
47
+ when :bool
48
+ encode_boolean(value)
49
+ when :string
50
+ encode_string(value)
51
+ when :bytes
52
+ encode_bytes(value)
53
+ else
54
+ if type.is_a?(Arf::Types::MapType)
55
+ encode_map(value)
56
+ elsif type.is_a?(Arf::Types::ArrayType)
57
+ encode_array(value)
58
+ elsif type.is_a?(String) && value.class.ancestors.include?(Arf::RPC::Struct)
59
+ encode_struct(value)
60
+ elsif type.is_a?(Class) && type.ancestors.include?(Arf::RPC::Struct)
61
+ encode_struct(value)
62
+ elsif type.is_a?(Class) && type.ancestors.include?(Arf::RPC::Enum)
63
+ encode_scalar(type.to_i(value), signed: false)
64
+ else
65
+ raise InvalidEncodingTypeError,
66
+ "Unable to encode value of type #{value.class.name} (reported type is #{type.inspect})"
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ FLOAT64_MASK = 0x01 << 4
6
+ FLOAT_EMPTY_MASK = 0x01 << 5
7
+ FLOAT32_MIN_VALUE = -3.4028235e38
8
+ FLOAT32_MAX_VALUE = 3.4028235e38
9
+
10
+ def self.encode_float32(value)
11
+ t = TYPE_FLOAT
12
+ if value.zero?
13
+ t |= FLOAT_EMPTY_MASK
14
+ return [t].pack("C*")
15
+ end
16
+
17
+ [
18
+ [t].pack("C*"),
19
+ [value].pack("g").unpack("N").pack("L>")
20
+ ].join
21
+ end
22
+
23
+ def self.encode_float64(value)
24
+ t = TYPE_FLOAT | FLOAT64_MASK
25
+ if value.zero?
26
+ t |= FLOAT_EMPTY_MASK
27
+ return [t].pack("C*")
28
+ end
29
+
30
+ [
31
+ [t].pack("C*"),
32
+ [value].pack("G").unpack("Q>").pack("Q>")
33
+ ].join
34
+ end
35
+
36
+ def self.decode_float(header, io)
37
+ return 0.0 if header.anybits?(FLOAT_EMPTY_MASK)
38
+
39
+ bits = header.nobits?(FLOAT64_MASK) ? 32 : 64
40
+ data = io.read(bits / 8)
41
+ if bits == 32
42
+ data.unpack("L>").pack("L>").unpack1("g")
43
+ else
44
+ data.unpack("Q>").pack("Q>").unpack1("G")
45
+ end
46
+ end
47
+ end
48
+ end