Class: Kobako::Codec::Factory
- Inherits:
-
Object
- Object
- Kobako::Codec::Factory
- Extended by:
- Forwardable, SingleForwardable
- Includes:
- Singleton
- Defined in:
- lib/kobako/codec/factory.rb,
sig/kobako/codec/factory.rbs
Overview
Cached MessagePack::Factory that owns the kobako wire ext-type
registration (docs/wire-codec.md
§ Ext Types).
The factory is the single place in the host gem that touches the msgpack API — both Encoder and Decoder delegate through it, so the three kobako ext codes (0x00 Symbol, 0x01 Capability Handle, 0x02 Exception envelope) are configured exactly once at first use.
Lifecycle is owned by Singleton from the Ruby standard library:
Factory.instance is lazy, thread-safe, and process-wide. Class-level
Factory.dump / Factory.load shortcuts are exposed via
SingleForwardable so callers do not have to spell the .instance
hop at every call site; the instance-level #dump / #load are in
turn delegated to the wrapped MessagePack::Factory via Forwardable.
Constant Summary collapse
- EXT_SYMBOL =
MessagePack ext type code reserved for Symbol (docs/wire-codec.md § Ext Types → ext 0x00). Class-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). Class-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). Class-private — mirrors
codec::EXT_ERRENVon the Rust side. 0x02- MAX_EXT_DEPTH =
An ext 0x02 (Fault) envelope nests through its
detailsfield, and each level re-enters the codec with a freshMessagePackunpacker whose built-in stack guard resets — so ext-envelope depth is tracked here instead. The cap matches the wire's overall nesting bound and keeps a nested chain from exhausting the native stack: an over-deep chain fails as a clean wire error, never a stack-level trap. 128- EXT_DEPTH_KEY =
:__kobako_codec_ext_depth__
Class Method Summary collapse
Instance Method Summary collapse
- #dump ⇒ String
-
#initialize ⇒ Factory
constructor
A new instance of Factory.
- #load ⇒ Object
-
#pack_fault(fault) ⇒ String
Encode the inner ext-0x02 map via Encoder (not
factory.dump) 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 via the cached singleton. -
#pack_symbol(symbol) ⇒ String
Symbol-to-name packer for the ext-0x00 registration.
- #register_fault ⇒ void
- #register_handle ⇒ void
- #register_symbol ⇒ void
-
#unpack_fault(payload) ⇒ Kobako::Fault
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) ⇒ Kobako::Handle
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 Utils.with_boundary. -
#unpack_symbol(payload) ⇒ Symbol
Validate the ext-0x00 payload as UTF-8 and intern.
-
#within_ext_frame(over_limit) ⇒ void
Track ext-envelope re-entry depth and refuse a chain past MAX_EXT_DEPTH, raising
over_limitso the failure lands in the caller's existing wire-error class.
Constructor Details
#initialize ⇒ Factory
Returns a new instance of Factory.
73 74 75 76 77 78 |
# File 'lib/kobako/codec/factory.rb', line 73 def initialize @factory = MessagePack::Factory.new register_symbol register_handle register_fault end |
Class Method Details
.dump ⇒ String
17 |
# File 'sig/kobako/codec/factory.rbs', line 17
def self.dump: (untyped value) -> String
|
.load ⇒ Object
18 |
# File 'sig/kobako/codec/factory.rbs', line 18
def self.load: (String bytes) -> untyped
|
Instance Method Details
#dump ⇒ String
14 |
# File 'sig/kobako/codec/factory.rbs', line 14
def dump: (untyped value) -> String
|
#load ⇒ Object
15 |
# File 'sig/kobako/codec/factory.rbs', line 15
def load: (String bytes) -> untyped
|
#pack_fault(fault) ⇒ String
Encode the inner ext-0x02 map via Encoder (not factory.dump) 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 via the cached singleton. A details
chain nested past MAX_EXT_DEPTH has no wire representation and
surfaces as UnsupportedType.
145 146 147 148 149 |
# File 'lib/kobako/codec/factory.rb', line 145 def pack_fault(fault) within_ext_frame(UnsupportedType) do Encoder.encode("type" => fault.type, "message" => fault., "details" => fault.details) end end |
#pack_symbol(symbol) ⇒ String
Symbol-to-name packer for the ext-0x00 registration.
91 92 93 |
# File 'lib/kobako/codec/factory.rb', line 91 def pack_symbol(symbol) symbol.name end |
#register_fault ⇒ void
This method returns an undefined value.
117 118 119 120 121 122 123 |
# File 'lib/kobako/codec/factory.rb', line 117 def register_fault @factory.register_type( EXT_ERRENV, Kobako::Fault, packer: ->(fault) { pack_fault(fault) }, unpacker: ->(payload) { unpack_fault(payload) } ) end |
#register_handle ⇒ void
This method returns an undefined value.
109 110 111 112 113 114 115 |
# File 'lib/kobako/codec/factory.rb', line 109 def register_handle @factory.register_type( EXT_HANDLE, Kobako::Handle, packer: ->(handle) { [handle.id].pack("N") }, unpacker: ->(payload) { unpack_handle(payload) } ) end |
#register_symbol ⇒ void
This method returns an undefined value.
82 83 84 85 86 87 88 |
# File 'lib/kobako/codec/factory.rb', line 82 def register_symbol @factory.register_type( EXT_SYMBOL, Symbol, packer: method(:pack_symbol), unpacker: method(:unpack_symbol) ) end |
#unpack_fault(payload) ⇒ Kobako::Fault
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
factory.load) 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 #within_ext_frame bounds the
chain depth to keep it from exhausting the native stack.
159 160 161 162 163 164 165 166 167 |
# File 'lib/kobako/codec/factory.rb', line 159 def unpack_fault(payload) 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) ⇒ Kobako::Handle
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 Utils.with_boundary.
The Value Object owns the id-range contract; this method only
owns the frame shape.
131 132 133 134 135 136 137 |
# File 'lib/kobako/codec/factory.rb', line 131 def unpack_handle(payload) 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) ⇒ Symbol
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.
103 104 105 106 107 |
# File 'lib/kobako/codec/factory.rb', line 103 def unpack_symbol(payload) name = payload.b.force_encoding(Encoding::UTF_8) Utils.assert_utf8!(name, "Symbol payload") name.to_sym end |
#within_ext_frame(over_limit) ⇒ void
This method returns an undefined value.
Track ext-envelope re-entry depth and refuse a chain past
MAX_EXT_DEPTH, raising over_limit so the failure lands in the
caller's existing wire-error class. The counter is thread-scoped and
balanced by the ensure, so a raise mid-chain still unwinds it to
its entry value.
174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/kobako/codec/factory.rb', line 174 def within_ext_frame(over_limit) depth = (Thread.current[EXT_DEPTH_KEY] || 0) + 1 raise over_limit, "ext envelope nesting exceeds #{MAX_EXT_DEPTH} levels" if depth > MAX_EXT_DEPTH Thread.current[EXT_DEPTH_KEY] = depth begin yield ensure Thread.current[EXT_DEPTH_KEY] = depth - 1 end end |