Skip to content
Plugins (183)

protobuf

stable

Encodes and decodes Protocol Buffer wire format at the byte level, providing field encoders for all scalar types plus message composition helpers.

use plugin protobuf::{encode_varint, decode_varint, encode_string_field, …}
22 functions Data Formats
/ filter jk navigate Esc clear
Functions (22)
  1. encode_varint Encode an integer as a varint byte buffer
  2. decode_varint Decode varint bytes to value and length
  3. encode_string_field Encode a string field (wire type 2)
  4. encode_int_field Encode an integer field (wire type 0)
  5. encode_bytes_field Encode a bytes field (wire type 2)
  6. decode_field Decode one field from a byte buffer
  7. wire_type_name Get human-readable wire type name
  8. encode_message Encode a full message from a field table
  9. decode_message Decode all fields from a byte buffer
  10. encode_repeated Encode repeated varint fields
  11. encode_packed Encode packed repeated varints
  12. encode_float_field Encode a float32 field (wire type 5)
  13. encode_double_field Encode a float64 field (wire type 1)
  14. encode_bool_field Encode a bool field (wire type 0)
  15. encode_fixed32 Encode a fixed 32-bit field
  16. encode_fixed64 Encode a fixed 64-bit field
  17. zigzag_encode ZigZag-encode a signed integer
  18. zigzag_decode ZigZag-decode an unsigned integer
  19. encode_sint_field Encode a sint field (zigzag + varint)
  20. encode_submessage_field Wrap encoded bytes as a submessage field
  21. concat_bytes Concatenate multiple byte buffers
  22. message_size Return byte length of an encoded message

Overview

protobuf is a low-level toolkit for hand-building and inspecting Protocol Buffer wire-format payloads, working directly with bytes rather than generated message classes. There is no schema compiler and no opaque message object: every encoder returns a raw bytes buffer, and you compose a full message by encoding individual fields and concatenating them (or by handing a descriptor table to encode_message). Use it when you need to speak protobuf to a service without pulling in a code generator — building a request by hand, parsing an unknown payload field by field, or implementing length-prefixed framing.

The mental model is field-at-a-time: each encode_*_field helper writes one tag-and-value record for a given field number, concat_bytes joins those records into a message, and decode_field / decode_message walk a buffer back into #{field_number, wire_type, value} entries. The zigzag_* helpers and encode_sint_field cover signed-varint compaction, while encode_submessage_field lets you nest a previously-encoded message as a single field.

Common patterns

Build a message field by field and concatenate the records:

use plugin protobuf::{encode_string_field, encode_int_field, encode_bool_field, concat_bytes, message_size}

let msg = concat_bytes(
  encode_string_field(1, "Alice"),
  encode_int_field(2, 30),
  encode_bool_field(3, true)
)
print("encoded {message_size(msg)} bytes")

Or describe the whole message as a table and let encode_message do the work, then read it back with decode_message:

use plugin protobuf::{encode_message, decode_message, wire_type_name}

let buf = encode_message([
  #{"field_num": 1, "type": "string", "value": "Bob"},
  #{"field_num": 2, "type": "sint",   "value": -42}
])

for _, f in decode_message(buf) {
  print("field {f["field_number"]} ({wire_type_name(f["wire_type"])}): {f["value"]}")
}

Nest an encoded message as a submessage field of an outer message:

use plugin protobuf::{encode_message, encode_submessage_field, encode_int_field, concat_bytes}

let inner = encode_message([#{"field_num": 1, "type": "string", "value": "nested"}])
let outer = concat_bytes(
  encode_int_field(1, 7),
  encode_submessage_field(2, inner)
)

Encode an integer as a varint byte buffer

Encodes an unsigned integer using the protobuf varint encoding. Use this for raw varint bytes without a field tag.

use plugin protobuf::{encode_varint}

let buf = encode_varint(300)
print(message_size(buf))  // 2 bytes

Decode varint bytes to value and length

Decodes the first varint from a byte buffer. Returns #{value: int, bytes_read: int}.

use plugin protobuf::{encode_varint, decode_varint}

let buf = encode_varint(150)
let result = decode_varint(buf)
print(result["value"])      // 150
print(result["bytes_read"]) // 2

Encode a string field (wire type 2)

Encodes a UTF-8 string as a length-delimited field (wire type 2).

use plugin protobuf::{encode_string_field, concat_bytes}

let name_field = encode_string_field(1, "Alice")
let email_field = encode_string_field(2, "alice@example.com")
let msg = concat_bytes(name_field, email_field)

Encode an integer field (wire type 0)

Encodes an unsigned integer as a varint field (wire type 0). Use for int32, int64, uint32, uint64, and enum field types.

use plugin protobuf::{encode_int_field}

let age_field = encode_int_field(3, 30)

Encode a bytes field (wire type 2)

Encodes a raw byte buffer as a length-delimited field (wire type 2). Use for protobuf bytes fields.

use plugin protobuf::{encode_bytes_field}

let payload = encode_bytes_field(4, some_bytes)

Decode one field from a byte buffer

Decodes one field from the start of a byte buffer. Returns #{field_number, wire_type, value, bytes_read}. String-valid length-delimited data is decoded as a string; otherwise as bytes.

use plugin protobuf::{encode_string_field, decode_field}

let buf = encode_string_field(1, "hello")
let f = decode_field(buf)
print(f["field_number"])  // 1
print(f["value"])         // hello

Use bytes_read to advance through a buffer one field at a time:

use plugin protobuf::{encode_int_field, encode_string_field, concat_bytes, decode_field, wire_type_name}

let buf = concat_bytes(encode_int_field(1, 42), encode_string_field(2, "ok"))
let first = decode_field(buf)
print("first is {wire_type_name(first["wire_type"])} = {first["value"]}, read {first["bytes_read"]} bytes")

Get human-readable wire type name

Returns the human-readable name for a wire type integer (0–5).

use plugin protobuf::{wire_type_name}

print(wire_type_name(0))  // varint
print(wire_type_name(2))  // length-delimited

Encode a full message from a field table

Encodes a complete message from a table of field descriptor entries. Each entry must have field_num, type, and value. Supported types: "varint", "int", "sint", "bool", "string", "bytes", "float", "double", "fixed32", "fixed64".

use plugin protobuf::{encode_message}

let msg = encode_message([
  #{"field_num": 1, "type": "string", "value": "Alice"},
  #{"field_num": 2, "type": "int",    "value": 30},
  #{"field_num": 3, "type": "bool",   "value": true}
])

Mix scalar kinds in one descriptor table — sint zigzag-encodes negatives and double writes a fixed 8-byte value:

use plugin protobuf::{encode_message, message_size}

let reading = encode_message([
  #{"field_num": 1, "type": "sint",   "value": -7},
  #{"field_num": 2, "type": "double", "value": 48.8566},
  #{"field_num": 3, "type": "bytes",  "value": some_bytes}
])
print("payload is {message_size(reading)} bytes")

Decode all fields from a byte buffer

Decodes all fields from a byte buffer. Returns a table where each entry has field_number, wire_type, and value.

use plugin protobuf::{encode_message, decode_message}

let buf = encode_message([#{"field_num": 1, "type": "string", "value": "Bob"}])
let fields = decode_message(buf)
for _, f in fields {
  print("field {f["field_number"]}: {f["value"]}")
}

Encode repeated varint fields

Encodes multiple values for the same field number as separate varint records (non-packed repeated).

use plugin protobuf::{encode_repeated}

let ids = encode_repeated(5, [1, 2, 3, 4])

Encode packed repeated varints

Encodes multiple integers as a single length-delimited packed field. More efficient than encode_repeated for numeric arrays.

use plugin protobuf::{encode_packed}

let scores = encode_packed(6, [100, 200, 300])

The packed form is one length-delimited record regardless of element count, so it is typically smaller than encode_repeated for the same values:

use plugin protobuf::{encode_repeated, encode_packed, message_size}

let values = [1, 2, 3, 4, 5]
print("repeated: {message_size(encode_repeated(6, values))} bytes")
print("packed:   {message_size(encode_packed(6, values))} bytes")

Encode a float32 field (wire type 5)

Encodes a 32-bit float using wire type 5 (fixed 4 bytes).

use plugin protobuf::{encode_float_field}

let temp = encode_float_field(7, 36.6)

Encode a float64 field (wire type 1)

Encodes a 64-bit double using wire type 1 (fixed 8 bytes).

use plugin protobuf::{encode_double_field}

let lat = encode_double_field(8, 48.8566)

Encode a bool field (wire type 0)

Encodes a boolean as a varint field (0 or 1).

use plugin protobuf::{encode_bool_field}

let active = encode_bool_field(9, true)

Encode a fixed 32-bit field

Encodes a fixed-width unsigned 32-bit integer (always 4 bytes, wire type 5). Use for fixed32 and sfixed32 proto fields.

use plugin protobuf::{encode_fixed32}

let crc = encode_fixed32(10, 0xDEADBEEF)

Encode a fixed 64-bit field

Encodes a fixed-width 64-bit integer (always 8 bytes, wire type 1).

use plugin protobuf::{encode_fixed64}

let ts = encode_fixed64(11, 1700000000000)

ZigZag-encode a signed integer

Applies ZigZag encoding to a signed integer, producing a non-negative value suitable for varint encoding. Negative numbers encode more efficiently this way.

use plugin protobuf::{zigzag_encode, zigzag_decode}

print(zigzag_encode(-1))  // 1
print(zigzag_encode(1))   // 2
print(zigzag_decode(1))   // -1

ZigZag-decode an unsigned integer

Reverses ZigZag encoding, recovering the original signed integer.

use plugin protobuf::{zigzag_decode}

print(zigzag_decode(4))  // 2

Encode a sint field (zigzag + varint)

Encodes a signed integer using ZigZag encoding followed by varint (the sint32/sint64 wire format). More compact than encode_int_field for negative values.

use plugin protobuf::{encode_sint_field}

let delta = encode_sint_field(12, -100)

Wrap encoded bytes as a submessage field

Wraps an already-encoded message byte buffer as a length-delimited field, enabling nested messages.

use plugin protobuf::{encode_message, encode_submessage_field, concat_bytes}

let inner = encode_message([#{"field_num": 1, "type": "string", "value": "nested"}])
let outer = encode_submessage_field(1, inner)

Concatenate multiple byte buffers

Concatenates any number of byte buffers into one. All arguments must be bytes values.

use plugin protobuf::{encode_string_field, encode_int_field, concat_bytes}

let msg = concat_bytes(
  encode_string_field(1, "Alice"),
  encode_int_field(2, 42)
)

Return byte length of an encoded message

Returns the byte length of an encoded message buffer. Useful for logging or length-prefix framing.

use plugin protobuf::{encode_message, message_size}

let buf = encode_message([#{"field_num": 1, "type": "string", "value": "test"}])
print("encoded size: {message_size(buf)} bytes")

Search Zolo

9 results

en