arf 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|