Use Codec
Delegates field decoding/encoding to an existing Codec object.
Use this only for custom, hand-written codecs — variable-byte-integer encoders, image-bitmap parsers, var-byte-int reason-code encodings, and the like. If the field's type is itself annotated with ProtocolMessage, declare the field with that type directly instead — the processor generates the codec by convention and wires it up automatically, including sealed dispatch and forward references to codecs generated in the same compilation round. @UseCodec cannot forward-reference a KSP-generated codec class.
The referenced codec must be a Kotlin object implementing Codec<T> (or, for the bounding shape, BoundingLengthCodec). Standalone expect/actual codecs are supported (Kotlin linker resolves the platform-side actual at the call site).
Bare scalar — codec reads directly from the buffer
@ProtocolMessage
data class Message(
@UseCodec(VariableIntCodec::class) val length: Int,
)
// Generated: val length = VariableIntCodec.decode(buffer, context)Length-prefixed payload — typed binary slot framed by an inline prefix
@ProtocolMessage
data class V5Properties(
@LengthPrefixed @UseCodec(JpegBitmapCodec::class) val bitmap: ImageBitmap,
)
// Generated: read 2-byte UShort prefix, narrow buffer.limit() by the prefix value,
// run JpegBitmapCodec.decode(buffer, context), restore the outer limit on finally.Length-prefixed list — codec is BoundingLengthCodec<UInt> driving a var-width prefix
@ProtocolMessage
data class V5Connect(
@LengthPrefixed @UseCodec(MqttRemainingLengthCodec::class) val properties: List<V5Property>,
)
// Generated: prefix codec writes a variable-byte-integer header carrying the
// body byte count; element loop runs against the bounded region.Bounded by a sibling — @LengthFrom provides the byte count
@ProtocolMessage
data class ImageFrame(
val bitmapLength: Int,
@UseCodec(PngBitmapCodec::class) @LengthFrom("bitmapLength") val bitmap: ImageBitmap,
)
// Generated: narrow buffer.limit() by bitmapLength.toInt(), call PngBitmapCodec.decode,
// restore the outer limit on finally.Extension pattern — ship your own per-charset / per-format codec
The library ships exactly one built-in string codec (com.ditchoom.buffer.codec.AsciiStringCodec, 7-bit ASCII). Other charsets (Latin-1, UTF-16 BE/LE, Modified UTF-8) and binary formats (PNG, MQTT remaining-length, etc.) live in consumer code: each one carries charset-specific nuance (BOM policy, surrogate handling, JVM-class-file quirks for Modified UTF-8) the consumer can resolve better than the framework. Author a Kotlin object implementing Codec<T> and plug it in via @UseCodec — the AsciiStringCodec source is the canonical template:
object Latin1StringCodec : Codec<String> {
override fun decode(buffer: ReadBuffer, context: DecodeContext): String =
buffer.readString(buffer.remaining(), Charset.ISOLatin1)
override fun encode(buffer: WriteBuffer, value: String, context: EncodeContext) {
buffer.writeString(value, Charset.ISOLatin1)
}
override fun wireSize(value: String, context: EncodeContext): WireSize = WireSize.Exact(value.length)
}When NOT to use @UseCodec
For nested @ProtocolMessage types, attach length annotations directly to the field — @UseCodec is unnecessary and forward-reference-incompatible:
@ProtocolMessage
data class Body(@LengthPrefixed val name: String)
@ProtocolMessage
data class Frame(
val length: UShort,
@LengthFrom("length") val body: Body, // Body has @ProtocolMessage — no @UseCodec
)Composes with LengthPrefixed, RemainingBytes, and LengthFrom.
Parameters
A KClass referencing a Kotlin object that implements Codec<T> (or BoundingLengthCodec<UInt> when paired with @LengthPrefixed on a List<T>).