Module: Kobako::Codec::ExtTypes
- Defined in:
- lib/kobako/codec/ext_types.rb,
sig/kobako/codec/ext_types.rbs
Overview
The kobako wire ext-type conversions
(docs/wire-codec.md § Ext Types)
as pure functions: per-operation decode state is threaded in as an
argument, so the module itself holds nothing. #build_factory assembles
the one MessagePack::Factory these conversions are registered on.
Constant Summary collapse
- EXT_SYMBOL =
MessagePack ext type code reserved for Symbol (docs/wire-codec.md § Ext Types → ext 0x00). Module-private — mirrors
codec::EXT_SYMBOLon the Rust side. 0x00- EXT_HANDLE =
MessagePack ext type code reserved for Capability Handle (docs/wire-codec.md § Ext Types → ext 0x01). Module-private — mirrors
codec::EXT_HANDLEon the Rust side. 0x01- EXT_ERRENV =
MessagePack ext type code reserved for Exception envelope (docs/wire-codec.md § Ext Types → ext 0x02). Module-private — mirrors
codec::EXT_ERRENVon the Rust side. 0x02- UNREPRESENTABLE_GUARD_ID =
Inert ext id the unrepresentable-value guard registers under. It is never emitted (the guard's packer always raises) and never decoded (no unpacker is registered, so the id stays an UnknownExtTypeError on the wire), so it is not a wire ext type: deliberately not named
EXT_*like the three real ext codes, since it has no Rust-side mirror and must stay outside the wire-symmetry inventory. 0x7F
Class Method Summary collapse
-
.build_factory ⇒ Object
Assemble a
MessagePack::Factorywith the three kobako ext types plus the unrepresentable-value guard registered, frozen because registration is its only mutation and happens exactly once. -
.pack_fault(fault, state) ⇒ Object
Encode the inner ext-0x02 map via Encoder (not the raw factory) so the embedded payload flows through the same boundary as a top-level encode — nested kobako values (Handle, nested Fault) reach the registered ext-type packers.
-
.pack_handle(handle) ⇒ Object
Handle-id packer for the ext-0x01 registration: the fixext-4 big-endian id frame.
-
.pack_symbol(symbol) ⇒ Object
Symbol-to-name packer for the ext-0x00 registration.
- .register_fault(factory) ⇒ Object
- .register_handle(factory) ⇒ Object
- .register_symbol(factory) ⇒ Object
-
.register_unrepresentable(factory) ⇒ Object
A catch-all packer that rejects any value with no wire representation as
UnsupportedType. -
.unpack_fault(payload, state) ⇒ Object
Peel the embedded msgpack map and hand it to
Kobako::Fault.newinside Decoder.decode's block form, so the value-object'sArgumentErrorinvariants surface asInvalidTypethrough the decoder boundary. -
.unpack_handle(payload, state) ⇒ Object
Peel off the fixext-4 frame, hand the bytes to the Host-Gem-internal
Kobako::Handle.restorefactory, and translate theArgumentErrorraised by Handle's invariants into a wire-layerInvalidTypevia Codec::Utils.with_boundary. -
.unpack_symbol(payload) ⇒ Object
Validate the ext-0x00 payload as UTF-8 and intern.
Instance Method Summary collapse
- #self?.build_factory ⇒ MessagePack::Factory
- #self?.pack_fault ⇒ String
- #self?.pack_handle ⇒ String
- #self?.pack_symbol ⇒ String
- #self?.register_fault ⇒ void
- #self?.register_handle ⇒ void
- #self?.register_symbol ⇒ void
- #self?.register_unrepresentable ⇒ void
- #self?.unpack_fault ⇒ Kobako::Fault
- #self?.unpack_handle ⇒ Kobako::Handle
- #self?.unpack_symbol ⇒ Symbol
Class Method Details
.build_factory ⇒ Object
Assemble a MessagePack::Factory with the three kobako ext types plus
the unrepresentable-value guard registered, frozen because
registration is its only mutation and happens exactly once. The
stateful conversions resolve their per-operation state at call time,
so one registered factory serves every thread.
52 53 54 55 56 57 58 59 |
# File 'lib/kobako/codec/ext_types.rb', line 52 def build_factory factory = MessagePack::Factory.new register_symbol(factory) register_handle(factory) register_fault(factory) register_unrepresentable(factory) factory.freeze end |
.pack_fault(fault, state) ⇒ Object
Encode the inner ext-0x02 map via Encoder (not the raw factory) so
the embedded payload flows through the same boundary as a top-level
encode — nested kobako values (Handle, nested Fault) reach the
registered ext-type packers. A details chain nested past the
state depth cap has no wire representation and surfaces as
UnsupportedType. In a payload position (+state+ inside a
forbid_faults bracket) the envelope has no wire representation at
all, so the refusal routes the value into the position's
non-representable handling — the Dispatcher's auto-wrap rescue,
or a raise at the yield site.
112 113 114 115 116 117 118 119 120 |
# File 'lib/kobako/codec/ext_types.rb', line 112 def pack_fault(fault, state) if state.faults_forbidden? raise UnsupportedType, "Kobako::Fault has no wire representation in a payload position" end state.within_ext_frame(UnsupportedType) do Encoder.encode("type" => fault.type, "message" => fault., "details" => fault.details) end end |
.pack_handle(handle) ⇒ Object
Handle-id packer for the ext-0x01 registration: the fixext-4 big-endian id frame.
82 83 84 |
# File 'lib/kobako/codec/ext_types.rb', line 82 def pack_handle(handle) [handle.id].pack("N") end |
.pack_symbol(symbol) ⇒ Object
Symbol-to-name packer for the ext-0x00 registration.
62 63 64 |
# File 'lib/kobako/codec/ext_types.rb', line 62 def pack_symbol(symbol) symbol.name end |
.register_fault(factory) ⇒ Object
163 164 165 166 167 168 169 |
# File 'lib/kobako/codec/ext_types.rb', line 163 def register_fault(factory) factory.register_type( EXT_ERRENV, Kobako::Fault, packer: ->(fault) { ExtTypes.pack_fault(fault, State.current) }, unpacker: ->(payload) { ExtTypes.unpack_fault(payload, State.current) } ) end |
.register_handle(factory) ⇒ Object
155 156 157 158 159 160 161 |
# File 'lib/kobako/codec/ext_types.rb', line 155 def register_handle(factory) factory.register_type( EXT_HANDLE, Kobako::Handle, packer: ->(handle) { ExtTypes.pack_handle(handle) }, unpacker: ->(payload) { ExtTypes.unpack_handle(payload, State.current) } ) end |
.register_symbol(factory) ⇒ Object
147 148 149 150 151 152 153 |
# File 'lib/kobako/codec/ext_types.rb', line 147 def register_symbol(factory) factory.register_type( EXT_SYMBOL, Symbol, packer: ->(symbol) { ExtTypes.pack_symbol(symbol) }, unpacker: ->(payload) { ExtTypes.unpack_symbol(payload) } ) end |
.register_unrepresentable(factory) ⇒ Object
A catch-all packer that rejects any value with no wire representation
as UnsupportedType. Registered on BasicObject so it also covers
BasicObject-based proxies; the narrower Symbol / Handle / Fault
registrations still win by most-specific match, and native types never
reach it. Packer-only: the guard never writes bytes, so its id is inert
and the decode surface stays fail-closed.
This makes the host's non-wire detection a positive allowlist — a value
outside the type set is rejected here rather than routed to to_msgpack
— matching the guest's classname allowlist and the Rust codec's closed
Value enum. Without it, a value with a permissive method_missing
answers the codec's to_msgpack probe and mis-encodes as nil instead
of crossing as a Capability Handle.
184 185 186 187 188 189 |
# File 'lib/kobako/codec/ext_types.rb', line 184 def register_unrepresentable(factory) factory.register_type( UNREPRESENTABLE_GUARD_ID, BasicObject, packer: ->(_value) { raise UnsupportedType, "value has no wire representation" } ) end |
.unpack_fault(payload, state) ⇒ Object
Peel the embedded msgpack map and hand it to Kobako::Fault.new
inside Decoder.decode's block form, so the value-object's
ArgumentError invariants surface as InvalidType through the
decoder boundary. Inner decode goes through Decoder (not the raw
factory) so the embedded str payloads flow through the same
UTF-8 validation as a top-level decode. A nested ext 0x02 in
details re-enters this method, so the state ext-frame guard
bounds the chain depth to keep it from exhausting the native stack.
In a payload position (+state+ inside a forbid_faults bracket) the
envelope is a wire violation outright — its sole legal position is
the Response fault field.
133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/kobako/codec/ext_types.rb', line 133 def unpack_fault(payload, state) if state.faults_forbidden? raise InvalidType, "Fault envelope (ext 0x02) is not a legal value in a payload position" end state.within_ext_frame(InvalidType) do Decoder.decode(payload) do |map| raise InvalidType, "Fault payload must be a map" unless map.is_a?(Hash) Kobako::Fault.new(type: map["type"], message: map["message"], details: map["details"]) end end end |
.unpack_handle(payload, state) ⇒ Object
Peel off the fixext-4 frame, hand the bytes to the
Host-Gem-internal Kobako::Handle.restore factory, and
translate the ArgumentError raised by Handle's invariants
into a wire-layer InvalidType via Codec::Utils.with_boundary.
The Value Object owns the id-range contract; this method only
owns the frame shape. Records the Handle sighting on state so a
Handle-free decode can skip the downstream resolution walk.
93 94 95 96 97 98 99 100 |
# File 'lib/kobako/codec/ext_types.rb', line 93 def unpack_handle(payload, state) state.record_handle! bytes = payload.b raise InvalidType, "Handle payload must be 4 bytes, got #{bytes.bytesize}" unless bytes.bytesize == 4 id = bytes.unpack1("N") # : Integer Codec::Utils.with_boundary { Kobako::Handle.restore(id) } end |
.unpack_symbol(payload) ⇒ Object
Validate the ext-0x00 payload as UTF-8 and intern. Raises
InvalidEncoding on invalid bytes — SPEC forbids the
binary-encoding fallback that msgpack-gem's default unpacker
would otherwise apply. The re-tag step lives here because the
msgpack ext-type unpacker hands us binary bytes; the assertion
itself is shared with Decoder via Utils.assert_utf8!. The
"Symbol" label keeps the error message in Ruby vocabulary
rather than wire-ext-code vocabulary.
74 75 76 77 78 |
# File 'lib/kobako/codec/ext_types.rb', line 74 def unpack_symbol(payload) name = payload.b.force_encoding(Encoding::UTF_8) Utils.assert_utf8!(name, "Symbol payload") name.to_sym end |
Instance Method Details
#self?.build_factory ⇒ MessagePack::Factory
9 |
# File 'sig/kobako/codec/ext_types.rbs', line 9
def self?.build_factory: () -> MessagePack::Factory
|
#self?.pack_fault ⇒ String
19 |
# File 'sig/kobako/codec/ext_types.rbs', line 19
def self?.pack_fault: (Kobako::Fault fault, State state) -> String
|
#self?.pack_handle ⇒ String
15 |
# File 'sig/kobako/codec/ext_types.rbs', line 15
def self?.pack_handle: (Kobako::Handle handle) -> String
|
#self?.pack_symbol ⇒ String
11 |
# File 'sig/kobako/codec/ext_types.rbs', line 11
def self?.pack_symbol: (Symbol symbol) -> String
|
#self?.register_fault ⇒ void
This method returns an undefined value.
27 |
# File 'sig/kobako/codec/ext_types.rbs', line 27
def self?.register_fault: (MessagePack::Factory factory) -> void
|
#self?.register_handle ⇒ void
This method returns an undefined value.
25 |
# File 'sig/kobako/codec/ext_types.rbs', line 25
def self?.register_handle: (MessagePack::Factory factory) -> void
|
#self?.register_symbol ⇒ void
This method returns an undefined value.
23 |
# File 'sig/kobako/codec/ext_types.rbs', line 23
def self?.register_symbol: (MessagePack::Factory factory) -> void
|
#self?.register_unrepresentable ⇒ void
This method returns an undefined value.
29 |
# File 'sig/kobako/codec/ext_types.rbs', line 29
def self?.register_unrepresentable: (MessagePack::Factory factory) -> void
|
#self?.unpack_fault ⇒ Kobako::Fault
21 |
# File 'sig/kobako/codec/ext_types.rbs', line 21
def self?.unpack_fault: (String payload, State state) -> Kobako::Fault
|
#self?.unpack_handle ⇒ Kobako::Handle
17 |
# File 'sig/kobako/codec/ext_types.rbs', line 17
def self?.unpack_handle: (String payload, State state) -> Kobako::Handle
|
#self?.unpack_symbol ⇒ Symbol
13 |
# File 'sig/kobako/codec/ext_types.rbs', line 13
def self?.unpack_symbol: (String payload) -> Symbol
|