Module: Terminalwire::V2::Codec
- Defined in:
- lib/terminalwire/v2/codec.rb
Overview
Pure bytes <-> frame conversion. A frame is a Hash with string keys (the wire shape). No I/O, no transport — this is the sans-IO seam the conformance corpus exercises directly.
Constant Summary collapse
- MAX_SID =
Largest valid stream id: a signed 64-bit max. Go decodes sids into int64, so anything above this would wrap to a negative (colliding) sid there; bounding it here keeps all three impls' sid validity identical.
(1 << 63) - 1
Class Method Summary collapse
-
.decode(bytes) ⇒ Hash
The decoded frame with string keys.
-
.encode(frame) ⇒ String
MessagePack bytes (binary encoding).
Class Method Details
.decode(bytes) ⇒ Hash
Returns the decoded frame with string keys.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/terminalwire/v2/codec.rb', line 28 def decode(bytes) obj = begin MessagePack.unpack(bytes) rescue StandardError => e raise ProtocolError, "malformed msgpack: #{e.}" end raise ProtocolError, "frame must be a map" unless obj.is_a?(Hash) # 't' must be a NON-EMPTY type string. Go and Elixir reject "" at the codec; # Ruby used to let it through to the state machine. An empty type is not a # valid frame — reject it here so all three behave identically. raise ProtocolError, "frame missing string 't'" unless obj["t"].is_a?(String) && !obj["t"].empty? # 'sid' must be a non-negative integer that fits in a signed 64-bit int (see # MAX_SID): real sids are small and server-allocated, and the range bound keeps # the three impls aligned (Go would otherwise wrap a uint64 sid to a negative). sid = obj["sid"] unless sid.is_a?(Integer) && sid >= 0 && sid <= MAX_SID raise ProtocolError, "frame missing integer 'sid'" end obj end |
.encode(frame) ⇒ String
Returns MessagePack bytes (binary encoding).
19 20 21 22 23 |
# File 'lib/terminalwire/v2/codec.rb', line 19 def encode(frame) raise ProtocolError, "frame must be a Hash, got #{frame.class}" unless frame.is_a?(Hash) MessagePack.pack(frame) end |