object {{ codec_name }} {
    const val STRUCT_SIZE = {{ struct_size }}
    private const val TAG_OFFSET = 0
    private const val PAYLOAD_OFFSET = {{ payload_offset }}

{%- for variant in variants %}
    private const val TAG_{{ variant.const_name }} = {{ variant.tag_value }}
{%- endfor %}

    fun pack(value: {{ class_name }}): ByteArray {
        val bytes = ByteArray(STRUCT_SIZE)
        val buf = ByteBuffer.wrap(bytes).order(ByteOrder.nativeOrder())

        when (value) {
{%- for variant in variants %}
            is {{ class_name }}.{{ variant.name }} -> {
                buf.putInt(TAG_OFFSET, TAG_{{ variant.const_name }})
{%- for field in variant.fields %}
                buf.{{ field.putter }}(PAYLOAD_OFFSET + {{ field.offset }}, {{ field.value_expr }})
{%- endfor %}
            }
{%- endfor %}
        }

        return bytes
    }

    fun read(buf: ByteBuffer): {{ class_name }} {
        buf.order(ByteOrder.nativeOrder())
        return when (val tag = buf.getInt(TAG_OFFSET)) {
{%- for variant in variants %}
            TAG_{{ variant.const_name }} ->
{%- if variant.fields.is_empty() %}
                {{ class_name }}.{{ variant.name }}
{%- else %}
                {{ class_name }}.{{ variant.name }}(
{%- for field in variant.fields %}
                    {{ field.param_name }} = buf.{{ field.getter }}(PAYLOAD_OFFSET + {{ field.offset }}){{ field.conversion }}{% if !loop.last %},{% endif %}
{%- endfor %}
                )
{%- endif %}
{%- endfor %}
            else -> throw FfiException(-1, "Unknown {{ class_name }} tag: $tag")
        }
    }
}
