diff --git a/bench/baseline.json b/bench/baseline.json index 813d4bf0118102f9d18518b6630618a27e55675a..6264ff9d028cd369c95c4e38912bc7152f1370e4 100644 --- a/bench/baseline.json +++ b/bench/baseline.json @@ -1,35 +1,56 @@ { - "schema_message": "hello.Person", - "results": [ + "schemas": [ { - "size_label": "10B", - "size_bytes": 10, - "encode": {"alloc_kb_per_op_full": 0.367, "alloc_kb_per_op_runtime": 0.633}, - "decode": {"alloc_kb_per_op_full": 0.508, "alloc_kb_per_op_runtime": 0.508} - }, - { - "size_label": "100B", - "size_bytes": 94, - "encode": {"alloc_kb_per_op_full": 0.367, "alloc_kb_per_op_runtime": 0.633}, - "decode": {"alloc_kb_per_op_full": 0.508, "alloc_kb_per_op_runtime": 0.508} - }, - { - "size_label": "1KB", - "size_bytes": 930, - "encode": {"alloc_kb_per_op_full": 5.978, "alloc_kb_per_op_runtime": 7.040}, - "decode": {"alloc_kb_per_op_full": 7.750, "alloc_kb_per_op_runtime": 7.883} - }, - { - "size_label": "10KB", - "size_bytes": 9634, - "encode": {"alloc_kb_per_op_full": 47.489, "alloc_kb_per_op_runtime": 48.551}, - "decode": {"alloc_kb_per_op_full": 59.500, "alloc_kb_per_op_runtime": 59.633} + "schema": "hello.Person", + "results": [ + { + "size_label": "10B", + "size_bytes": 10, + "encode": {"alloc_kb_per_op_full": 0.133, "alloc_kb_per_op_runtime": 0.133}, + "decode": {"alloc_kb_per_op_full": 0.109, "alloc_kb_per_op_runtime": 0.109} + }, + { + "size_label": "100B", + "size_bytes": 94, + "encode": {"alloc_kb_per_op_full": 0.133, "alloc_kb_per_op_runtime": 0.133}, + "decode": {"alloc_kb_per_op_full": 0.109, "alloc_kb_per_op_runtime": 0.109} + }, + { + "size_label": "1KB", + "size_bytes": 930, + "encode": {"alloc_kb_per_op_full": 1.336, "alloc_kb_per_op_runtime": 1.336}, + "decode": {"alloc_kb_per_op_full": 0.977, "alloc_kb_per_op_runtime": 0.977} + }, + { + "size_label": "10KB", + "size_bytes": 9634, + "encode": {"alloc_kb_per_op_full": 8.340, "alloc_kb_per_op_runtime": 8.340}, + "decode": {"alloc_kb_per_op_full": 4.727, "alloc_kb_per_op_runtime": 4.727} + }, + { + "size_label": "100KB", + "size_bytes": 96674, + "encode": {"alloc_kb_per_op_full": 128.521, "alloc_kb_per_op_runtime": 128.521}, + "decode": {"alloc_kb_per_op_full": 32.727, "alloc_kb_per_op_runtime": 32.727} + } + ] }, { - "size_label": "100KB", - "size_bytes": 96674, - "encode": {"alloc_kb_per_op_full": 444.126, "alloc_kb_per_op_runtime": 445.189}, - "decode": {"alloc_kb_per_op_full": 567.500, "alloc_kb_per_op_runtime": 567.633} + "schema": "proto2_basic.BenchPayload", + "results": [ + { + "size_label": "min", + "size_bytes": 5, + "encode": {"alloc_kb_per_op_full": 0.102, "alloc_kb_per_op_runtime": 0.102}, + "decode": {"alloc_kb_per_op_full": 0.359, "alloc_kb_per_op_runtime": 0.359} + }, + { + "size_label": "mid", + "size_bytes": 609, + "encode": {"alloc_kb_per_op_full": 1.438, "alloc_kb_per_op_runtime": 1.438}, + "decode": {"alloc_kb_per_op_full": 2.313, "alloc_kb_per_op_runtime": 2.313} + } + ] } ] } diff --git a/bench/bench.lua b/bench/bench.lua index 356b9c35370568c2e4fbadd048efd1fa2e75190c..bcb14e5927dc81efa66f81ba298a302b48d0ca6a 100644 --- a/bench/bench.lua +++ b/bench/bench.lua @@ -22,20 +22,30 @@ local json = require('json') local fio = require('fio') local MODES = {'full', 'runtime'} -local SIZES = { + +-- Per-fixture size lists. The proto3 Person fixture sweeps five decades to +-- characterize behavior across allocation regimes; proto2 only needs two +-- sizes (one fits-in-cache, one larger) because its purpose is to gate +-- the proto2-specific shapes (required/group/extension), not to recharacterize +-- alloc scaling — that's already covered by Person. +local PERSON_SIZES = { {label = '10B', target = 10}, {label = '100B', target = 100}, {label = '1KB', target = 1024}, {label = '10KB', target = 10240}, {label = '100KB', target = 102400}, } +local PROTO2_SIZES = { + {label = 'min', target = 0}, -- minimal: required + extension only + {label = 'mid', target = 1024}, -- with group + repeated + extension +} -- Build a `Person` payload whose encoded size is close to `target` bytes. -- -- Strategy: pick one knob per decade so each size still exercises the -- full encoder (varints, packed repeated, length-delimited strings, -- nested messages) — not just one giant byte-string. -local function build_payload(target) +local function build_person_payload(target) if target <= 10 then -- name(6) + age(1) ⇒ 10 bytes encoded. return {name = 'bigbes', age = 42} @@ -65,6 +75,65 @@ end return p end +-- Build a `BenchPayload` proto2 message. Two sizes: +-- * `min` — required + one extension (~12 bytes): tightest measure of the +-- per-message overhead the new code paths add. +-- * `mid` — required + group + repeated + extension (~1 KB): exercises +-- SGROUP/EGROUP framing, the packed-int32 path under proto2 semantics +-- (NOT packed unless explicitly marked), the `pairs`-free +-- extensions_list walk, and the nested message branch. +local function build_proto2_payload(target) + if target <= 0 then + return { + id = 7, + _extensions = { + ['proto2_basic.ext_count'] = 42, + }, + } + end + local n_tags = 16 + local per_tag = 30 + local tags = {} + for i = 1, n_tags do + tags[i] = string.rep('t', per_tag - 2) .. string.format('%02d', i) + end + local lucky = {} + for i = 1, 8 do lucky[i] = 1000 + i end + return { + id = 7, + name = 'bench', + retries = 9, + lucky_numbers = lucky, + tags = tags, + inner = {key = string.rep('k', 16), weight = 3}, + stats = {latency_ns = 1234567, attempts = 4}, + _extensions = { + ['proto2_basic.ext_count'] = 99, + ['proto2_basic.ext_label'] = string.rep('x', 32), + }, + } +end + +-- Each fixture binds a schema + payload builder + sizes to bench. +local FIXTURES = { + { + name = 'hello.Person', + module_path = '.hello.hello_pb', + encode_field = 'Person_encode', + decode_field = 'Person_decode', + build_payload = build_person_payload, + sizes = PERSON_SIZES, + }, + { + name = 'proto2_basic.BenchPayload', + module_path = '.proto2_basic.proto2_basic_pb', + encode_field = 'BenchPayload_encode', + decode_field = 'BenchPayload_decode', + build_payload = build_proto2_payload, + sizes = PROTO2_SIZES, + }, +} + -- Pick iteration count adaptively: smaller messages need more iters to -- amortize loop + clock overhead; larger messages need fewer to keep -- wall time bounded. @@ -142,12 +211,12 @@ iters = n, } end -local function bench_one(mode, size) - local hello_pb = require(mode .. '.hello.hello_pb') - local encode = hello_pb.Person_encode - local decode = hello_pb.Person_decode +local function bench_one(fixture, mode, size) + local mod = require(mode .. fixture.module_path) + local encode = mod[fixture.encode_field] + local decode = mod[fixture.decode_field] - local payload = build_payload(size.target) + local payload = fixture.build_payload(size.target) local bytes = encode(payload) local n = iter_count(#bytes) local runs = 5 @@ -168,6 +237,7 @@ dec_throughput.alloc_kb_per_op = dec_alloc.kb_per_op dec_throughput.alloc_bytes_per_op = dec_alloc.bytes_per_op return { + schema = fixture.name, mode = mode, size_label = size.label, size_bytes = #bytes, @@ -177,24 +247,28 @@ } end local function run_all() - local results = {} - for _, mode in ipairs(MODES) do - for _, size in ipairs(SIZES) do - io.stderr:write(string.format(' bench %s/%s ... ', mode, size.label)) - io.stderr:flush() - local r = bench_one(mode, size) - io.stderr:write(string.format( - 'enc %.0f msgs/s (%.1f MB/s) dec %.0f msgs/s (%.1f MB/s)\n', - r.encode.msgs_per_s, r.encode.mb_per_s, - r.decode.msgs_per_s, r.decode.mb_per_s)) - results[#results + 1] = r + local schemas = {} + for _, fixture in ipairs(FIXTURES) do + io.stderr:write(string.format('schema %s\n', fixture.name)) + local results = {} + for _, mode in ipairs(MODES) do + for _, size in ipairs(fixture.sizes) do + io.stderr:write(string.format(' bench %s/%s ... ', mode, size.label)) + io.stderr:flush() + local r = bench_one(fixture, mode, size) + io.stderr:write(string.format( + 'enc %.0f msgs/s (%.1f MB/s) dec %.0f msgs/s (%.1f MB/s)\n', + r.encode.msgs_per_s, r.encode.mb_per_s, + r.decode.msgs_per_s, r.decode.mb_per_s)) + results[#results + 1] = r + end end + schemas[#schemas + 1] = {schema = fixture.name, results = results} end return { - tarantool = _TARANTOOL, - jit = jit and jit.version or nil, - schema_message = 'hello.Person', - results = results, + tarantool = _TARANTOOL, + jit = jit and jit.version or nil, + schemas = schemas, } end @@ -216,17 +290,24 @@ local lines = {} lines[#lines + 1] = '{' lines[#lines + 1] = string.format(' "tarantool": %s,', json.encode(doc.tarantool)) lines[#lines + 1] = string.format(' "jit": %s,', json.encode(doc.jit or json.NULL)) - lines[#lines + 1] = string.format(' "schema_message": %s,', json.encode(doc.schema_message)) - lines[#lines + 1] = ' "results": [' - for i, r in ipairs(doc.results) do - local sep = (i == #doc.results) and '' or ',' + lines[#lines + 1] = ' "schemas": [' + for si, schema in ipairs(doc.schemas) do + local schema_sep = (si == #doc.schemas) and '' or ',' lines[#lines + 1] = ' {' - lines[#lines + 1] = string.format(' "mode": %s,', json.encode(r.mode)) - lines[#lines + 1] = string.format(' "size_label": %s,', json.encode(r.size_label)) - lines[#lines + 1] = string.format(' "size_bytes": %d,', r.size_bytes) - lines[#lines + 1] = string.format(' "encode": %s,', render_metric(r.encode)) - lines[#lines + 1] = string.format(' "decode": %s', render_metric(r.decode)) - lines[#lines + 1] = ' }' .. sep + lines[#lines + 1] = string.format(' "schema": %s,', json.encode(schema.schema)) + lines[#lines + 1] = ' "results": [' + for i, r in ipairs(schema.results) do + local sep = (i == #schema.results) and '' or ',' + lines[#lines + 1] = ' {' + lines[#lines + 1] = string.format(' "mode": %s,', json.encode(r.mode)) + lines[#lines + 1] = string.format(' "size_label": %s,', json.encode(r.size_label)) + lines[#lines + 1] = string.format(' "size_bytes": %d,', r.size_bytes) + lines[#lines + 1] = string.format(' "encode": %s,', render_metric(r.encode)) + lines[#lines + 1] = string.format(' "decode": %s', render_metric(r.decode)) + lines[#lines + 1] = ' }' .. sep + end + lines[#lines + 1] = ' ]' + lines[#lines + 1] = ' }' .. schema_sep end lines[#lines + 1] = ' ]' lines[#lines + 1] = '}' @@ -239,50 +320,69 @@ -- reproducible to within ~10 bytes regardless of machine — it counts -- bytes, not time — so that's all we commit. Throughput is in --print -- output for human inspection only. local function reduce_for_baseline(doc) - local by_key = {} - for _, r in ipairs(doc.results) do - by_key[r.mode .. '/' .. r.size_label] = r - end - local out = {} - for _, size in ipairs(SIZES) do - local full = by_key['full/' .. size.label] - local runtime = by_key['runtime/' .. size.label] - out[#out + 1] = { - size_label = size.label, - size_bytes = full.size_bytes, - encode = { - alloc_kb_per_op_full = full.encode.alloc_kb_per_op, - alloc_kb_per_op_runtime = runtime.encode.alloc_kb_per_op, - }, - decode = { - alloc_kb_per_op_full = full.decode.alloc_kb_per_op, - alloc_kb_per_op_runtime = runtime.decode.alloc_kb_per_op, - }, - } + local out_schemas = {} + for _, schema in ipairs(doc.schemas) do + local by_key = {} + for _, r in ipairs(schema.results) do + by_key[r.mode .. '/' .. r.size_label] = r + end + -- Recover size order from the first mode's run (declaration order). + local seen, sizes = {}, {} + for _, r in ipairs(schema.results) do + if not seen[r.size_label] then + seen[r.size_label] = true + sizes[#sizes + 1] = r.size_label + end + end + local results = {} + for _, label in ipairs(sizes) do + local full = by_key['full/' .. label] + local runtime = by_key['runtime/' .. label] + results[#results + 1] = { + size_label = label, + size_bytes = full.size_bytes, + encode = { + alloc_kb_per_op_full = full.encode.alloc_kb_per_op, + alloc_kb_per_op_runtime = runtime.encode.alloc_kb_per_op, + }, + decode = { + alloc_kb_per_op_full = full.decode.alloc_kb_per_op, + alloc_kb_per_op_runtime = runtime.decode.alloc_kb_per_op, + }, + } + end + out_schemas[#out_schemas + 1] = {schema = schema.schema, results = results} end - return {schema_message = doc.schema_message, results = out} + return {schemas = out_schemas} end local function render_baseline(reduced) local lines = {'{'} - lines[#lines + 1] = string.format(' "schema_message": %s,', json.encode(reduced.schema_message)) - lines[#lines + 1] = ' "results": [' - for i, r in ipairs(reduced.results) do - local sep = (i == #reduced.results) and '' or ',' + lines[#lines + 1] = ' "schemas": [' + for si, schema in ipairs(reduced.schemas) do + local schema_sep = (si == #reduced.schemas) and '' or ',' lines[#lines + 1] = ' {' - lines[#lines + 1] = string.format(' "size_label": %s,', json.encode(r.size_label)) - lines[#lines + 1] = string.format(' "size_bytes": %d,', r.size_bytes) - lines[#lines + 1] = string.format( - ' "encode": {"alloc_kb_per_op_full": %.3f, ' - .. '"alloc_kb_per_op_runtime": %.3f},', - r.encode.alloc_kb_per_op_full, - r.encode.alloc_kb_per_op_runtime) - lines[#lines + 1] = string.format( - ' "decode": {"alloc_kb_per_op_full": %.3f, ' - .. '"alloc_kb_per_op_runtime": %.3f}', - r.decode.alloc_kb_per_op_full, - r.decode.alloc_kb_per_op_runtime) - lines[#lines + 1] = ' }' .. sep + lines[#lines + 1] = string.format(' "schema": %s,', json.encode(schema.schema)) + lines[#lines + 1] = ' "results": [' + for i, r in ipairs(schema.results) do + local sep = (i == #schema.results) and '' or ',' + lines[#lines + 1] = ' {' + lines[#lines + 1] = string.format(' "size_label": %s,', json.encode(r.size_label)) + lines[#lines + 1] = string.format(' "size_bytes": %d,', r.size_bytes) + lines[#lines + 1] = string.format( + ' "encode": {"alloc_kb_per_op_full": %.3f, ' + .. '"alloc_kb_per_op_runtime": %.3f},', + r.encode.alloc_kb_per_op_full, + r.encode.alloc_kb_per_op_runtime) + lines[#lines + 1] = string.format( + ' "decode": {"alloc_kb_per_op_full": %.3f, ' + .. '"alloc_kb_per_op_runtime": %.3f}', + r.decode.alloc_kb_per_op_full, + r.decode.alloc_kb_per_op_runtime) + lines[#lines + 1] = ' }' .. sep + end + lines[#lines + 1] = ' ]' + lines[#lines + 1] = ' }' .. schema_sep end lines[#lines + 1] = ' ]' lines[#lines + 1] = '}' @@ -299,27 +399,36 @@ -- ratios swing 30%+ run-to-run on a busy laptop — useless as a gate. local function compare(current, baseline, tolerance) local function index(b) local m = {} - for _, r in ipairs(b.results) do m[r.size_label] = r end + for _, schema in ipairs(b.schemas or {}) do + for _, r in ipairs(schema.results) do + m[schema.schema .. '|' .. r.size_label] = r + end + end return m end - local cur = index(current) + local cur = index(current) local base = index(baseline) local regressions = {} - for _, size in ipairs(SIZES) do - local c = cur[size.label] - local b = base[size.label] - if not c or not b then goto continue end - for _, op in ipairs({'encode', 'decode'}) do - for _, key in ipairs({'alloc_kb_per_op_full', 'alloc_kb_per_op_runtime'}) do - local bv, cv = b[op][key], c[op][key] - if bv > 0 and cv > bv * (1 + tolerance) then - regressions[#regressions + 1] = string.format( - '%s/%s %s: %.3f -> %.3f KB/op (+%.1f%%)', - size.label, op, key, bv, cv, (cv / bv - 1) * 100) + -- Walk in baseline declaration order so the report is stable. + for _, schema in ipairs(baseline.schemas or {}) do + for _, r in ipairs(schema.results) do + local key = schema.schema .. '|' .. r.size_label + local c = cur[key] + local b = base[key] + if c and b then + for _, op in ipairs({'encode', 'decode'}) do + for _, ak in ipairs({'alloc_kb_per_op_full', 'alloc_kb_per_op_runtime'}) do + local bv, cv = b[op][ak], c[op][ak] + if bv > 0 and cv > bv * (1 + tolerance) then + regressions[#regressions + 1] = string.format( + '%s %s/%s %s: %.3f -> %.3f KB/op (+%.1f%%)', + schema.schema, r.size_label, op, ak, + bv, cv, (cv / bv - 1) * 100) + end + end end end end - ::continue:: end return regressions end diff --git a/bench/jit_trace.lua b/bench/jit_trace.lua index 68b6ef38eac033a4975ea95c5761e87cf66dea8e..ce852452b7ac9ba2a14a58cdaba45e65f020f10b 100644 --- a/bench/jit_trace.lua +++ b/bench/jit_trace.lua @@ -269,6 +269,76 @@ check(mode .. '/Person_decode multi-byte varint', function() hello.Person_decode(big_person_bytes) end) end +-- --------------------------------------------------------------------------- +-- Proto2-specific shapes: required, groups, extensions, closed enums. +-- +-- The new code paths share most of their machinery with proto3 message and +-- scalar fields, but introduce three distinct shapes worth pinning: +-- +-- 1. The `required` writer wraps the regular scalar writer with a missing- +-- value error. Should stay JIT-stable when the value is set. +-- 2. Group fields use SGROUP/EGROUP tags and a per-element body without a +-- length prefix. Different wire shape than messages; new writer/reader. +-- 3. Extensions are walked at encode time via a `pairs()` over an array +-- view (`extensions_list`), and looked up at decode time via the +-- `extensions_by_id` hash. Both sides need to stay on trace; the +-- hash lookup is one cdata read so should be fine, but the encode +-- walk would abort if we used `pairs()` over a non-array — see the +-- list-view layer in pb.codec. +-- --------------------------------------------------------------------------- + +for _, mode in ipairs({'full', 'runtime'}) do + local p2 = require(mode .. '.proto2_basic.proto2_basic_pb') + + -- Required: encode emits a clear error on missing, but the happy path + -- where the value is set should compile and stay on trace. + local card = {r = 7} + local card_bytes = p2.Cardinality_encode(card) + check(mode .. '/Cardinality_encode (required)', + function() p2.Cardinality_encode(card) end) + check(mode .. '/Cardinality_decode (required)', + function() p2.Cardinality_decode(card_bytes) end) + + -- Groups: SGROUP/EGROUP framed body, no length prefix. Singular path. + local with_group = {singlegroup = {a = 7, s = 'ok'}} + local with_group_bytes = p2.WithGroup_encode(with_group) + check(mode .. '/WithGroup_encode (group)', + function() p2.WithGroup_encode(with_group) end) + check(mode .. '/WithGroup_decode (group)', + function() p2.WithGroup_decode(with_group_bytes) end) + + -- Repeated groups: same shape, exercises the per-element bracket loop. + local with_rep = {repgroup = {{n = 1}, {n = 2}, {n = 3}, {n = 4}}} + local with_rep_bytes = p2.WithGroup_encode(with_rep) + check(mode .. '/WithGroup_encode (repeated group)', + function() p2.WithGroup_encode(with_rep) end) + check(mode .. '/WithGroup_decode (repeated group)', + function() p2.WithGroup_decode(with_rep_bytes) end) +end + +-- Extensions: register a small proto2 fixture inline so the trace gate +-- doesn't depend on the bulky conformance schema. Exercises both encode +-- (walks _extensions and routes through encode_field) and decode (routes +-- an unknown tag through extensions_by_id → decode_extension). +do + local p2tests = require('full.protobuf_test_messages.proto2.test_messages_proto2_pb') + local Foo = p2tests.TestAllTypesProto2_descriptor + local msg = { + _extensions = { + ['protobuf_test_messages.proto2.extension_int32'] = 42, + ['protobuf_test_messages.proto2.extension_string'] = 'hi', + }, + } + -- Encode-prime via the descriptor-driven path; tracing the inline + -- emitted code wouldn't cover the extension loop (it's in codec.lua). + local pb = require('pb') + local ext_bytes = pb.encode(Foo, msg) + check('full/extension_encode', + function() pb.encode(Foo, msg) end) + check('full/extension_decode', + function() pb.decode(Foo, ext_bytes) end) +end + -- Pin the known map limitation: pairs() over a hash compiles to bytecode -- ISNEXT, which Tarantool LuaJIT 2.1 can't trace. If this stops triggering, -- upstream lifted the restriction and our scope claim can broaden. diff --git a/cmd/protoc-gen-tarantool/internal/gen/inline.go b/cmd/protoc-gen-tarantool/internal/gen/inline.go index 9b5e06aa8e75a59b855077cf39ef77df4fd49eb0..8854a1c73d3e333ad56ea11d790c4375e5e9913b 100644 --- a/cmd/protoc-gen-tarantool/internal/gen/inline.go +++ b/cmd/protoc-gen-tarantool/internal/gen/inline.go @@ -53,6 +53,31 @@ for _, f := range m.Fields { emitInlineEncodeField(w, f, file, selfPath, imports, prefix) } + // Proto2 extensions: delegate to pb.codec.encode_field once per set + // entry. We don't inline an extension-specific writer here because + // extensions are runtime-mutable — a user can register more after the + // module loads — so the canonical iteration source is the descriptor's + // extensions_list. The walk is JIT-stable (array, not pairs over a + // hash). Skip entirely when extensions_list is nil (most proto3 messages + // and proto2 messages without `extensions` ranges) so the hot path + // pays nothing. + w.line(" local _exts = t._extensions") + w.line(" if _exts ~= nil then") + w.line(" local _elist = M.%s_descriptor.extensions_list", name) + w.line(" if _elist ~= nil then") + w.line(" for _i = 1, #_elist do") + w.line(" local _ext = _elist[_i]") + w.line(" local _ev = _exts[_ext.full_name]") + w.line(" if _ev ~= nil then") + w.line(" pb.codec.encode_field(_ext, _ev, out, true)") + w.line(" end") + w.line(" end") + // encode_field uses out[#out + 1] = … and doesn't know about our + // local `n` cursor — resync so the unknown-fields append below lands + // after the extension bytes, not on top of them. + w.line(" n = #out") + w.line(" end") + w.line(" end") // Preserve unknown fields captured at decode time. w.line(" local _uf = t._unknown_fields") w.line(" if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end") @@ -342,9 +367,20 @@ // No fields — every wire byte is unknown. w.line(" if true then") } w.line(" else") - w.line(" pos = wire.skip_field(buf, pos, wt, id)") - w.line(" if _uf == nil then _uf = {} end") - w.line(" _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1)") + // Proto2 extensions: an unknown tag may belong to a registered + // extension on this descriptor. Route through pb.codec's + // decode_extension which mirrors the in-line dispatch on field + // kind (scalar/enum/message/group, singular/repeated) and stores + // into result._extensions[full_name]. + w.line(" local _ebid = M.%s_descriptor.extensions_by_id", name) + w.line(" local _ext = _ebid and _ebid[id] or nil") + w.line(" if _ext ~= nil then") + w.line(" pos = pb.codec.decode_extension(_ext, buf, pos, wt, result)") + w.line(" else") + w.line(" pos = wire.skip_field(buf, pos, wt, id)") + w.line(" if _uf == nil then _uf = {} end") + w.line(" _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1)") + w.line(" end") w.line(" end") w.line(" end") w.line(" if _uf ~= nil then result._unknown_fields = table.concat(_uf) end") diff --git a/examples/expected/full/conformance/conformance_pb.lua b/examples/expected/full/conformance/conformance_pb.lua index df624215fd601f547f653e0435f3f7b7adbbc865..82619429a10c814b58a8c0b9eb7146af1d3d224a 100644 --- a/examples/expected/full/conformance/conformance_pb.lua +++ b/examples/expected/full/conformance/conformance_pb.lua @@ -225,6 +225,20 @@ n = n + 1; out[n] = "\x1a" n = n + 1; out[n] = wire.encode_varint(#v) n = n + 1; out[n] = v end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestStatus_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -256,9 +270,15 @@ local val val, pos = wire.decode_string(buf, pos) result.matched_name = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestStatus_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -296,6 +316,20 @@ n = n + 1; out[n] = wire.encode_varint(#_b) n = n + 1; out[n] = _b end end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.FailureSet_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -321,9 +355,15 @@ local payload payload, pos = wire.decode_len(buf, pos) list[#list + 1] = M.TestStatus_decode(payload) else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.FailureSet_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -430,6 +470,20 @@ if v ~= nil and v ~= false then n = n + 1; out[n] = "\x48" n = n + 1; out[n] = wire.encode_bool(v) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.ConformanceRequest_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -502,9 +556,15 @@ local val val, pos = wire.decode_bool(buf, pos) result.print_unknown_fields = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.ConformanceRequest_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -604,6 +664,20 @@ n = n + 1; out[n] = "\x42" n = n + 1; out[n] = wire.encode_varint(#v) n = n + 1; out[n] = v end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.ConformanceResponse_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -731,9 +805,15 @@ result.json_payload = nil result.skipped = nil result.jspb_payload = nil else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.ConformanceResponse_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -766,6 +846,20 @@ if v ~= nil and v ~= false then n = n + 1; out[n] = "\x08" n = n + 1; out[n] = wire.encode_bool(v) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.JspbEncodingConfig_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -789,9 +883,15 @@ local val val, pos = wire.decode_bool(buf, pos) result.use_jspb_array_any_format = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.JspbEncodingConfig_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end diff --git a/examples/expected/full/hello/hello_pb.lua b/examples/expected/full/hello/hello_pb.lua index f59d634c0a297d42a60f327baf877f09eb615ec4..88cac7b9e0c63e8c0dbf08eaf83dc172bc12b152 100644 --- a/examples/expected/full/hello/hello_pb.lua +++ b/examples/expected/full/hello/hello_pb.lua @@ -246,6 +246,20 @@ n = n + 1; out[n] = "\x22" n = n + 1; out[n] = wire.encode_varint(#_b) n = n + 1; out[n] = _b end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.Result_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -292,9 +306,15 @@ end result.text = nil result.code = nil else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.Result_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -328,6 +348,20 @@ n = n + 1; out[n] = "\x0a" n = n + 1; out[n] = wire.encode_varint(#v) n = n + 1; out[n] = v end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.HelloRequest_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -351,9 +385,15 @@ local val val, pos = wire.decode_string(buf, pos) result.name = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.HelloRequest_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -387,6 +427,20 @@ n = n + 1; out[n] = "\x0a" n = n + 1; out[n] = wire.encode_varint(#v) n = n + 1; out[n] = v end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.HelloReply_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -410,9 +464,15 @@ local val val, pos = wire.decode_string(buf, pos) result.greeting = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.HelloReply_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -534,6 +594,20 @@ n = n + 1; out[n] = "\x62" n = n + 1; out[n] = wire.encode_varint(#_b) n = n + 1; out[n] = _b end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.Event_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -601,9 +675,15 @@ local payload payload, pos = wire.decode_len(buf, pos) result.update_mask = pb.wkt.FieldMask_decode(payload) else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.Event_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -657,6 +737,20 @@ n = n + 1; out[n] = "\x22" n = n + 1; out[n] = wire.encode_varint(#v) n = n + 1; out[n] = v end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.Address_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -692,9 +786,15 @@ local val val, pos = wire.decode_string(buf, pos) result.apartment = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.Address_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -873,6 +973,20 @@ n = n + 1; out[n] = _tag n = n + 1; out[n] = wire.encode_len(table.concat(entry)) end end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.Person_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -1017,9 +1131,15 @@ end end map[_key] = _val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.Person_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end diff --git a/examples/expected/full/proto2_basic/proto2_basic_pb.lua b/examples/expected/full/proto2_basic/proto2_basic_pb.lua index 185b7ef43c6d4740f4dffef1d5826355491a6b86..5fff79a93056ba600eab3777bd427e728a96a010 100644 --- a/examples/expected/full/proto2_basic/proto2_basic_pb.lua +++ b/examples/expected/full/proto2_basic/proto2_basic_pb.lua @@ -27,6 +27,9 @@ M.Nested_Inner_descriptor = {name = "proto2_basic.Nested.Inner"} M.WithGroup_descriptor = {name = "proto2_basic.WithGroup"} M.WithGroup_SingleGroup_descriptor = {name = "proto2_basic.WithGroup.SingleGroup"} M.WithGroup_RepGroup_descriptor = {name = "proto2_basic.WithGroup.RepGroup"} +M.BenchPayload_descriptor = {name = "proto2_basic.BenchPayload"} +M.BenchPayload_Stats_descriptor = {name = "proto2_basic.BenchPayload.Stats"} +M.BenchPayload_Inner_descriptor = {name = "proto2_basic.BenchPayload.Inner"} -- Message: proto2_basic.Defaults M.Defaults_descriptor.fields = { @@ -121,6 +124,49 @@ M.WithGroup_RepGroup_fields = pb.field_names({ n = "n", }) +-- Message: proto2_basic.BenchPayload +M.BenchPayload_descriptor.fields = { + {name="id", id=1, kind='scalar', proto_type="int32", required=true}, + {name="name", id=2, kind='scalar', proto_type="string", optional=true, default_value="anonymous"}, + {name="retries", id=3, kind='scalar', proto_type="int32", optional=true, default_value=3}, + {name="lucky_numbers", id=4, kind='scalar', proto_type="int32", repeated=true, packed=true, options={packed = true}}, + {name="tags", id=5, kind='scalar', proto_type="string", repeated=true}, + {name="inner", id=6, kind='message', message=M.BenchPayload_Inner_descriptor, optional=true}, + {name="stats", id=7, kind='group', message=M.BenchPayload_Stats_descriptor, optional=true}, +} +pb.finalize_message(M.BenchPayload_descriptor) +M.BenchPayload_fields = pb.field_names({ + id = "id", + name = "name", + retries = "retries", + lucky_numbers = "lucky_numbers", + tags = "tags", + inner = "inner", + stats = "stats", +}) + +-- Message: proto2_basic.BenchPayload.Stats +M.BenchPayload_Stats_descriptor.fields = { + {name="latency_ns", id=8, kind='scalar', proto_type="int32", optional=true}, + {name="attempts", id=9, kind='scalar', proto_type="int32", optional=true}, +} +pb.finalize_message(M.BenchPayload_Stats_descriptor) +M.BenchPayload_Stats_fields = pb.field_names({ + latency_ns = "latency_ns", + attempts = "attempts", +}) + +-- Message: proto2_basic.BenchPayload.Inner +M.BenchPayload_Inner_descriptor.fields = { + {name="key", id=1, kind='scalar', proto_type="string", required=true}, + {name="weight", id=2, kind='scalar', proto_type="int32", optional=true}, +} +pb.finalize_message(M.BenchPayload_Inner_descriptor) +M.BenchPayload_Inner_fields = pb.field_names({ + key = "key", + weight = "weight", +}) + -- EmmyLua / lua-language-server type annotations. -- These are comments — no runtime effect. They give editors -- autocomplete and type-checking for the generated wrappers. @@ -164,6 +210,26 @@ ---@class proto2_basic.WithGroup.RepGroup ---@field n? integer +--- Bench fixture exercising the proto2-only shapes: required scalar, +--- optional repeated, custom default, a `group` field, and a nested +--- message. Used by bench/bench.lua against both modes. +---@class proto2_basic.BenchPayload +---@field id integer +---@field name? string +---@field retries? integer +---@field lucky_numbers integer[] +---@field tags string[] +---@field inner? proto2_basic.BenchPayload.Inner +---@field stats? proto2_basic.BenchPayload.Stats + +---@class proto2_basic.BenchPayload.Stats +---@field latency_ns? integer +---@field attempts? integer + +---@class proto2_basic.BenchPayload.Inner +---@field key string +---@field weight? integer + ---@param t? proto2_basic.Defaults ---@return proto2_basic.Defaults function M.Defaults_new(t) return t or {} end @@ -237,6 +303,20 @@ end n = n + 1; out[n] = "\x48" n = n + 1; out[n] = wire.encode_int32(nv) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.Defaults_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -292,9 +372,15 @@ local u u, pos = wire.decode_varint(buf, pos) result.color = wire.varint_to_int32(u) else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.Defaults_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -409,6 +495,20 @@ n = n + 1; out[n] = _tag n = n + 1; out[n] = wire.encode_int32(v[_i]) end end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.Cardinality_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -487,9 +587,15 @@ val, pos = wire.decode_int32(buf, pos) list[#list + 1] = val end else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.Cardinality_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -538,6 +644,20 @@ n = n + 1; out[n] = "\x12" n = n + 1; out[n] = wire.encode_varint(#_b) n = n + 1; out[n] = _b end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.Nested_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -575,9 +695,15 @@ else pb.codec.merge_message(M.Nested_Inner_descriptor, prev, M.Nested_Inner_decode(payload)) end else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.Nested_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -616,6 +742,20 @@ error("required field missing on encode: proto2_basic.Nested.Inner.x", 0) end n = n + 1; out[n] = "\x08" n = n + 1; out[n] = wire.encode_int32(v) + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.Nested_Inner_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -639,9 +779,15 @@ local val val, pos = wire.decode_int32(buf, pos) result.x = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.Nested_Inner_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -686,6 +832,20 @@ n = n + 1; out[n] = M.WithGroup_RepGroup_encode(v[_i]) n = n + 1; out[n] = _etag end end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.WithGroup_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -720,9 +880,15 @@ local payload payload, pos = pb.codec.decode_group(M.WithGroup_RepGroup_descriptor, buf, pos, 4) list[#list + 1] = payload else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.WithGroup_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -767,6 +933,20 @@ n = n + 1; out[n] = "\x1a" n = n + 1; out[n] = wire.encode_varint(#v) n = n + 1; out[n] = v end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.WithGroup_SingleGroup_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -794,9 +974,15 @@ local val val, pos = wire.decode_string(buf, pos) result.s = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.WithGroup_SingleGroup_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -839,6 +1025,20 @@ if v ~= nil then n = n + 1; out[n] = "\x28" n = n + 1; out[n] = wire.encode_int32(v) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.WithGroup_RepGroup_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -862,9 +1062,15 @@ local val val, pos = wire.decode_int32(buf, pos) result.n = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.WithGroup_RepGroup_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -883,5 +1089,402 @@ ---@return boolean function M.WithGroup_RepGroup_has_n(t) return t.n ~= nil end ---@param t proto2_basic.WithGroup.RepGroup function M.WithGroup_RepGroup_clear_n(t) t.n = nil end + +---@param t? proto2_basic.BenchPayload +---@return proto2_basic.BenchPayload +function M.BenchPayload_new(t) return t or {} end + +---@param t proto2_basic.BenchPayload +---@return string +function M.BenchPayload_encode(t) + if type(t) ~= 'table' then + error("expected table for proto2_basic.BenchPayload, got " .. type(t), 0) + end + local out, n = {}, 0 + local v + -- field 1: id + v = t.id + if v == nil then + error("required field missing on encode: proto2_basic.BenchPayload.id", 0) + end + n = n + 1; out[n] = "\x08" + n = n + 1; out[n] = wire.encode_int32(v) + -- field 2: name + v = t.name + if v ~= nil then + n = n + 1; out[n] = "\x12" + n = n + 1; out[n] = wire.encode_varint(#v) + n = n + 1; out[n] = v + end + -- field 3: retries + v = t.retries + if v ~= nil then + n = n + 1; out[n] = "\x18" + n = n + 1; out[n] = wire.encode_int32(v) + end + -- field 4: lucky_numbers + v = t.lucky_numbers + if v ~= nil and #v > 0 then + local parts, m = {}, 0 + for _i = 1, #v do + m = m + 1; parts[m] = wire.encode_int32(v[_i]) + end + local _b = table.concat(parts) + n = n + 1; out[n] = "\x22" + n = n + 1; out[n] = wire.encode_varint(#_b) + n = n + 1; out[n] = _b + end + -- field 5: tags + v = t.tags + if v ~= nil and #v > 0 then + local _tag = "\x2a" + for _i = 1, #v do + local _b = v[_i] + n = n + 1; out[n] = _tag + n = n + 1; out[n] = wire.encode_varint(#_b) + n = n + 1; out[n] = _b + end + end + -- field 6: inner + v = t.inner + if v ~= nil or type(v) == 'cdata' then + local _b = M.BenchPayload_Inner_encode(v) + n = n + 1; out[n] = "\x32" + n = n + 1; out[n] = wire.encode_varint(#_b) + n = n + 1; out[n] = _b + end + -- field 7: stats + v = t.stats + if v ~= nil or type(v) == 'cdata' then + n = n + 1; out[n] = "\x3b" + n = n + 1; out[n] = M.BenchPayload_Stats_encode(v) + n = n + 1; out[n] = "\x3c" + end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.BenchPayload_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end + local _uf = t._unknown_fields + if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end + return table.concat(out) +end + +---@param b string +---@return proto2_basic.BenchPayload +function M.BenchPayload_decode(buf) + if type(buf) ~= 'string' then + error("expected string for proto2_basic.BenchPayload decode, got " .. type(buf), 0) + end + local result = {} + local pos, len = 1, #buf + local _uf + while pos <= len do + local _tag_start = pos + local id, wt + id, wt, pos = wire.decode_tag(buf, pos) + if id == 1 then + local val + val, pos = wire.decode_int32(buf, pos) + result.id = val + elseif id == 2 then + local val + val, pos = wire.decode_string(buf, pos) + result.name = val + elseif id == 3 then + local val + val, pos = wire.decode_int32(buf, pos) + result.retries = val + elseif id == 4 then + local list = result.lucky_numbers + if list == nil then list = {}; result.lucky_numbers = list end + if wt == 2 then + local payload + payload, pos = wire.decode_len(buf, pos) + local p2, lim = 1, #payload + while p2 <= lim do + local val + val, p2 = wire.decode_int32(payload, p2) + list[#list + 1] = val + end + else + local val + val, pos = wire.decode_int32(buf, pos) + list[#list + 1] = val + end + elseif id == 5 then + local list = result.tags + if list == nil then list = {}; result.tags = list end + local val + val, pos = wire.decode_string(buf, pos) + list[#list + 1] = val + elseif id == 6 then + local payload + payload, pos = wire.decode_len(buf, pos) + local prev = result.inner + if prev == nil then + result.inner = M.BenchPayload_Inner_decode(payload) + else + pb.codec.merge_message(M.BenchPayload_Inner_descriptor, prev, M.BenchPayload_Inner_decode(payload)) + end + elseif id == 7 then + local payload + payload, pos = pb.codec.decode_group(M.BenchPayload_Stats_descriptor, buf, pos, 7) + local prev = result.stats + if prev == nil then + result.stats = payload + else + pb.codec.merge_message(M.BenchPayload_Stats_descriptor, prev, payload) + end + else + local _ebid = M.BenchPayload_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end + end + end + if _uf ~= nil then result._unknown_fields = table.concat(_uf) end + return result +end + +---@param b string +---@return pb.MessageView +function M.BenchPayload_decode_lazy(b) return pb.decode_lazy(M.BenchPayload_descriptor, b) end +---@param t proto2_basic.BenchPayload +---@param opts? {single_line: boolean?, indent: string?} +---@return string +function M.BenchPayload_text(t, opts) return pb.text.encode(M.BenchPayload_descriptor, t, opts) end +---@param t proto2_basic.BenchPayload +---@return boolean +function M.BenchPayload_has_name(t) return t.name ~= nil end +---@param t proto2_basic.BenchPayload +function M.BenchPayload_clear_name(t) t.name = nil end +---@param t proto2_basic.BenchPayload +---@return boolean +function M.BenchPayload_has_retries(t) return t.retries ~= nil end +---@param t proto2_basic.BenchPayload +function M.BenchPayload_clear_retries(t) t.retries = nil end +---@param t proto2_basic.BenchPayload +---@return boolean +function M.BenchPayload_has_inner(t) return t.inner ~= nil end +---@param t proto2_basic.BenchPayload +function M.BenchPayload_clear_inner(t) t.inner = nil end +---@param t proto2_basic.BenchPayload +---@return boolean +function M.BenchPayload_has_stats(t) return t.stats ~= nil end +---@param t proto2_basic.BenchPayload +function M.BenchPayload_clear_stats(t) t.stats = nil end + +---@param t? proto2_basic.BenchPayload.Stats +---@return proto2_basic.BenchPayload.Stats +function M.BenchPayload_Stats_new(t) return t or {} end + +---@param t proto2_basic.BenchPayload.Stats +---@return string +function M.BenchPayload_Stats_encode(t) + if type(t) ~= 'table' then + error("expected table for proto2_basic.BenchPayload.Stats, got " .. type(t), 0) + end + local out, n = {}, 0 + local v + -- field 8: latency_ns + v = t.latency_ns + if v ~= nil then + n = n + 1; out[n] = "\x40" + n = n + 1; out[n] = wire.encode_int32(v) + end + -- field 9: attempts + v = t.attempts + if v ~= nil then + n = n + 1; out[n] = "\x48" + n = n + 1; out[n] = wire.encode_int32(v) + end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.BenchPayload_Stats_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end + local _uf = t._unknown_fields + if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end + return table.concat(out) +end + +---@param b string +---@return proto2_basic.BenchPayload.Stats +function M.BenchPayload_Stats_decode(buf) + if type(buf) ~= 'string' then + error("expected string for proto2_basic.BenchPayload.Stats decode, got " .. type(buf), 0) + end + local result = {} + local pos, len = 1, #buf + local _uf + while pos <= len do + local _tag_start = pos + local id, wt + id, wt, pos = wire.decode_tag(buf, pos) + if id == 8 then + local val + val, pos = wire.decode_int32(buf, pos) + result.latency_ns = val + elseif id == 9 then + local val + val, pos = wire.decode_int32(buf, pos) + result.attempts = val + else + local _ebid = M.BenchPayload_Stats_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end + end + end + if _uf ~= nil then result._unknown_fields = table.concat(_uf) end + return result +end + +---@param b string +---@return pb.MessageView +function M.BenchPayload_Stats_decode_lazy(b) return pb.decode_lazy(M.BenchPayload_Stats_descriptor, b) end +---@param t proto2_basic.BenchPayload.Stats +---@param opts? {single_line: boolean?, indent: string?} +---@return string +function M.BenchPayload_Stats_text(t, opts) return pb.text.encode(M.BenchPayload_Stats_descriptor, t, opts) end +---@param t proto2_basic.BenchPayload.Stats +---@return boolean +function M.BenchPayload_Stats_has_latency_ns(t) return t.latency_ns ~= nil end +---@param t proto2_basic.BenchPayload.Stats +function M.BenchPayload_Stats_clear_latency_ns(t) t.latency_ns = nil end +---@param t proto2_basic.BenchPayload.Stats +---@return boolean +function M.BenchPayload_Stats_has_attempts(t) return t.attempts ~= nil end +---@param t proto2_basic.BenchPayload.Stats +function M.BenchPayload_Stats_clear_attempts(t) t.attempts = nil end + +---@param t? proto2_basic.BenchPayload.Inner +---@return proto2_basic.BenchPayload.Inner +function M.BenchPayload_Inner_new(t) return t or {} end + +---@param t proto2_basic.BenchPayload.Inner +---@return string +function M.BenchPayload_Inner_encode(t) + if type(t) ~= 'table' then + error("expected table for proto2_basic.BenchPayload.Inner, got " .. type(t), 0) + end + local out, n = {}, 0 + local v + -- field 1: key + v = t.key + if v == nil then + error("required field missing on encode: proto2_basic.BenchPayload.Inner.key", 0) + end + n = n + 1; out[n] = "\x0a" + n = n + 1; out[n] = wire.encode_varint(#v) + n = n + 1; out[n] = v + -- field 2: weight + v = t.weight + if v ~= nil then + n = n + 1; out[n] = "\x10" + n = n + 1; out[n] = wire.encode_int32(v) + end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.BenchPayload_Inner_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end + local _uf = t._unknown_fields + if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end + return table.concat(out) +end + +---@param b string +---@return proto2_basic.BenchPayload.Inner +function M.BenchPayload_Inner_decode(buf) + if type(buf) ~= 'string' then + error("expected string for proto2_basic.BenchPayload.Inner decode, got " .. type(buf), 0) + end + local result = {} + local pos, len = 1, #buf + local _uf + while pos <= len do + local _tag_start = pos + local id, wt + id, wt, pos = wire.decode_tag(buf, pos) + if id == 1 then + local val + val, pos = wire.decode_string(buf, pos) + result.key = val + elseif id == 2 then + local val + val, pos = wire.decode_int32(buf, pos) + result.weight = val + else + local _ebid = M.BenchPayload_Inner_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end + end + end + if _uf ~= nil then result._unknown_fields = table.concat(_uf) end + return result +end + +---@param b string +---@return pb.MessageView +function M.BenchPayload_Inner_decode_lazy(b) return pb.decode_lazy(M.BenchPayload_Inner_descriptor, b) end +---@param t proto2_basic.BenchPayload.Inner +---@param opts? {single_line: boolean?, indent: string?} +---@return string +function M.BenchPayload_Inner_text(t, opts) return pb.text.encode(M.BenchPayload_Inner_descriptor, t, opts) end +---@param t proto2_basic.BenchPayload.Inner +---@return boolean +function M.BenchPayload_Inner_has_weight(t) return t.weight ~= nil end +---@param t proto2_basic.BenchPayload.Inner +function M.BenchPayload_Inner_clear_weight(t) t.weight = nil end + +-- Extension: proto2_basic.ext_count extends proto2_basic.BenchPayload (tag 100) +pb.register_extension(M.BenchPayload_descriptor, {name="ext_count", full_name="proto2_basic.ext_count", id=100, kind='scalar', proto_type="int32", optional=true}) +-- Extension: proto2_basic.ext_label extends proto2_basic.BenchPayload (tag 101) +pb.register_extension(M.BenchPayload_descriptor, {name="ext_label", full_name="proto2_basic.ext_label", id=101, kind='scalar', proto_type="string", optional=true}) return M diff --git a/examples/expected/full/protobuf_test_messages/proto2/test_messages_proto2_pb.lua b/examples/expected/full/protobuf_test_messages/proto2/test_messages_proto2_pb.lua index ce7617bd1d56ce71e063c7f63d8cfeba249a87d8..f84ac372610e9b6078a4879370370feda640b25b 100644 --- a/examples/expected/full/protobuf_test_messages/proto2/test_messages_proto2_pb.lua +++ b/examples/expected/full/protobuf_test_messages/proto2/test_messages_proto2_pb.lua @@ -2259,6 +2259,20 @@ if v ~= nil then n = n + 1; out[n] = "\x90\x1a" n = n + 1; out[n] = wire.encode_int32(v) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestAllTypesProto2_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -3835,9 +3849,15 @@ local val val, pos = wire.decode_int32(buf, pos) result.Field_name18__ = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestAllTypesProto2_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4163,6 +4183,20 @@ n = n + 1; out[n] = "\x12" n = n + 1; out[n] = wire.encode_varint(#_b) n = n + 1; out[n] = _b end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestAllTypesProto2_NestedMessage_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4195,9 +4229,15 @@ else pb.codec.merge_message(M.TestAllTypesProto2_descriptor, prev, M.TestAllTypesProto2_decode(payload)) end else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestAllTypesProto2_NestedMessage_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4246,6 +4286,20 @@ if v ~= nil then n = n + 1; out[n] = "\xd8\x0c" n = n + 1; out[n] = wire.encode_uint32(v) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestAllTypesProto2_Data_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4273,9 +4327,15 @@ local val val, pos = wire.decode_uint32(buf, pos) result.group_uint32 = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestAllTypesProto2_Data_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4324,6 +4384,20 @@ if v ~= nil then n = n + 1; out[n] = "\xf0\x0c" n = n + 1; out[n] = wire.encode_uint32(v) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestAllTypesProto2_MultiWordGroupField_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4351,9 +4425,15 @@ local val val, pos = wire.decode_uint32(buf, pos) result.group_uint32 = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestAllTypesProto2_MultiWordGroupField_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4396,6 +4476,20 @@ if v ~= nil then n = n + 1; out[n] = "\x08" n = n + 1; out[n] = wire.encode_int32(v) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.ForeignMessageProto2_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4419,9 +4513,15 @@ local val val, pos = wire.decode_int32(buf, pos) result.c = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.ForeignMessageProto2_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4464,6 +4564,20 @@ v = t.group_uint32 if v ~= nil then n = n + 1; out[n] = "\xd8\x07" n = n + 1; out[n] = wire.encode_uint32(v) + end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.GroupField_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end @@ -4492,9 +4606,15 @@ local val val, pos = wire.decode_uint32(buf, pos) result.group_uint32 = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.GroupField_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4574,6 +4694,20 @@ n = n + 1; out[n] = _tag n = n + 1; out[n] = wire.encode_int32(v[_i]) end end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.UnknownToTestAllTypes_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4640,9 +4774,15 @@ val, pos = wire.decode_int32(buf, pos) list[#list + 1] = val end else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.UnknownToTestAllTypes_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4700,6 +4840,20 @@ if v ~= nil then n = n + 1; out[n] = "\x08" n = n + 1; out[n] = wire.encode_int32(v) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.UnknownToTestAllTypes_OptionalGroup_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4723,9 +4877,15 @@ local val val, pos = wire.decode_int32(buf, pos) result.a = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.UnknownToTestAllTypes_OptionalGroup_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4757,6 +4917,20 @@ error("expected table for protobuf_test_messages.proto2.NullHypothesisProto2, got " .. type(t), 0) end local out, n = {}, 0 local v + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.NullHypothesisProto2_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4777,9 +4951,15 @@ local id, wt id, wt, pos = wire.decode_tag(buf, pos) if true then else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.NullHypothesisProto2_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4806,6 +4986,20 @@ error("expected table for protobuf_test_messages.proto2.EnumOnlyProto2, got " .. type(t), 0) end local out, n = {}, 0 local v + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.EnumOnlyProto2_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4826,9 +5020,15 @@ local id, wt id, wt, pos = wire.decode_tag(buf, pos) if true then else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.EnumOnlyProto2_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4862,6 +5062,20 @@ n = n + 1; out[n] = "\x0a" n = n + 1; out[n] = wire.encode_varint(#v) n = n + 1; out[n] = v end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.OneStringProto2_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4885,9 +5099,15 @@ local val val, pos = wire.decode_string(buf, pos) result.data = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.OneStringProto2_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4943,6 +5163,20 @@ n = n + 1; out[n] = wire.encode_varint(#_b) n = n + 1; out[n] = _b end end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.ProtoWithKeywords_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4976,9 +5210,15 @@ local val val, pos = wire.decode_string(buf, pos) list[#list + 1] = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.ProtoWithKeywords_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -5317,6 +5557,20 @@ end n = n + 1; out[n] = "\xfa\x0f" n = n + 1; out[n] = wire.encode_varint(#v) n = n + 1; out[n] = v + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestAllRequiredTypesProto2_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -5517,9 +5771,15 @@ local val val, pos = wire.decode_bytes(buf, pos) result.default_bytes = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestAllRequiredTypesProto2_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -5575,6 +5835,20 @@ n = n + 1; out[n] = "\x1a" n = n + 1; out[n] = wire.encode_varint(#_b) n = n + 1; out[n] = _b end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestAllRequiredTypesProto2_NestedMessage_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -5616,9 +5890,15 @@ else pb.codec.merge_message(M.TestAllRequiredTypesProto2_descriptor, prev, M.TestAllRequiredTypesProto2_decode(payload)) end else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestAllRequiredTypesProto2_NestedMessage_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -5664,6 +5944,20 @@ error("required field missing on encode: protobuf_test_messages.proto2.TestAllRequiredTypesProto2.Data.group_uint32", 0) end n = n + 1; out[n] = "\xd8\x0c" n = n + 1; out[n] = wire.encode_uint32(v) + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestAllRequiredTypesProto2_Data_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -5691,9 +5985,15 @@ local val val, pos = wire.decode_uint32(buf, pos) result.group_uint32 = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestAllRequiredTypesProto2_Data_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -5766,6 +6066,20 @@ n = n + 1; out[n] = "\x2a" n = n + 1; out[n] = wire.encode_varint(#_b) n = n + 1; out[n] = _b end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestLargeOneof_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -5850,9 +6164,15 @@ result.a2 = nil result.a3 = nil result.a4 = nil else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestLargeOneof_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -5879,6 +6199,20 @@ error("expected table for protobuf_test_messages.proto2.TestLargeOneof.A1, got " .. type(t), 0) end local out, n = {}, 0 local v + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestLargeOneof_A1_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -5899,9 +6233,15 @@ local id, wt id, wt, pos = wire.decode_tag(buf, pos) if true then else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestLargeOneof_A1_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -5928,6 +6268,20 @@ error("expected table for protobuf_test_messages.proto2.TestLargeOneof.A2, got " .. type(t), 0) end local out, n = {}, 0 local v + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestLargeOneof_A2_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -5948,9 +6302,15 @@ local id, wt id, wt, pos = wire.decode_tag(buf, pos) if true then else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestLargeOneof_A2_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -5977,6 +6337,20 @@ error("expected table for protobuf_test_messages.proto2.TestLargeOneof.A3, got " .. type(t), 0) end local out, n = {}, 0 local v + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestLargeOneof_A3_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -5997,9 +6371,15 @@ local id, wt id, wt, pos = wire.decode_tag(buf, pos) if true then else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestLargeOneof_A3_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -6026,6 +6406,20 @@ error("expected table for protobuf_test_messages.proto2.TestLargeOneof.A4, got " .. type(t), 0) end local out, n = {}, 0 local v + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestLargeOneof_A4_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -6046,9 +6440,15 @@ local id, wt id, wt, pos = wire.decode_tag(buf, pos) if true then else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestLargeOneof_A4_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -6075,6 +6475,20 @@ error("expected table for protobuf_test_messages.proto2.TestLargeOneof.A5, got " .. type(t), 0) end local out, n = {}, 0 local v + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestLargeOneof_A5_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -6095,9 +6509,15 @@ local id, wt id, wt, pos = wire.decode_tag(buf, pos) if true then else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestLargeOneof_A5_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end diff --git a/examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua b/examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua index 68f67851f7c6a8e03938f3590e6dd5b96d20e3e8..8f3ad036c5cb3ba7454883e6269efc637a4a032b 100644 --- a/examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua +++ b/examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua @@ -2207,6 +2207,20 @@ if v ~= nil and v ~= 0 then n = n + 1; out[n] = "\x90\x1a" n = n + 1; out[n] = wire.encode_int32(v) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestAllTypesProto3_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -3861,9 +3875,15 @@ local val val, pos = wire.decode_int32(buf, pos) result.Field_name18__ = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestAllTypesProto3_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -3904,6 +3924,20 @@ n = n + 1; out[n] = "\x12" n = n + 1; out[n] = wire.encode_varint(#_b) n = n + 1; out[n] = _b end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.TestAllTypesProto3_NestedMessage_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -3936,9 +3970,15 @@ else pb.codec.merge_message(M.TestAllTypesProto3_descriptor, prev, M.TestAllTypesProto3_decode(payload)) end else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.TestAllTypesProto3_NestedMessage_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -3971,6 +4011,20 @@ if v ~= nil and v ~= 0 then n = n + 1; out[n] = "\x08" n = n + 1; out[n] = wire.encode_int32(v) end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.ForeignMessage_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -3994,9 +4048,15 @@ local val val, pos = wire.decode_int32(buf, pos) result.c = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.ForeignMessage_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4023,6 +4083,20 @@ error("expected table for protobuf_test_messages.proto3.NullHypothesisProto3, got " .. type(t), 0) end local out, n = {}, 0 local v + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.NullHypothesisProto3_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4043,9 +4117,15 @@ local id, wt id, wt, pos = wire.decode_tag(buf, pos) if true then else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.NullHypothesisProto3_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end @@ -4072,6 +4152,20 @@ error("expected table for protobuf_test_messages.proto3.EnumOnlyProto3, got " .. type(t), 0) end local out, n = {}, 0 local v + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.EnumOnlyProto3_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -4092,9 +4186,15 @@ local id, wt id, wt, pos = wire.decode_tag(buf, pos) if true then else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.EnumOnlyProto3_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end diff --git a/examples/expected/full/quickstart/quickstart_pb.lua b/examples/expected/full/quickstart/quickstart_pb.lua index 742d565bf46a3b940ba02fca2dfa4810df92f0de..8df8c9a674b22328cc9efa8205e5b342e1cff152 100644 --- a/examples/expected/full/quickstart/quickstart_pb.lua +++ b/examples/expected/full/quickstart/quickstart_pb.lua @@ -96,6 +96,20 @@ n = n + 1; out[n] = wire.encode_varint(#_b) n = n + 1; out[n] = _b end end + local _exts = t._extensions + if _exts ~= nil then + local _elist = M.User_descriptor.extensions_list + if _elist ~= nil then + for _i = 1, #_elist do + local _ext = _elist[_i] + local _ev = _exts[_ext.full_name] + if _ev ~= nil then + pb.codec.encode_field(_ext, _ev, out, true) + end + end + n = #out + end + end local _uf = t._unknown_fields if _uf ~= nil and _uf ~= '' then n = n + 1; out[n] = _uf end return table.concat(out) @@ -133,9 +147,15 @@ local val val, pos = wire.decode_string(buf, pos) list[#list + 1] = val else - pos = wire.skip_field(buf, pos, wt, id) - if _uf == nil then _uf = {} end - _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + local _ebid = M.User_descriptor.extensions_by_id + local _ext = _ebid and _ebid[id] or nil + if _ext ~= nil then + pos = pb.codec.decode_extension(_ext, buf, pos, wt, result) + else + pos = wire.skip_field(buf, pos, wt, id) + if _uf == nil then _uf = {} end + _uf[#_uf + 1] = buf:sub(_tag_start, pos - 1) + end end end if _uf ~= nil then result._unknown_fields = table.concat(_uf) end diff --git a/examples/expected/runtime/proto2_basic/proto2_basic_pb.lua b/examples/expected/runtime/proto2_basic/proto2_basic_pb.lua index d8b7fc0565daaa148fc119b8dcaff622cae5f02b..46b43130c9cd99a63a03a7af17e04bfd2f46de08 100644 --- a/examples/expected/runtime/proto2_basic/proto2_basic_pb.lua +++ b/examples/expected/runtime/proto2_basic/proto2_basic_pb.lua @@ -27,6 +27,9 @@ M.Nested_Inner_descriptor = {name = "proto2_basic.Nested.Inner"} M.WithGroup_descriptor = {name = "proto2_basic.WithGroup"} M.WithGroup_SingleGroup_descriptor = {name = "proto2_basic.WithGroup.SingleGroup"} M.WithGroup_RepGroup_descriptor = {name = "proto2_basic.WithGroup.RepGroup"} +M.BenchPayload_descriptor = {name = "proto2_basic.BenchPayload"} +M.BenchPayload_Stats_descriptor = {name = "proto2_basic.BenchPayload.Stats"} +M.BenchPayload_Inner_descriptor = {name = "proto2_basic.BenchPayload.Inner"} -- Message: proto2_basic.Defaults M.Defaults_descriptor.fields = { @@ -121,6 +124,49 @@ M.WithGroup_RepGroup_fields = pb.field_names({ n = "n", }) +-- Message: proto2_basic.BenchPayload +M.BenchPayload_descriptor.fields = { + {name="id", id=1, kind='scalar', proto_type="int32", required=true}, + {name="name", id=2, kind='scalar', proto_type="string", optional=true, default_value="anonymous"}, + {name="retries", id=3, kind='scalar', proto_type="int32", optional=true, default_value=3}, + {name="lucky_numbers", id=4, kind='scalar', proto_type="int32", repeated=true, packed=true, options={packed = true}}, + {name="tags", id=5, kind='scalar', proto_type="string", repeated=true}, + {name="inner", id=6, kind='message', message=M.BenchPayload_Inner_descriptor, optional=true}, + {name="stats", id=7, kind='group', message=M.BenchPayload_Stats_descriptor, optional=true}, +} +pb.finalize_message(M.BenchPayload_descriptor) +M.BenchPayload_fields = pb.field_names({ + id = "id", + name = "name", + retries = "retries", + lucky_numbers = "lucky_numbers", + tags = "tags", + inner = "inner", + stats = "stats", +}) + +-- Message: proto2_basic.BenchPayload.Stats +M.BenchPayload_Stats_descriptor.fields = { + {name="latency_ns", id=8, kind='scalar', proto_type="int32", optional=true}, + {name="attempts", id=9, kind='scalar', proto_type="int32", optional=true}, +} +pb.finalize_message(M.BenchPayload_Stats_descriptor) +M.BenchPayload_Stats_fields = pb.field_names({ + latency_ns = "latency_ns", + attempts = "attempts", +}) + +-- Message: proto2_basic.BenchPayload.Inner +M.BenchPayload_Inner_descriptor.fields = { + {name="key", id=1, kind='scalar', proto_type="string", required=true}, + {name="weight", id=2, kind='scalar', proto_type="int32", optional=true}, +} +pb.finalize_message(M.BenchPayload_Inner_descriptor) +M.BenchPayload_Inner_fields = pb.field_names({ + key = "key", + weight = "weight", +}) + -- EmmyLua / lua-language-server type annotations. -- These are comments — no runtime effect. They give editors -- autocomplete and type-checking for the generated wrappers. @@ -163,6 +209,26 @@ ---@field s? string ---@class proto2_basic.WithGroup.RepGroup ---@field n? integer + +--- Bench fixture exercising the proto2-only shapes: required scalar, +--- optional repeated, custom default, a `group` field, and a nested +--- message. Used by bench/bench.lua against both modes. +---@class proto2_basic.BenchPayload +---@field id integer +---@field name? string +---@field retries? integer +---@field lucky_numbers integer[] +---@field tags string[] +---@field inner? proto2_basic.BenchPayload.Inner +---@field stats? proto2_basic.BenchPayload.Stats + +---@class proto2_basic.BenchPayload.Stats +---@field latency_ns? integer +---@field attempts? integer + +---@class proto2_basic.BenchPayload.Inner +---@field key string +---@field weight? integer ---@param t? proto2_basic.Defaults ---@return proto2_basic.Defaults @@ -357,5 +423,96 @@ ---@return boolean function M.WithGroup_RepGroup_has_n(t) return t.n ~= nil end ---@param t proto2_basic.WithGroup.RepGroup function M.WithGroup_RepGroup_clear_n(t) t.n = nil end + +---@param t? proto2_basic.BenchPayload +---@return proto2_basic.BenchPayload +function M.BenchPayload_new(t) return t or {} end +---@param t proto2_basic.BenchPayload +---@return string +function M.BenchPayload_encode(t) return pb.encode(M.BenchPayload_descriptor, t) end +---@param b string +---@return proto2_basic.BenchPayload +function M.BenchPayload_decode(b) return pb.decode(M.BenchPayload_descriptor, b) end +---@param b string +---@return pb.MessageView +function M.BenchPayload_decode_lazy(b) return pb.decode_lazy(M.BenchPayload_descriptor, b) end +---@param t proto2_basic.BenchPayload +---@param opts? {single_line: boolean?, indent: string?} +---@return string +function M.BenchPayload_text(t, opts) return pb.text.encode(M.BenchPayload_descriptor, t, opts) end +---@param t proto2_basic.BenchPayload +---@return boolean +function M.BenchPayload_has_name(t) return t.name ~= nil end +---@param t proto2_basic.BenchPayload +function M.BenchPayload_clear_name(t) t.name = nil end +---@param t proto2_basic.BenchPayload +---@return boolean +function M.BenchPayload_has_retries(t) return t.retries ~= nil end +---@param t proto2_basic.BenchPayload +function M.BenchPayload_clear_retries(t) t.retries = nil end +---@param t proto2_basic.BenchPayload +---@return boolean +function M.BenchPayload_has_inner(t) return t.inner ~= nil end +---@param t proto2_basic.BenchPayload +function M.BenchPayload_clear_inner(t) t.inner = nil end +---@param t proto2_basic.BenchPayload +---@return boolean +function M.BenchPayload_has_stats(t) return t.stats ~= nil end +---@param t proto2_basic.BenchPayload +function M.BenchPayload_clear_stats(t) t.stats = nil end + +---@param t? proto2_basic.BenchPayload.Stats +---@return proto2_basic.BenchPayload.Stats +function M.BenchPayload_Stats_new(t) return t or {} end +---@param t proto2_basic.BenchPayload.Stats +---@return string +function M.BenchPayload_Stats_encode(t) return pb.encode(M.BenchPayload_Stats_descriptor, t) end +---@param b string +---@return proto2_basic.BenchPayload.Stats +function M.BenchPayload_Stats_decode(b) return pb.decode(M.BenchPayload_Stats_descriptor, b) end +---@param b string +---@return pb.MessageView +function M.BenchPayload_Stats_decode_lazy(b) return pb.decode_lazy(M.BenchPayload_Stats_descriptor, b) end +---@param t proto2_basic.BenchPayload.Stats +---@param opts? {single_line: boolean?, indent: string?} +---@return string +function M.BenchPayload_Stats_text(t, opts) return pb.text.encode(M.BenchPayload_Stats_descriptor, t, opts) end +---@param t proto2_basic.BenchPayload.Stats +---@return boolean +function M.BenchPayload_Stats_has_latency_ns(t) return t.latency_ns ~= nil end +---@param t proto2_basic.BenchPayload.Stats +function M.BenchPayload_Stats_clear_latency_ns(t) t.latency_ns = nil end +---@param t proto2_basic.BenchPayload.Stats +---@return boolean +function M.BenchPayload_Stats_has_attempts(t) return t.attempts ~= nil end +---@param t proto2_basic.BenchPayload.Stats +function M.BenchPayload_Stats_clear_attempts(t) t.attempts = nil end + +---@param t? proto2_basic.BenchPayload.Inner +---@return proto2_basic.BenchPayload.Inner +function M.BenchPayload_Inner_new(t) return t or {} end +---@param t proto2_basic.BenchPayload.Inner +---@return string +function M.BenchPayload_Inner_encode(t) return pb.encode(M.BenchPayload_Inner_descriptor, t) end +---@param b string +---@return proto2_basic.BenchPayload.Inner +function M.BenchPayload_Inner_decode(b) return pb.decode(M.BenchPayload_Inner_descriptor, b) end +---@param b string +---@return pb.MessageView +function M.BenchPayload_Inner_decode_lazy(b) return pb.decode_lazy(M.BenchPayload_Inner_descriptor, b) end +---@param t proto2_basic.BenchPayload.Inner +---@param opts? {single_line: boolean?, indent: string?} +---@return string +function M.BenchPayload_Inner_text(t, opts) return pb.text.encode(M.BenchPayload_Inner_descriptor, t, opts) end +---@param t proto2_basic.BenchPayload.Inner +---@return boolean +function M.BenchPayload_Inner_has_weight(t) return t.weight ~= nil end +---@param t proto2_basic.BenchPayload.Inner +function M.BenchPayload_Inner_clear_weight(t) t.weight = nil end + +-- Extension: proto2_basic.ext_count extends proto2_basic.BenchPayload (tag 100) +pb.register_extension(M.BenchPayload_descriptor, {name="ext_count", full_name="proto2_basic.ext_count", id=100, kind='scalar', proto_type="int32", optional=true}) +-- Extension: proto2_basic.ext_label extends proto2_basic.BenchPayload (tag 101) +pb.register_extension(M.BenchPayload_descriptor, {name="ext_label", full_name="proto2_basic.ext_label", id=101, kind='scalar', proto_type="string", optional=true}) return M diff --git a/runtime/pb/codec.lua b/runtime/pb/codec.lua index b46fb400b0f4670053f459f3517d3f12007ea26f..6724e6c2564b72ca010310049813492720b6f805 100644 --- a/runtime/pb/codec.lua +++ b/runtime/pb/codec.lua @@ -909,15 +909,21 @@ encode_field(f, data[f.name], out, f.optional) end end -- Proto2 extensions: data._extensions = { [full_name] = value, ... }. - -- Walk known extensions in declaration order via extensions_by_full_name - -- so the wire bytes are stable across runs (pairs() ordering otherwise - -- depends on hash). Unknown extensions stay in _unknown_fields. + -- Walk via the parallel array `extensions_list` rather than `pairs()` + -- over a hash — pairs() compiles to bytecode ISNEXT, which is NYI in + -- Tarantool's LuaJIT 2.1 fork (same limitation that gates map encode). + -- pb.register_extension keeps the array in registration order, which + -- is also the deterministic on-wire order. local exts = data._extensions - if exts ~= nil and desc.extensions_by_full_name ~= nil then - for full_name, ext in pairs(desc.extensions_by_full_name) do - local v = exts[full_name] - if v ~= nil then - encode_field(ext, v, out, true) + if exts ~= nil then + local elist = desc.extensions_list + if elist ~= nil then + for i = 1, #elist do + local ext = elist[i] + local v = exts[ext.full_name] + if v ~= nil then + encode_field(ext, v, out, true) + end end end end @@ -1037,7 +1043,13 @@ -- decode_extension routes wire bytes for a registered proto2 extension into -- result._extensions[ext.full_name]. Mirrors the in-line decode dispatch on -- field kind (scalar/enum/message/group, singular/repeated). Returns the -- new buffer position after the value bytes. -local function decode_extension(ext, buf, pos, wt, result) +-- Exposed for inline-mode generated code: the per-message decoder calls +-- decode_extension when it sees an unknown tag that matches a registered +-- extension on the descriptor. +local decode_extension +function M.decode_extension(...) return decode_extension(...) end + +decode_extension = function(ext, buf, pos, wt, result) local exts = result._extensions if exts == nil then exts = {}; result._extensions = exts end local key = ext.full_name diff --git a/runtime/pb/init.lua b/runtime/pb/init.lua index 71c1e068b6b744dadcdc17419d8b821899e90926..14bb3b722751eab8919fac95455fc0de49c2b4f5 100644 --- a/runtime/pb/init.lua +++ b/runtime/pb/init.lua @@ -190,14 +190,23 @@ -- Proto2 extension registration. Generated code emits one call per -- `extend Foo { ... }` field, attaching the extension's field shape -- to the extendee's descriptor. The codec consults `extensions_by_id` - -- when decoding an unrecognized tag and walks `extensions_by_full_name` - -- when encoding `data._extensions`. + -- when decoding an unrecognized tag, walks `extensions_list` (array) + -- when encoding `data._extensions`, and uses + -- `extensions_by_full_name` for text/JSON encoder lookups. + -- + -- `extensions_list` is the JIT-stable iteration source — pairs() over + -- a hash compiles to bytecode ISNEXT which is NYI in LuaJIT 2.1, so + -- the hot encode loop iterates the array instead. The hash tables + -- stay around for O(1) name/id lookups. register_extension = function(extendee_desc, ext) if extendee_desc.extensions_by_id == nil then extendee_desc.extensions_by_id = {} extendee_desc.extensions_by_full_name = {} + extendee_desc.extensions_list = {} end extendee_desc.extensions_by_id[ext.id] = ext extendee_desc.extensions_by_full_name[ext.full_name] = ext + local list = extendee_desc.extensions_list + list[#list + 1] = ext end, } diff --git a/runtime/pb/json.lua b/runtime/pb/json.lua index b1c9851319ab1e9a9c205aac9ed4ede0b30ecc2e..57975ff7dd38f02809cdffbe0d6b67bebd233196 100644 --- a/runtime/pb/json.lua +++ b/runtime/pb/json.lua @@ -940,17 +940,21 @@ end end end -- Proto2 extensions: surface set entries under their bracketed - -- fully-qualified name (`[pkg.ext_name]`). Repeated extensions - -- emit as JSON arrays per the proto2 JSON spec. + -- fully-qualified name (`[pkg.ext_name]`). Walk the array + -- (`extensions_list`) rather than the hash — pairs() compiles to + -- ISNEXT which is NYI on the JIT. Repeated extensions emit as JSON + -- arrays per the proto2 JSON spec. local exts = t._extensions - if exts ~= nil and desc.extensions_by_full_name ~= nil then - for full_name, ext in pairs(desc.extensions_by_full_name) do - local v = exts[full_name] + local elist = exts ~= nil and desc.extensions_list or nil + if elist ~= nil then + for i = 1, #elist do + local ext = elist[i] + local v = exts[ext.full_name] if v ~= nil then - local k = '[' .. full_name .. ']' + local k = '[' .. ext.full_name .. ']' if ext.repeated then local arr = setmetatable({}, {__serialize='seq'}) - for i = 1, #v do arr[i] = encode_field_value(ext, v[i]) end + for j = 1, #v do arr[j] = encode_field_value(ext, v[j]) end out[k] = arr else out[k] = encode_field_value(ext, v) diff --git a/runtime/pb/text.lua b/runtime/pb/text.lua index d940150eb7585e6d4a8271021d8b9260f9165f36..b8055e6c79c857e282c4efdbbaf4bba7ebef9394 100644 --- a/runtime/pb/text.lua +++ b/runtime/pb/text.lua @@ -370,19 +370,23 @@ emit_field(buf, f, v, depth) end end -- Proto2 extensions: emit each set entry as `[full.name]: value` in - -- declaration order via the registry. Repeated extensions emit one - -- entry per element to keep the round-trip lossless. + -- registration order via the array view (`extensions_list`). Same + -- ISNEXT-NYI reasoning as the wire encoder — walk the array, not the + -- hash. Repeated extensions emit one entry per element to keep the + -- round-trip lossless. local exts = t._extensions - if exts ~= nil and desc.extensions_by_full_name ~= nil then - for full_name, ext in pairs(desc.extensions_by_full_name) do - local v = exts[full_name] + local elist = exts ~= nil and desc.extensions_list or nil + if elist ~= nil then + for i = 1, #elist do + local ext = elist[i] + local v = exts[ext.full_name] if v ~= nil then if ext.repeated then - for i = 1, #v do - emit_extension_entry(buf, ext, full_name, v[i], depth) + for j = 1, #v do + emit_extension_entry(buf, ext, ext.full_name, v[j], depth) end else - emit_extension_entry(buf, ext, full_name, v, depth) + emit_extension_entry(buf, ext, ext.full_name, v, depth) end end end diff --git a/test/proto/proto2_basic.proto b/test/proto/proto2_basic.proto index 98f2b6ff7b0f632ba29468db8b969d549ce8e172..6a588b069bb0d498b4733e2856057dbb2851eb30 100644 --- a/test/proto/proto2_basic.proto +++ b/test/proto/proto2_basic.proto @@ -48,3 +48,32 @@ repeated group RepGroup = 4 { optional int32 n = 5; } } + +// Bench fixture exercising the proto2-only shapes: required scalar, +// optional repeated, custom default, a `group` field, and a nested +// message. Used by bench/bench.lua against both modes. +message BenchPayload { + required int32 id = 1; + optional string name = 2 [default = "anonymous"]; + optional int32 retries = 3 [default = 3]; + repeated int32 lucky_numbers = 4 [packed = true]; + repeated string tags = 5; + optional Inner inner = 6; + optional group Stats = 7 { + optional int32 latency_ns = 8; + optional int32 attempts = 9; + } + + message Inner { + required string key = 1; + optional int32 weight = 2; + } + + // Reserve a range for the file-level extensions declared below. + extensions 100 to 199; +} + +extend BenchPayload { + optional int32 ext_count = 100; + optional string ext_label = 101; +} diff --git a/test/proto2_test.lua b/test/proto2_test.lua index c1f0a9158add19606445deda9e9eafdf54286a0a..73de9044d885ba020cb8df19281846b28dac5f8f 100644 --- a/test/proto2_test.lua +++ b/test/proto2_test.lua @@ -188,6 +188,52 @@ local s = pb.WithGroup_text({singlegroup = {a = 7}}) t.assert(s:find('SingleGroup'), 'expected SingleGroup label in: ' .. s) t.assert_not(s:find('singlegroup'), 'lowercase field name must not appear') end + + -- ----- Proto2 extensions ----- + + g.test_extension_registry_uses_array_view = function() + -- The hot encode loop iterates extensions_list (array) rather than + -- pairs() over extensions_by_full_name to stay JIT-stable. If a + -- future refactor drops the array view, encode silently falls off + -- the JIT — pin both indices here. + local d = pb.BenchPayload_descriptor + t.assert(type(d.extensions_list) == 'table', + 'extensions_list array view must exist') + t.assert(#d.extensions_list >= 2) + t.assert(d.extensions_by_id[100]) + t.assert(d.extensions_by_full_name['proto2_basic.ext_count']) + end + + g.test_extension_round_trip = function() + local msg = { + id = 7, + _extensions = { + ['proto2_basic.ext_count'] = 42, + ['proto2_basic.ext_label'] = 'tag', + }, + } + local dec = pb.BenchPayload_decode(pb.BenchPayload_encode(msg)) + t.assert_equals(dec.id, 7) + t.assert_equals(dec._extensions['proto2_basic.ext_count'], 42) + t.assert_equals(dec._extensions['proto2_basic.ext_label'], 'tag') + end + + g.test_extension_inline_codegen_walks_them = function() + -- Regression pin: the inline (full-mode) generated code added an + -- explicit extensions walk after the field loop. Verify that the + -- bytes-on-wire match what the runtime codec produces for the + -- same input. + local full = require('full.proto2_basic.proto2_basic_pb') + local runtime = require('runtime.proto2_basic.proto2_basic_pb') + local msg = { + id = 9, + _extensions = {['proto2_basic.ext_count'] = 17}, + } + t.assert_equals( + hex(full.BenchPayload_encode(msg)), + hex(runtime.BenchPayload_encode(msg)), + 'inline and runtime extension emission must match byte-for-byte') + end end -- Dynamic (source-parsed) proto2: load test/proto/proto2_basic.proto at