M .beads/issues.jsonl => .beads/issues.jsonl +1 -1
@@ 69,7 69,7 @@
{"_type":"issue","id":"tarantool-protobuf-o8n","title":"gRPC: reference network transports (http_server, netbox, http_client_unary)","description":"Ship reference transports as separate Tarantool modules (deferred from M4). Designs in docs/specs/grpc_transports.md. Three transports: (1) pb.grpc.transport.http_server — Connect-JSON over HTTP/1.1 via tarantool/http; default external transport; works with browsers and curl without an HTTP/2 proxy. (2) pb.grpc.transport.netbox — gRPC tunneled over net.box calls; first-class in-cluster path. (3) pb.grpc.transport.http_client_unary — outbound unary only via http_client. HTTP/2 termination explicitly out of scope; the transport contract is HTTP/2-shaped so the same generated code works behind Envoy. Conformance anchor: connectrpc/conformance harness (memory: connect_conformance_suite).","status":"open","priority":3,"issue_type":"feature","owner":"bigbes@gmail.com","created_at":"2026-05-17T15:47:14Z","created_by":"Eugene Blikh","updated_at":"2026-05-17T15:47:14Z","labels":["grpc","transport"],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"tarantool-protobuf-2me","title":"Decide lazy-default for repeated string|bytes fields","description":"Open design question. For routing/proxy workloads that decode but don't iterate every element, repeated LEN fields could stay as unread ArrayViews until first access. Eager callers pay one extra table alloc; lazy callers skip N per-element string allocations. At 100KB Person, 26+ emails = 26+ string allocations deferrable. Decide: (a) flip default for repeated LEN, (b) gate behind a codegen mode/option, (c) leave eager as default and rely on _decode_lazy. Surface compatibility implications (callers iterate with ipairs today and expect a real array) before deciding.","status":"open","priority":3,"issue_type":"decision","owner":"bigbes@gmail.com","created_at":"2026-05-17T15:47:13Z","created_by":"Eugene Blikh","updated_at":"2026-05-17T15:47:13Z","labels":["decoder","design","perf"],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"tarantool-protobuf-0u1","title":"Decoder: decode_len-style byte-then-varint symmetry in typed decoders","description":"The single-byte peek shortcut in decode_len (wire.lua:410-419) isn't replicated in decode_int32 / decode_uint32 / decode_int64 / decode_uint64 / decode_bool / decode_enum, which always call decode_varint(). Mirror the fast path in each typed decoder so the dominant 1-byte varint case skips a function call. Expected: 5-10% on scalar-heavy decodes. Memory: wire_2byte_varint_cliff (same shape, encode side, already fixed).","status":"open","priority":3,"issue_type":"task","owner":"bigbes@gmail.com","created_at":"2026-05-17T15:47:11Z","created_by":"Eugene Blikh","updated_at":"2026-05-17T15:47:11Z","labels":["decoder","perf","wire"],"dependencies":[{"issue_id":"tarantool-protobuf-0u1","depends_on_id":"tarantool-protobuf-0an","type":"blocks","created_at":"2026-05-17T18:47:21Z","created_by":"Eugene Blikh","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
-{"_type":"issue","id":"tarantool-protobuf-a7l","title":"Encoder: static type-elision on scalars in mode=full","description":"wire.to_uint64(v) does a type(v) dispatch in three branches (number/cdata/boolean); in mode=full codegen the field's static type is known. Emit UINT64(v) / INT64(v) directly at the call site and let LuaJIT type-guard the trace. Expected: 10-15% on scalar-heavy messages.","status":"open","priority":3,"issue_type":"task","assignee":"Eugene Blikh","owner":"bigbes@gmail.com","created_at":"2026-05-17T15:47:07Z","created_by":"Eugene Blikh","updated_at":"2026-05-24T20:14:50Z","started_at":"2026-05-24T20:11:25Z","labels":["codegen","encoder","perf"],"dependency_count":0,"dependent_count":0,"comment_count":0}
+{"_type":"issue","id":"tarantool-protobuf-a7l","title":"Encoder: static type-elision on scalars in mode=full","description":"wire.to_uint64(v) does a type(v) dispatch in three branches (number/cdata/boolean); in mode=full codegen the field's static type is known. Emit UINT64(v) / INT64(v) directly at the call site and let LuaJIT type-guard the trace. Expected: 10-15% on scalar-heavy messages.","status":"closed","priority":3,"issue_type":"task","assignee":"Eugene Blikh","owner":"bigbes@gmail.com","created_at":"2026-05-17T15:47:07Z","created_by":"Eugene Blikh","updated_at":"2026-05-24T21:02:23Z","started_at":"2026-05-24T20:11:25Z","closed_at":"2026-05-24T21:02:23Z","close_reason":"Codegen emits typed fast variants for sint64/fixed64/sfixed64 — wire.encode_sint64_i(INT64(v)), wire.encode_fixed64_u(UINT64(v)), wire.encode_sfixed64_u(UINT64(v)) — skipping the to_int64/to_uint64 runtime type dispatch at every call site where the field type is statically known. Wire-level: added encode_sint64_i / encode_fixed64_u / encode_sfixed64_u alongside the existing encoders; encode_fixed64 is now a thin wrapper over encode_fixed64_u. INT64/UINT64 added as file-header upvalues alongside ffi.\n\nCodegen applied at: singular non-string scalar, singular required scalar, repeated non-packed per-element, extension singular, extension repeated per-element, packed scalar slow path, proto2 extension repeated. int32/int64/uint32/uint64 unchanged since they alias to encode_varint which doesn't call to_uint64 except on the slow-slow path.\n\nMeasured on c_int64.Wide encode (full mode, no PB_ENABLE_C, 4-run median):\n lua-num inputs 2769 -\u003e 2520 ns/op (+9.9% throughput)\n cdata inputs 12993 -\u003e 12935 ns/op (unchanged; to_int64 already takes the cdata fast path there)\n\nJust below the issue's 10-15% claim but real and measurable, and exactly the case where it matters (Lua-number-input is the common case for user code on sint64/fixed64 fields). Suites: test 766/766, test-c 1057/1057.","labels":["codegen","encoder","perf"],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"tarantool-protobuf-cma","title":"Encoder: inline zigzag_encode32 into encode_sint32 (one function call vs two)","description":"encode_sint32(v) = encode_varint(zigzag_encode32(v)) — two function calls. Inline as:\n\n local function encode_sint32(v)\n local n = bit.bxor(bit.lshift(v, 1), bit.arshift(v, 31))\n -- existing 1-byte fast path\n if n \u003e= 0 and n \u003c 0x80 then return string.char(n) end\n return encode_varint_slow(n)\n end\n\nSame for encode_sint64 (with the int64 zigzag formula).\n\nTiny win per sint encode — only matters for sint-heavy payloads. Filed for completeness; h8v makes this moot for mode=full (FFI direct writes inline the whole thing anyway). Real applicability: runtime mode encoders.","notes":"If h8v lands first, this becomes runtime-mode-only and may not be worth the duplication. Defer the call until h8v ships.","status":"open","priority":4,"issue_type":"task","owner":"bigbes@gmail.com","created_at":"2026-05-18T17:15:08Z","created_by":"Eugene Blikh","updated_at":"2026-05-18T17:15:08Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"tarantool-protobuf-yvc","title":"Feature: gRPC server reflection service","description":"PLAN.md section 8 lists gRPC server reflection as a non-goal until after M5. M5 is complete, so keep this as deferred future work. Implement standard reflection once real transport/server use cases require it.","status":"open","priority":4,"issue_type":"feature","owner":"bigbes@gmail.com","created_at":"2026-05-17T16:29:18Z","created_by":"Eugene Blikh","updated_at":"2026-05-17T16:29:18Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"tarantool-protobuf-0j5","title":"Feature: Tarantool 3.x config integration for schema registration","description":"PLAN.md section 7 mentions a pb.types.\u003cname\u003e declarative config role as a possible Tarantool 3.x integration. Define the use case first, then design how protobuf schemas/descriptors are declared, loaded, validated, and made available to application roles.","status":"open","priority":4,"issue_type":"feature","owner":"bigbes@gmail.com","created_at":"2026-05-17T16:29:17Z","created_by":"Eugene Blikh","updated_at":"2026-05-17T16:29:17Z","dependency_count":0,"dependent_count":0,"comment_count":0}
M cmd/protoc-gen-tarantool/internal/gen/gen.go => cmd/protoc-gen-tarantool/internal/gen/gen.go +6 -0
@@ 223,6 223,12 @@ func emitHeader(w *writer, file *protogen.File) {
// element count is recoverable from the LEN payload (2sn). LuaJIT
// 2.1 (Tarantool's bundled fork) ships `table.new`.
w.line("local table_new = require('table.new')")
+ // Pre-typed cdata constructors for sint64/fixed64/sfixed64 singular
+ // encode sites — the codegen casts at the call site instead of going
+ // through wire.to_int64 / wire.to_uint64's runtime type dispatch (a7l).
+ w.line("local ffi = require('ffi')")
+ w.line("local INT64 = ffi.typeof('int64_t')")
+ w.line("local UINT64 = ffi.typeof('uint64_t')")
}
// collectImports returns the deduplicated set of Lua require paths for all
M cmd/protoc-gen-tarantool/internal/gen/inline.go => cmd/protoc-gen-tarantool/internal/gen/inline.go +33 -13
@@ 10,6 10,26 @@ import (
"google.golang.org/protobuf/reflect/protoreflect"
)
+// encodeCallExpr returns the Lua expression that encodes a single value
+// of the given scalar type `st`. For types where mode=full can elide
+// wire.to_int64 / wire.to_uint64's runtime type dispatch (sint64, fixed64,
+// sfixed64), the expression pre-casts the value to int64_t / uint64_t at
+// the call site and routes through the typed fast variant (encode_sint64_i
+// / encode_fixed64_u / encode_sfixed64_u). For everything else it emits
+// the generic wire.encode_<st> call. INT64 and UINT64 are localized in
+// the file header (see emitHeader). (a7l)
+func encodeCallExpr(st, valExpr string) string {
+ switch st {
+ case "sint64":
+ return fmt.Sprintf("wire.encode_sint64_i(INT64(%s))", valExpr)
+ case "fixed64":
+ return fmt.Sprintf("wire.encode_fixed64_u(UINT64(%s))", valExpr)
+ case "sfixed64":
+ return fmt.Sprintf("wire.encode_sfixed64_u(UINT64(%s))", valExpr)
+ }
+ return fmt.Sprintf("wire.encode_%s(%s)", st, valExpr)
+}
+
// wireRefRe matches `wire.<name>` references inside generated function
// bodies. Generated code never embeds `wire.` inside a string literal, so a
// straight textual sweep is safe here. (Verified: error messages mention
@@ 314,8 334,8 @@ func emitPackedVarintElem(w *writer, indent, list, idx, valExpr, st string) {
w.line("%s %s = %s + 1; %s[%s] = CHARS[bit.bxor(bit.lshift(_e, 1), bit.arshift(_e, 31))]",
indent, idx, idx, list, idx)
w.line("%selse", indent)
- w.line("%s %s = %s + 1; %s[%s] = wire.encode_%s(_e)",
- indent, idx, idx, list, idx, st)
+ w.line("%s %s = %s + 1; %s[%s] = %s",
+ indent, idx, idx, list, idx, encodeCallExpr(st, "_e"))
w.line("%send", indent)
case "int32", "int64", "uint32", "uint64":
w.line("%slocal _e = %s", indent, valExpr)
@@ 323,12 343,12 @@ func emitPackedVarintElem(w *writer, indent, list, idx, valExpr, st string) {
w.line("%s %s = %s + 1; %s[%s] = CHARS[_e]",
indent, idx, idx, list, idx)
w.line("%selse", indent)
- w.line("%s %s = %s + 1; %s[%s] = wire.encode_%s(_e)",
- indent, idx, idx, list, idx, st)
+ w.line("%s %s = %s + 1; %s[%s] = %s",
+ indent, idx, idx, list, idx, encodeCallExpr(st, "_e"))
w.line("%send", indent)
default:
- w.line("%s%s = %s + 1; %s[%s] = wire.encode_%s(%s)",
- indent, idx, idx, list, idx, st, valExpr)
+ w.line("%s%s = %s + 1; %s[%s] = %s",
+ indent, idx, idx, list, idx, encodeCallExpr(st, valExpr))
}
}
@@ 455,7 475,7 @@ func emitInlineEncodeField(w *writer, f *protogen.Field, file *protogen.File, se
w.line(" n = n + 1; out[n] = v")
} else {
w.line(" n = n + 1; out[n] = %s", tag)
- w.line(" n = n + 1; out[n] = wire.encode_%s(v)", st)
+ w.line(" n = n + 1; out[n] = %s", encodeCallExpr(st, "v"))
}
w.line(" end")
}
@@ 498,7 518,7 @@ func emitInlineEncodeRequiredField(w *writer, f *protogen.Field, tag string, fil
w.line(" n = n + 1; out[n] = v")
} else {
w.line(" n = n + 1; out[n] = %s", tag)
- w.line(" n = n + 1; out[n] = wire.encode_%s(v)", st)
+ w.line(" n = n + 1; out[n] = %s", encodeCallExpr(st, "v"))
}
}
}
@@ 579,7 599,7 @@ func emitInlineEncodeRepeated(w *writer, f *protogen.Field, tag, fname string, f
w.line(" local _tag = %s", tag)
w.line(" for _i = 1, #v do")
w.line(" n = n + 1; out[n] = _tag")
- w.line(" n = n + 1; out[n] = wire.encode_%s(v[_i])", st)
+ w.line(" n = n + 1; out[n] = %s", encodeCallExpr(st, "v[_i]"))
w.line(" end")
w.line(" end")
}
@@ 646,7 666,7 @@ func emitInlineEncodeExtension(w *writer, ext *protogen.Extension, file *protoge
w.line(" n = n + 1; out[n] = _ev")
} else {
w.line(" n = n + 1; out[n] = %s", tag)
- w.line(" n = n + 1; out[n] = wire.encode_%s(_ev)", st)
+ w.line(" n = n + 1; out[n] = %s", encodeCallExpr(st, "_ev"))
}
w.line(" end")
}
@@ 747,7 767,7 @@ func emitInlineEncodeExtensionRepeated(w *writer, ext *protogen.Extension, tag,
w.line(" local _tag = %s", tag)
w.line(" for _i = 1, #_ev do")
w.line(" n = n + 1; out[n] = _tag")
- w.line(" n = n + 1; out[n] = wire.encode_%s(_ev[_i])", st)
+ w.line(" n = n + 1; out[n] = %s", encodeCallExpr(st, "_ev[_i]"))
w.line(" end")
w.line(" end")
}
@@ 1373,8 1393,8 @@ func emitMapPiece(w *writer, list, idx, tag, valExpr string, f *protogen.Field,
default:
st := scalarName(f.Desc.Kind())
w.line(" %s = %s + 1; %s[%s] = %s", idx, idx, list, idx, tag)
- w.line(" %s = %s + 1; %s[%s] = wire.encode_%s(%s)",
- idx, idx, list, idx, st, valExpr)
+ w.line(" %s = %s + 1; %s[%s] = %s",
+ idx, idx, list, idx, encodeCallExpr(st, valExpr))
}
}
M examples/expected/full/c_int64/c_int64_pb.lua => examples/expected/full/c_int64/c_int64_pb.lua +6 -3
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
@@ 79,19 82,19 @@ function M.Wide_encode(t)
v = t.a_sint64
if v ~= nil and v ~= 0 then
n = n + 1; out[n] = "\x18"
- n = n + 1; out[n] = wire.encode_sint64(v)
+ n = n + 1; out[n] = wire.encode_sint64_i(INT64(v))
end
-- field 4: a_fixed64
v = t.a_fixed64
if v ~= nil and v ~= 0 then
n = n + 1; out[n] = "\x21"
- n = n + 1; out[n] = wire.encode_fixed64(v)
+ n = n + 1; out[n] = wire.encode_fixed64_u(UINT64(v))
end
-- field 5: a_sfixed64
v = t.a_sfixed64
if v ~= nil and v ~= 0 then
n = n + 1; out[n] = "\x29"
- n = n + 1; out[n] = wire.encode_sfixed64(v)
+ n = n + 1; out[n] = wire.encode_sfixed64_u(UINT64(v))
end
local _uf = t._unknown_fields
if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end
M examples/expected/full/c_nested/c_nested_pb.lua => examples/expected/full/c_nested/c_nested_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/full/c_repeated/c_repeated_pb.lua => examples/expected/full/c_repeated/c_repeated_pb.lua +6 -3
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
@@ 331,7 334,7 @@ function M.Holder_new(t) return t or {} end
---@param t c_repeated.Holder
---@return string
function M.Holder_encode(t)
- local encode_fixed64 = wire.encode_fixed64
+ local encode_fixed64_u = wire.encode_fixed64_u
local encode_int32 = wire.encode_int32
local encode_sint32 = wire.encode_sint32
local encode_varint = wire.encode_varint
@@ 461,7 464,7 @@ function M.Holder_encode(t)
local _n = #v
local parts, m = table_new(_n, 0), 0
for _i = 1, _n do
- m = m + 1; parts[m] = encode_fixed64(v[_i])
+ m = m + 1; parts[m] = encode_fixed64_u(UINT64(v[_i]))
end
local _b = table.concat(parts)
n = n + 1; out[n] = "\x32"
@@ 542,7 545,7 @@ function M.Holder_encode(t)
local _tag = "\xa9\x01"
for _i = 1, #v do
n = n + 1; out[n] = _tag
- n = n + 1; out[n] = encode_fixed64(v[_i])
+ n = n + 1; out[n] = encode_fixed64_u(UINT64(v[_i]))
end
end
-- field 22: unpacked_sint32
M examples/expected/full/conformance/conformance_pb.lua => examples/expected/full/conformance/conformance_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/full/hello/hello_pb.lua => examples/expected/full/hello/hello_pb.lua +4 -1
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
@@ 1915,7 1918,7 @@ function M.Person_encode(t)
v = t.user_id
if v ~= nil and v ~= 0 then
n = n + 1; out[n] = "\x49"
- n = n + 1; out[n] = wire.encode_fixed64(v)
+ n = n + 1; out[n] = wire.encode_fixed64_u(UINT64(v))
end
-- field 10: balance
v = t.balance
M examples/expected/full/proto2_basic/proto2_basic_pb.lua => examples/expected/full/proto2_basic/proto2_basic_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/full/protobuf_test_messages/proto2/test_messages_proto2_pb.lua => examples/expected/full/protobuf_test_messages/proto2/test_messages_proto2_pb.lua +36 -33
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
@@ 908,15 911,15 @@ function M.TestAllTypesProto2_encode(t)
local encode_bool = wire.encode_bool
local encode_double = wire.encode_double
local encode_fixed32 = wire.encode_fixed32
- local encode_fixed64 = wire.encode_fixed64
+ local encode_fixed64_u = wire.encode_fixed64_u
local encode_float = wire.encode_float
local encode_int32 = wire.encode_int32
local encode_int64 = wire.encode_int64
local encode_len = wire.encode_len
local encode_sfixed32 = wire.encode_sfixed32
- local encode_sfixed64 = wire.encode_sfixed64
+ local encode_sfixed64_u = wire.encode_sfixed64_u
local encode_sint32 = wire.encode_sint32
- local encode_sint64 = wire.encode_sint64
+ local encode_sint64_i = wire.encode_sint64_i
local encode_string = wire.encode_string
local encode_uint32 = wire.encode_uint32
local encode_uint64 = wire.encode_uint64
@@ 975,7 978,7 @@ function M.TestAllTypesProto2_encode(t)
v = t.optional_sint64
if v ~= nil then
n = n + 1; out[n] = "\x30"
- n = n + 1; out[n] = encode_sint64(v)
+ n = n + 1; out[n] = encode_sint64_i(INT64(v))
end
-- field 7: optional_fixed32
v = t.optional_fixed32
@@ 987,7 990,7 @@ function M.TestAllTypesProto2_encode(t)
v = t.optional_fixed64
if v ~= nil then
n = n + 1; out[n] = "\x41"
- n = n + 1; out[n] = encode_fixed64(v)
+ n = n + 1; out[n] = encode_fixed64_u(UINT64(v))
end
-- field 9: optional_sfixed32
v = t.optional_sfixed32
@@ 999,7 1002,7 @@ function M.TestAllTypesProto2_encode(t)
v = t.optional_sfixed64
if v ~= nil then
n = n + 1; out[n] = "\x51"
- n = n + 1; out[n] = encode_sfixed64(v)
+ n = n + 1; out[n] = encode_sfixed64_u(UINT64(v))
end
-- field 11: optional_float
v = t.optional_float
@@ 1179,7 1182,7 @@ function M.TestAllTypesProto2_encode(t)
local _tag = "\xa0\x02"
for _i = 1, #v do
n = n + 1; out[n] = _tag
- n = n + 1; out[n] = encode_sint64(v[_i])
+ n = n + 1; out[n] = encode_sint64_i(INT64(v[_i]))
end
end
-- field 37: repeated_fixed32
@@ 1197,7 1200,7 @@ function M.TestAllTypesProto2_encode(t)
local _tag = "\xb1\x02"
for _i = 1, #v do
n = n + 1; out[n] = _tag
- n = n + 1; out[n] = encode_fixed64(v[_i])
+ n = n + 1; out[n] = encode_fixed64_u(UINT64(v[_i]))
end
end
-- field 39: repeated_sfixed32
@@ 1215,7 1218,7 @@ function M.TestAllTypesProto2_encode(t)
local _tag = "\xc1\x02"
for _i = 1, #v do
n = n + 1; out[n] = _tag
- n = n + 1; out[n] = encode_sfixed64(v[_i])
+ n = n + 1; out[n] = encode_sfixed64_u(UINT64(v[_i]))
end
end
-- field 41: repeated_float
@@ 1524,7 1527,7 @@ function M.TestAllTypesProto2_encode(t)
if type(_e) == 'number' and _e >= -64 and _e <= 63 then
m = m + 1; parts[m] = CHARS[bit.bxor(bit.lshift(_e, 1), bit.arshift(_e, 31))]
else
- m = m + 1; parts[m] = encode_sint64(_e)
+ m = m + 1; parts[m] = encode_sint64_i(INT64(_e))
end
end
local _b = table.concat(parts)
@@ 1561,7 1564,7 @@ function M.TestAllTypesProto2_encode(t)
local _n = #v
local parts, m = table_new(_n, 0), 0
for _i = 1, _n do
- m = m + 1; parts[m] = encode_fixed64(v[_i])
+ m = m + 1; parts[m] = encode_fixed64_u(UINT64(v[_i]))
end
local _b = table.concat(parts)
n = n + 1; out[n] = "\x92\x05"
@@ 1597,7 1600,7 @@ function M.TestAllTypesProto2_encode(t)
local _n = #v
local parts, m = table_new(_n, 0), 0
for _i = 1, _n do
- m = m + 1; parts[m] = encode_sfixed64(v[_i])
+ m = m + 1; parts[m] = encode_sfixed64_u(UINT64(v[_i]))
end
local _b = table.concat(parts)
n = n + 1; out[n] = "\xa2\x05"
@@ 1743,7 1746,7 @@ function M.TestAllTypesProto2_encode(t)
local _tag = "\xf0\x05"
for _i = 1, #v do
n = n + 1; out[n] = _tag
- n = n + 1; out[n] = encode_sint64(v[_i])
+ n = n + 1; out[n] = encode_sint64_i(INT64(v[_i]))
end
end
-- field 95: unpacked_fixed32
@@ 1761,7 1764,7 @@ function M.TestAllTypesProto2_encode(t)
local _tag = "\x81\x06"
for _i = 1, #v do
n = n + 1; out[n] = _tag
- n = n + 1; out[n] = encode_fixed64(v[_i])
+ n = n + 1; out[n] = encode_fixed64_u(UINT64(v[_i]))
end
end
-- field 97: unpacked_sfixed32
@@ 1779,7 1782,7 @@ function M.TestAllTypesProto2_encode(t)
local _tag = "\x91\x06"
for _i = 1, #v do
n = n + 1; out[n] = _tag
- n = n + 1; out[n] = encode_sfixed64(v[_i])
+ n = n + 1; out[n] = encode_sfixed64_u(UINT64(v[_i]))
end
end
-- field 99: unpacked_float
@@ 1971,11 1974,11 @@ function M.TestAllTypesProto2_encode(t)
local entry, _m = {}, 0
if _k ~= 0 then
_m = _m + 1; entry[_m] = _ktag
- _m = _m + 1; entry[_m] = encode_sint64(_k)
+ _m = _m + 1; entry[_m] = encode_sint64_i(INT64(_k))
end
if _val ~= 0 then
_m = _m + 1; entry[_m] = _vtag
- _m = _m + 1; entry[_m] = encode_sint64(_val)
+ _m = _m + 1; entry[_m] = encode_sint64_i(INT64(_val))
end
n = n + 1; out[n] = _tag
local _b = table.concat(entry)
@@ 2021,11 2024,11 @@ function M.TestAllTypesProto2_encode(t)
local entry, _m = {}, 0
if _k ~= 0 then
_m = _m + 1; entry[_m] = _ktag
- _m = _m + 1; entry[_m] = encode_fixed64(_k)
+ _m = _m + 1; entry[_m] = encode_fixed64_u(UINT64(_k))
end
if _val ~= 0 then
_m = _m + 1; entry[_m] = _vtag
- _m = _m + 1; entry[_m] = encode_fixed64(_val)
+ _m = _m + 1; entry[_m] = encode_fixed64_u(UINT64(_val))
end
n = n + 1; out[n] = _tag
local _b = table.concat(entry)
@@ 2071,11 2074,11 @@ function M.TestAllTypesProto2_encode(t)
local entry, _m = {}, 0
if _k ~= 0 then
_m = _m + 1; entry[_m] = _ktag
- _m = _m + 1; entry[_m] = encode_sfixed64(_k)
+ _m = _m + 1; entry[_m] = encode_sfixed64_u(UINT64(_k))
end
if _val ~= 0 then
_m = _m + 1; entry[_m] = _vtag
- _m = _m + 1; entry[_m] = encode_sfixed64(_val)
+ _m = _m + 1; entry[_m] = encode_sfixed64_u(UINT64(_val))
end
n = n + 1; out[n] = _tag
local _b = table.concat(entry)
@@ 2499,7 2502,7 @@ function M.TestAllTypesProto2_encode(t)
v = t.default_sint64
if v ~= nil then
n = n + 1; out[n] = "\xb0\x0f"
- n = n + 1; out[n] = encode_sint64(v)
+ n = n + 1; out[n] = encode_sint64_i(INT64(v))
end
-- field 247: default_fixed32
v = t.default_fixed32
@@ 2511,7 2514,7 @@ function M.TestAllTypesProto2_encode(t)
v = t.default_fixed64
if v ~= nil then
n = n + 1; out[n] = "\xc1\x0f"
- n = n + 1; out[n] = encode_fixed64(v)
+ n = n + 1; out[n] = encode_fixed64_u(UINT64(v))
end
-- field 249: default_sfixed32
v = t.default_sfixed32
@@ 2523,7 2526,7 @@ function M.TestAllTypesProto2_encode(t)
v = t.default_sfixed64
if v ~= nil then
n = n + 1; out[n] = "\xd1\x0f"
- n = n + 1; out[n] = encode_sfixed64(v)
+ n = n + 1; out[n] = encode_sfixed64_u(UINT64(v))
end
-- field 251: default_float
v = t.default_float
@@ 9920,14 9923,14 @@ function M.TestAllRequiredTypesProto2_encode(t)
local encode_bool = wire.encode_bool
local encode_double = wire.encode_double
local encode_fixed32 = wire.encode_fixed32
- local encode_fixed64 = wire.encode_fixed64
+ local encode_fixed64_u = wire.encode_fixed64_u
local encode_float = wire.encode_float
local encode_int32 = wire.encode_int32
local encode_int64 = wire.encode_int64
local encode_sfixed32 = wire.encode_sfixed32
- local encode_sfixed64 = wire.encode_sfixed64
+ local encode_sfixed64_u = wire.encode_sfixed64_u
local encode_sint32 = wire.encode_sint32
- local encode_sint64 = wire.encode_sint64
+ local encode_sint64_i = wire.encode_sint64_i
local encode_uint32 = wire.encode_uint32
local encode_uint64 = wire.encode_uint64
local encode_varint = wire.encode_varint
@@ 9982,7 9985,7 @@ function M.TestAllRequiredTypesProto2_encode(t)
error("required field missing on encode: protobuf_test_messages.proto2.TestAllRequiredTypesProto2.required_sint64", 0)
end
n = n + 1; out[n] = "\x30"
- n = n + 1; out[n] = encode_sint64(v)
+ n = n + 1; out[n] = encode_sint64_i(INT64(v))
-- field 7: required_fixed32
v = t.required_fixed32
if v == nil then
@@ 9996,7 9999,7 @@ function M.TestAllRequiredTypesProto2_encode(t)
error("required field missing on encode: protobuf_test_messages.proto2.TestAllRequiredTypesProto2.required_fixed64", 0)
end
n = n + 1; out[n] = "\x41"
- n = n + 1; out[n] = encode_fixed64(v)
+ n = n + 1; out[n] = encode_fixed64_u(UINT64(v))
-- field 9: required_sfixed32
v = t.required_sfixed32
if v == nil then
@@ 10010,7 10013,7 @@ function M.TestAllRequiredTypesProto2_encode(t)
error("required field missing on encode: protobuf_test_messages.proto2.TestAllRequiredTypesProto2.required_sfixed64", 0)
end
n = n + 1; out[n] = "\x51"
- n = n + 1; out[n] = encode_sfixed64(v)
+ n = n + 1; out[n] = encode_sfixed64_u(UINT64(v))
-- field 11: required_float
v = t.required_float
if v == nil then
@@ 10222,7 10225,7 @@ function M.TestAllRequiredTypesProto2_encode(t)
error("required field missing on encode: protobuf_test_messages.proto2.TestAllRequiredTypesProto2.default_sint64", 0)
end
n = n + 1; out[n] = "\xb0\x0f"
- n = n + 1; out[n] = encode_sint64(v)
+ n = n + 1; out[n] = encode_sint64_i(INT64(v))
-- field 247: default_fixed32
v = t.default_fixed32
if v == nil then
@@ 10236,7 10239,7 @@ function M.TestAllRequiredTypesProto2_encode(t)
error("required field missing on encode: protobuf_test_messages.proto2.TestAllRequiredTypesProto2.default_fixed64", 0)
end
n = n + 1; out[n] = "\xc1\x0f"
- n = n + 1; out[n] = encode_fixed64(v)
+ n = n + 1; out[n] = encode_fixed64_u(UINT64(v))
-- field 249: default_sfixed32
v = t.default_sfixed32
if v == nil then
@@ 10250,7 10253,7 @@ function M.TestAllRequiredTypesProto2_encode(t)
error("required field missing on encode: protobuf_test_messages.proto2.TestAllRequiredTypesProto2.default_sfixed64", 0)
end
n = n + 1; out[n] = "\xd1\x0f"
- n = n + 1; out[n] = encode_sfixed64(v)
+ n = n + 1; out[n] = encode_sfixed64_u(UINT64(v))
-- field 251: default_float
v = t.default_float
if v == nil then
M examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua => examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua +24 -21
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
@@ 602,15 605,15 @@ function M.TestAllTypesProto3_encode(t)
local encode_bool = wire.encode_bool
local encode_double = wire.encode_double
local encode_fixed32 = wire.encode_fixed32
- local encode_fixed64 = wire.encode_fixed64
+ local encode_fixed64_u = wire.encode_fixed64_u
local encode_float = wire.encode_float
local encode_int32 = wire.encode_int32
local encode_int64 = wire.encode_int64
local encode_len = wire.encode_len
local encode_sfixed32 = wire.encode_sfixed32
- local encode_sfixed64 = wire.encode_sfixed64
+ local encode_sfixed64_u = wire.encode_sfixed64_u
local encode_sint32 = wire.encode_sint32
- local encode_sint64 = wire.encode_sint64
+ local encode_sint64_i = wire.encode_sint64_i
local encode_string = wire.encode_string
local encode_uint32 = wire.encode_uint32
local encode_uint64 = wire.encode_uint64
@@ 670,7 673,7 @@ function M.TestAllTypesProto3_encode(t)
v = t.optional_sint64
if v ~= nil and v ~= 0 then
n = n + 1; out[n] = "\x30"
- n = n + 1; out[n] = encode_sint64(v)
+ n = n + 1; out[n] = encode_sint64_i(INT64(v))
end
-- field 7: optional_fixed32
v = t.optional_fixed32
@@ 682,7 685,7 @@ function M.TestAllTypesProto3_encode(t)
v = t.optional_fixed64
if v ~= nil and v ~= 0 then
n = n + 1; out[n] = "\x41"
- n = n + 1; out[n] = encode_fixed64(v)
+ n = n + 1; out[n] = encode_fixed64_u(UINT64(v))
end
-- field 9: optional_sfixed32
v = t.optional_sfixed32
@@ 694,7 697,7 @@ function M.TestAllTypesProto3_encode(t)
v = t.optional_sfixed64
if v ~= nil and v ~= 0 then
n = n + 1; out[n] = "\x51"
- n = n + 1; out[n] = encode_sfixed64(v)
+ n = n + 1; out[n] = encode_sfixed64_u(UINT64(v))
end
-- field 11: optional_float
v = t.optional_float
@@ 965,7 968,7 @@ function M.TestAllTypesProto3_encode(t)
if type(_e) == 'number' and _e >= -64 and _e <= 63 then
m = m + 1; parts[m] = CHARS[bit.bxor(bit.lshift(_e, 1), bit.arshift(_e, 31))]
else
- m = m + 1; parts[m] = encode_sint64(_e)
+ m = m + 1; parts[m] = encode_sint64_i(INT64(_e))
end
end
local _b = table.concat(parts)
@@ 1002,7 1005,7 @@ function M.TestAllTypesProto3_encode(t)
local _n = #v
local parts, m = table_new(_n, 0), 0
for _i = 1, _n do
- m = m + 1; parts[m] = encode_fixed64(v[_i])
+ m = m + 1; parts[m] = encode_fixed64_u(UINT64(v[_i]))
end
local _b = table.concat(parts)
n = n + 1; out[n] = "\xb2\x02"
@@ 1038,7 1041,7 @@ function M.TestAllTypesProto3_encode(t)
local _n = #v
local parts, m = table_new(_n, 0), 0
for _i = 1, _n do
- m = m + 1; parts[m] = encode_sfixed64(v[_i])
+ m = m + 1; parts[m] = encode_sfixed64_u(UINT64(v[_i]))
end
local _b = table.concat(parts)
n = n + 1; out[n] = "\xc2\x02"
@@ 1383,7 1386,7 @@ function M.TestAllTypesProto3_encode(t)
if type(_e) == 'number' and _e >= -64 and _e <= 63 then
m = m + 1; parts[m] = CHARS[bit.bxor(bit.lshift(_e, 1), bit.arshift(_e, 31))]
else
- m = m + 1; parts[m] = encode_sint64(_e)
+ m = m + 1; parts[m] = encode_sint64_i(INT64(_e))
end
end
local _b = table.concat(parts)
@@ 1420,7 1423,7 @@ function M.TestAllTypesProto3_encode(t)
local _n = #v
local parts, m = table_new(_n, 0), 0
for _i = 1, _n do
- m = m + 1; parts[m] = encode_fixed64(v[_i])
+ m = m + 1; parts[m] = encode_fixed64_u(UINT64(v[_i]))
end
local _b = table.concat(parts)
n = n + 1; out[n] = "\x92\x05"
@@ 1456,7 1459,7 @@ function M.TestAllTypesProto3_encode(t)
local _n = #v
local parts, m = table_new(_n, 0), 0
for _i = 1, _n do
- m = m + 1; parts[m] = encode_sfixed64(v[_i])
+ m = m + 1; parts[m] = encode_sfixed64_u(UINT64(v[_i]))
end
local _b = table.concat(parts)
n = n + 1; out[n] = "\xa2\x05"
@@ 1602,7 1605,7 @@ function M.TestAllTypesProto3_encode(t)
local _tag = "\xf0\x05"
for _i = 1, #v do
n = n + 1; out[n] = _tag
- n = n + 1; out[n] = encode_sint64(v[_i])
+ n = n + 1; out[n] = encode_sint64_i(INT64(v[_i]))
end
end
-- field 95: unpacked_fixed32
@@ 1620,7 1623,7 @@ function M.TestAllTypesProto3_encode(t)
local _tag = "\x81\x06"
for _i = 1, #v do
n = n + 1; out[n] = _tag
- n = n + 1; out[n] = encode_fixed64(v[_i])
+ n = n + 1; out[n] = encode_fixed64_u(UINT64(v[_i]))
end
end
-- field 97: unpacked_sfixed32
@@ 1638,7 1641,7 @@ function M.TestAllTypesProto3_encode(t)
local _tag = "\x91\x06"
for _i = 1, #v do
n = n + 1; out[n] = _tag
- n = n + 1; out[n] = encode_sfixed64(v[_i])
+ n = n + 1; out[n] = encode_sfixed64_u(UINT64(v[_i]))
end
end
-- field 99: unpacked_float
@@ 1830,11 1833,11 @@ function M.TestAllTypesProto3_encode(t)
local entry, _m = {}, 0
if _k ~= 0 then
_m = _m + 1; entry[_m] = _ktag
- _m = _m + 1; entry[_m] = encode_sint64(_k)
+ _m = _m + 1; entry[_m] = encode_sint64_i(INT64(_k))
end
if _val ~= 0 then
_m = _m + 1; entry[_m] = _vtag
- _m = _m + 1; entry[_m] = encode_sint64(_val)
+ _m = _m + 1; entry[_m] = encode_sint64_i(INT64(_val))
end
n = n + 1; out[n] = _tag
local _b = table.concat(entry)
@@ 1880,11 1883,11 @@ function M.TestAllTypesProto3_encode(t)
local entry, _m = {}, 0
if _k ~= 0 then
_m = _m + 1; entry[_m] = _ktag
- _m = _m + 1; entry[_m] = encode_fixed64(_k)
+ _m = _m + 1; entry[_m] = encode_fixed64_u(UINT64(_k))
end
if _val ~= 0 then
_m = _m + 1; entry[_m] = _vtag
- _m = _m + 1; entry[_m] = encode_fixed64(_val)
+ _m = _m + 1; entry[_m] = encode_fixed64_u(UINT64(_val))
end
n = n + 1; out[n] = _tag
local _b = table.concat(entry)
@@ 1930,11 1933,11 @@ function M.TestAllTypesProto3_encode(t)
local entry, _m = {}, 0
if _k ~= 0 then
_m = _m + 1; entry[_m] = _ktag
- _m = _m + 1; entry[_m] = encode_sfixed64(_k)
+ _m = _m + 1; entry[_m] = encode_sfixed64_u(UINT64(_k))
end
if _val ~= 0 then
_m = _m + 1; entry[_m] = _vtag
- _m = _m + 1; entry[_m] = encode_sfixed64(_val)
+ _m = _m + 1; entry[_m] = encode_sfixed64_u(UINT64(_val))
end
n = n + 1; out[n] = _tag
local _b = table.concat(entry)
M examples/expected/full/quickstart/quickstart_pb.lua => examples/expected/full/quickstart/quickstart_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/runtime/c_int64/c_int64_pb.lua => examples/expected/runtime/c_int64/c_int64_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/runtime/c_nested/c_nested_pb.lua => examples/expected/runtime/c_nested/c_nested_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/runtime/c_repeated/c_repeated_pb.lua => examples/expected/runtime/c_repeated/c_repeated_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/runtime/conformance/conformance_pb.lua => examples/expected/runtime/conformance/conformance_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/runtime/hello/hello_pb.lua => examples/expected/runtime/hello/hello_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/runtime/proto2_basic/proto2_basic_pb.lua => examples/expected/runtime/proto2_basic/proto2_basic_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/runtime/protobuf_test_messages/proto2/test_messages_proto2_pb.lua => examples/expected/runtime/protobuf_test_messages/proto2/test_messages_proto2_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/runtime/protobuf_test_messages/proto3/test_messages_proto3_pb.lua => examples/expected/runtime/protobuf_test_messages/proto3/test_messages_proto3_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M examples/expected/runtime/quickstart/quickstart_pb.lua => examples/expected/runtime/quickstart/quickstart_pb.lua +3 -0
@@ 11,6 11,9 @@ local band = bit.band
local rshift = bit.rshift
local CHARS = wire.CHARS
local table_new = require('table.new')
+local ffi = require('ffi')
+local INT64 = ffi.typeof('int64_t')
+local UINT64 = ffi.typeof('uint64_t')
local M = {}
M runtime/pb/wire.lua => runtime/pb/wire.lua +30 -5
@@ 286,6 286,16 @@ local function zigzag_encode64(n)
end
M.zigzag_encode64 = zigzag_encode64
+-- Variant that takes an int64_t cdata directly, skipping the to_int64()
+-- type-dispatch wrapper. Use from mode=full codegen sites where the field
+-- type is statically known (a7l).
+local function zigzag_encode64_i(i)
+ local doubled = bit.lshift(UINT64(i), 1)
+ if i >= 0 then return doubled end
+ return bit.bnot(doubled)
+end
+M.zigzag_encode64_i = zigzag_encode64_i
+
local function zigzag_decode64(u)
u = to_uint64(u)
local half = bit.rshift(u, 1)
@@ 323,8 333,8 @@ local function decode_fixed32(buf, pos)
end
M.decode_fixed32 = decode_fixed32
-local function encode_fixed64(v)
- local u = to_uint64(v)
+-- Body shared by encode_fixed64 and encode_fixed64_u. Takes a uint64_t cdata.
+local function encode_fixed64_u(u)
local lo = tonumber(bit.band(u, 0xffffffff))
local hi = tonumber(bit.rshift(u, 32))
return string.char(
@@ 337,7 347,16 @@ local function encode_fixed64(v)
bit.band(bit.rshift(hi, 16), 0xff),
bit.band(bit.rshift(hi, 24), 0xff))
end
-M.encode_fixed64 = encode_fixed64
+-- Generic wrapper: accepts Lua number / cdata / boolean via to_uint64.
+local function encode_fixed64(v)
+ return encode_fixed64_u(to_uint64(v))
+end
+M.encode_fixed64 = encode_fixed64
+-- Variant for mode=full codegen sites where the value is already a uint64_t
+-- cdata (caller wraps with UINT64() at the static-type-known call site).
+-- Used for fixed64 and sfixed64 (sfixed64 reuses this body — same bit
+-- pattern, different sign interpretation). (a7l)
+M.encode_fixed64_u = encode_fixed64_u
-- decode_fixed64(buf, pos) -> uint64_t cdata, new_pos
-- Reading via the FFI union avoids the multi-step `UINT64(lo) +
@@ 461,8 480,14 @@ M.encode_sint32 = encode_sint32
M.encode_sint64 = encode_sint64
M.encode_bool = encode_bool
-M.encode_sfixed32 = encode_fixed32 -- bits are identical, only interpretation differs
-M.encode_sfixed64 = encode_fixed64
+-- Fast variant for mode=full codegen: takes an int64_t cdata, skips to_int64.
+local function encode_sint64_i(i) return encode_varint(zigzag_encode64_i(i)) end
+M.encode_sint64_i = encode_sint64_i
+
+M.encode_sfixed32 = encode_fixed32 -- bits are identical, only interpretation differs
+M.encode_sfixed64 = encode_fixed64
+-- sfixed64 reuses encode_fixed64_u — same 8-byte little-endian shape.
+M.encode_sfixed64_u = encode_fixed64_u
M.encode_string = encode_len
M.encode_bytes = encode_len
-- (encode_fixed32, encode_fixed64, encode_float, encode_double already on M)