arf 0.1.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 +7 -0
- data/.ruby-version +1 -0
- data/LICENSE.txt +21 -0
- data/Rakefile +12 -0
- data/lib/arf/configuration.rb +67 -0
- data/lib/arf/context.rb +170 -0
- data/lib/arf/errors.rb +10 -0
- data/lib/arf/io/buffer.rb +25 -0
- data/lib/arf/io/compression.rb +44 -0
- data/lib/arf/io/limit_reader.rb +30 -0
- data/lib/arf/observer.rb +29 -0
- data/lib/arf/proto/array.rb +31 -0
- data/lib/arf/proto/boolean.rb +15 -0
- data/lib/arf/proto/bytes.rb +35 -0
- data/lib/arf/proto/decoder.rb +31 -0
- data/lib/arf/proto/encoder.rb +71 -0
- data/lib/arf/proto/float.rb +48 -0
- data/lib/arf/proto/map.rb +49 -0
- data/lib/arf/proto/registry.rb +27 -0
- data/lib/arf/proto/scalar.rb +55 -0
- data/lib/arf/proto/string.rb +36 -0
- data/lib/arf/proto/struct.rb +84 -0
- data/lib/arf/proto/types.rb +67 -0
- data/lib/arf/proto/union.rb +25 -0
- data/lib/arf/proto.rb +17 -0
- data/lib/arf/reactor.rb +270 -0
- data/lib/arf/rpc/base_message.rb +119 -0
- data/lib/arf/rpc/client_base.rb +110 -0
- data/lib/arf/rpc/end_stream.rb +9 -0
- data/lib/arf/rpc/enum.rb +51 -0
- data/lib/arf/rpc/message_kind.rb +27 -0
- data/lib/arf/rpc/metadata.rb +120 -0
- data/lib/arf/rpc/method_meta.rb +42 -0
- data/lib/arf/rpc/request.rb +39 -0
- data/lib/arf/rpc/responder.rb +186 -0
- data/lib/arf/rpc/response.rb +46 -0
- data/lib/arf/rpc/service_base.rb +137 -0
- data/lib/arf/rpc/start_stream.rb +9 -0
- data/lib/arf/rpc/stream_error.rb +23 -0
- data/lib/arf/rpc/stream_item.rb +16 -0
- data/lib/arf/rpc/stream_metadata.rb +16 -0
- data/lib/arf/rpc/struct.rb +255 -0
- data/lib/arf/rpc.rb +19 -0
- data/lib/arf/server.rb +123 -0
- data/lib/arf/status.rb +75 -0
- data/lib/arf/types/array_type.rb +24 -0
- data/lib/arf/types/base_type.rb +14 -0
- data/lib/arf/types/coercion.rb +36 -0
- data/lib/arf/types/in_out_stream.rb +28 -0
- data/lib/arf/types/input_stream.rb +8 -0
- data/lib/arf/types/map_type.rb +32 -0
- data/lib/arf/types/mixin.rb +29 -0
- data/lib/arf/types/output_stream.rb +8 -0
- data/lib/arf/types/streamer.rb +21 -0
- data/lib/arf/types.rb +69 -0
- data/lib/arf/version.rb +5 -0
- data/lib/arf/wire/base_connection.rb +177 -0
- data/lib/arf/wire/client.rb +101 -0
- data/lib/arf/wire/encoding.rb +49 -0
- data/lib/arf/wire/error_code.rb +35 -0
- data/lib/arf/wire/errors.rb +88 -0
- data/lib/arf/wire/frame.rb +111 -0
- data/lib/arf/wire/frame_kind.rb +23 -0
- data/lib/arf/wire/frame_reader.rb +104 -0
- data/lib/arf/wire/frames/base_frame.rb +108 -0
- data/lib/arf/wire/frames/configuration_frame.rb +33 -0
- data/lib/arf/wire/frames/data_frame.rb +21 -0
- data/lib/arf/wire/frames/go_away_frame.rb +29 -0
- data/lib/arf/wire/frames/make_stream_frame.rb +15 -0
- data/lib/arf/wire/frames/ping_frame.rb +18 -0
- data/lib/arf/wire/frames/reset_stream_frame.rb +19 -0
- data/lib/arf/wire/frames.rb +9 -0
- data/lib/arf/wire/server/peer.rb +85 -0
- data/lib/arf/wire/server.rb +63 -0
- data/lib/arf/wire/stream/state.rb +69 -0
- data/lib/arf/wire/stream.rb +128 -0
- data/lib/arf/wire/wait_signal.rb +24 -0
- data/lib/arf/wire.rb +14 -0
- data/lib/arf.rb +46 -0
- 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,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
|
data/lib/arf/context.rb
ADDED
@@ -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
|
data/lib/arf/observer.rb
ADDED
@@ -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
|