grpc
stableEncode and decode Protocol Buffer wire format fields and gRPC frames, and build gRPC HTTP/2 request metadata.
use plugin grpc::{encode_varint, decode_varint, encode_string_field, …} Functions (19)
- encode_varint Encode integer as protobuf LEB128 varint bytes
- decode_varint Decode varint bytes to value and bytes consumed
- encode_string_field Encode a protobuf string field
- encode_int_field Encode a protobuf integer field
- decode_fields Decode all fields from a protobuf message
- encode_grpc_frame Wrap message bytes in a gRPC length-prefixed frame
- decode_grpc_frame Unwrap a gRPC frame into header and message
- build_grpc_request Build gRPC HTTP/2 request metadata and body
- parse_grpc_status Extract status code and message from response headers
- status_name Map a gRPC status code to its canonical name
- encode_bytes_field Encode a protobuf bytes/embedded-message field
- encode_map Encode protobuf map entries
- decode_map Decode protobuf map entries
- encode_fixed32_field Encode a 32-bit fixed-width field
- encode_fixed64_field Encode a 64-bit fixed-width field
- concat_bytes Concatenate two byte sequences
- encode_float_field Encode a protobuf float field
- encode_double_field Encode a protobuf double field
- encode_bool_field Encode a protobuf bool field
Overview
grpc is a low-level toolkit for the Protocol Buffers wire format and the gRPC
framing layer, built entirely on plain byte sequences. It has no client of its
own and opens no sockets: every function either produces bytes (the encoded
form of a protobuf field, a map, or a gRPC frame) or consumes bytes and
returns a decoded table. Because each encoder emits a self-contained chunk for
a single field, you assemble a full message by concatenating those chunks with
concat_bytes, wrap the result in a gRPC length-prefixed frame, and pair it with
the HTTP/2 metadata from build_grpc_request.
The mental model is bottom-up: encode individual fields (encode_string_field,
encode_int_field, the encode_*_field family), glue them together, frame the
message with encode_grpc_frame, send it over your own HTTP/2 transport, then
read the reply back with decode_grpc_frame, decode_fields, and
parse_grpc_status / status_name. Reach for it whenever you need to speak gRPC
or read and write protobuf bytes without depending on generated stubs.
Common patterns
Build a multi-field protobuf message and wrap it in a gRPC request:
use plugin grpc::{encode_string_field, encode_int_field, concat_bytes, build_grpc_request}
let body = concat_bytes(encode_string_field(1, "Alice"), encode_int_field(2, 30))
let req = build_grpc_request("user.UserService", "CreateUser", body)
print("POST {req["path"]} ({req["content_type"]})")
Frame a message, then decode the frame and its fields on the other side:
use plugin grpc::{encode_string_field, encode_grpc_frame, decode_grpc_frame, decode_fields}
let frame = encode_grpc_frame(encode_string_field(1, "ping"), false)
let unwrapped = decode_grpc_frame(frame)
let fields = decode_fields(unwrapped["message"])
print("field {fields[0]["field_number"]} is wire type {fields[0]["wire_type"]}")
Inspect a gRPC status returned in response trailers:
use plugin grpc::{parse_grpc_status, status_name}
let trailers = #{"grpc-status": "5", "grpc-message": "user not found"}
let status = parse_grpc_status(trailers)
print("{status_name(status["code"])}: {status["message"]}")
Encode integer as protobuf LEB128 varint bytes
Encodes a non-negative integer using protobuf LEB128 variable-length encoding.
use plugin grpc::{encode_varint}
let b = encode_varint(300)
print(b.len())
Decode varint bytes to value and bytes consumed
Decodes the first varint from bytes and returns a table with value (int) and bytes_read (int).
use plugin grpc::{encode_varint, decode_varint}
let b = encode_varint(300)
let result = decode_varint(b)
print(result["value"])
print(result["bytes_read"])
Encode a protobuf string field
Encodes a string as a length-delimited protobuf field (wire type 2) for the given field number.
use plugin grpc::{encode_string_field}
let b = encode_string_field(1, "hello")
print(b.len())
Encode a protobuf integer field
Encodes an integer as a varint protobuf field (wire type 0).
use plugin grpc::{encode_int_field}
let b = encode_int_field(2, 42)
print(b.len())
Decode all fields from a protobuf message
Decodes all fields from a protobuf-encoded message. Returns a list of {field_number, wire_type, data} entries.
use plugin grpc::{encode_string_field, encode_int_field, concat_bytes, decode_fields}
let msg = concat_bytes(encode_string_field(1, "Alice"), encode_int_field(2, 30))
let fields = decode_fields(msg)
print(fields[0]["field_number"])
Iterate every decoded field to print its wire type alongside its number:
use plugin grpc::{encode_string_field, encode_int_field, encode_bool_field, concat_bytes, decode_fields}
let msg = concat_bytes(concat_bytes(encode_string_field(1, "Bob"), encode_int_field(2, 7)), encode_bool_field(3, true))
let fields = decode_fields(msg)
for field in fields {
print("#{field["field_number"]} wire={field["wire_type"]}")
}
Wrap message bytes in a gRPC length-prefixed frame
Wraps message bytes in a gRPC 5-byte length-prefixed frame: [compressed:1][length:4][message:N].
use plugin grpc::{encode_string_field, encode_grpc_frame, decode_grpc_frame}
let msg = encode_string_field(1, "ping")
let frame = encode_grpc_frame(msg, false)
let decoded = decode_grpc_frame(frame)
print(decoded["compressed"])
print(decoded["length"])
Unwrap a gRPC frame into header and message
Parses a gRPC frame and returns {compressed, length, message}. Errors if the input is shorter than five bytes or is truncated relative to its declared length.
use plugin grpc::{encode_int_field, encode_grpc_frame, decode_grpc_frame}
let frame = encode_grpc_frame(encode_int_field(1, 99), false)
let result = decode_grpc_frame(frame)
print(result["length"])
Build gRPC HTTP/2 request metadata and body
Builds the HTTP/2 metadata needed for a gRPC call: path, content_type, te, grpc_encoding, and body (the message already wrapped in an uncompressed frame).
use plugin grpc::{encode_string_field, build_grpc_request}
let msg = encode_string_field(1, "world")
let req = build_grpc_request("helloworld.Greeter", "SayHello", msg)
print(req["path"])
print(req["content_type"])
Extract status code and message from response headers
Reads grpc-status, grpc-message, and grpc-status-details-bin from a response headers table (keys are matched case-insensitively). Returns {code, message, details}, with code defaulting to -1 when no status header is present.
use plugin grpc::{parse_grpc_status, status_name}
let headers = #{"grpc-status": "0", "grpc-message": "OK"}
let status = parse_grpc_status(headers)
print(status_name(status["code"]))
Map a gRPC status code to its canonical name
Maps a gRPC status integer code (0–16) to its canonical name such as "OK", "NOT_FOUND", or "UNAUTHENTICATED". Unknown codes return "UNKNOWN".
use plugin grpc::{status_name}
print(status_name(5))
print(status_name(0))
Encode a protobuf bytes/embedded-message field
Encodes a byte sequence as a length-delimited protobuf field (wire type 2). Use this for bytes fields and for nesting an already-encoded embedded message.
use plugin grpc::{encode_string_field, encode_bytes_field, decode_fields}
let inner = encode_string_field(1, "nested")
let outer = encode_bytes_field(5, inner)
let fields = decode_fields(outer)
print(fields[0]["field_number"])
Encode protobuf map entries
Encodes a table of (key, value) pairs as a sequence of protobuf map entries. Each entry becomes a length-delimited message with field 1 as the key and field 2 as the value; string, integer, and bytes values are supported.
use plugin grpc::{encode_map, decode_map}
let encoded = encode_map(#{"name": "Alice", "city": "Berlin"})
let back = decode_map(encoded, "string", "string")
print(back["name"])
Decode protobuf map entries
Decodes a sequence of protobuf map entries back into a table. key_type and value_type select how each side is interpreted: "string", "int", or "bytes".
use plugin grpc::{encode_map, decode_map}
let encoded = encode_map(#{"retries": 3, "timeout": 30})
let settings = decode_map(encoded, "string", "int")
print(settings["timeout"])
Encode a 32-bit fixed-width field
Encodes an integer as a protobuf fixed32 field (wire type 5, four little-endian bytes). Unlike a varint, the width is constant regardless of the value.
use plugin grpc::{encode_fixed32_field}
let b = encode_fixed32_field(4, 1000)
print(b.len())
Encode a 64-bit fixed-width field
Encodes an integer as a protobuf fixed64 field (wire type 1, eight little-endian bytes).
use plugin grpc::{encode_fixed64_field}
let b = encode_fixed64_field(6, 9000000000)
print(b.len())
Concatenate two byte sequences
Concatenates two byte sequences. Useful for assembling multi-field protobuf messages before framing.
use plugin grpc::{encode_string_field, encode_int_field, concat_bytes}
let name_field = encode_string_field(1, "Alice")
let age_field = encode_int_field(2, 30)
let msg = concat_bytes(name_field, age_field)
print(msg.len())
Encode a protobuf float field
Encodes a number as a protobuf float field (wire type 5, 32-bit IEEE-754). The value is narrowed to single precision.
use plugin grpc::{encode_float_field}
let b = encode_float_field(7, 3.14)
print(b.len())
Encode a protobuf double field
Encodes a number as a protobuf double field (wire type 1, 64-bit IEEE-754), preserving full double precision.
use plugin grpc::{encode_double_field, concat_bytes, encode_string_field, encode_grpc_frame}
let payload = concat_bytes(encode_string_field(1, "EUR"), encode_double_field(2, 19.99))
let frame = encode_grpc_frame(payload, false)
print(frame.len())
Encode a protobuf bool field
Encodes a boolean as a varint protobuf field (0 for false, 1 for true).
use plugin grpc::{encode_bool_field}
let b = encode_bool_field(3, true)
print(b.len())