~bigbes/tarantool

tarantool-protobuf

b09c7213874b18bd0f8d1c4d60d3ec8253f1be6d — Eugene Blikh 2 months ago ee07048
codegen: inline 1-byte LEN fast path for string/bytes decode

Singular and repeated string/bytes fields in generated full-mode _decode
now read the length byte and dispatch in-line instead of calling
wire.decode_string / wire.decode_bytes. For length < 128 (the common
short-string RPC case), the path stays inside the parent JIT trace:
no child-trace stitch, no per-element function frame, utf8_len lookup
hoisted to a generated-file upvalue.

The original a6n design — one ffi.cast(U8CP, buf) at the top of each
_decode — was abandoned: the cdata wrapper is 24 bytes per call and
LuaJIT can't sink the allocation because the pointer local lives
across wire.decode_* call frames. Net regression at small sizes
(+11-16%) overwhelmed the single-byte-read savings.

Bench (hello.Person full-mode decode):

      size    ns/op before   ns/op after   delta
      10B          419            390     -6.9%
      100B         515            478     -7.2%
      1KB         9004           8042    -10.7%
      10KB       64644          56745    -12.2%
      100KB     625638         537750    -14.0%

Zero allocation impact across all sizes. Tests: 1043 pass, 37 JIT
trace gates pass. (a6n)
M cmd/protoc-gen-tarantool/internal/gen/gen.go => cmd/protoc-gen-tarantool/internal/gen/gen.go +1 -0
@@ 181,6 181,7 @@ func emitHeader(w *writer, file *protogen.File) {
	// generated _decode function. Localizing turns the LuaJIT references
	// into upvalue reads on the trace instead of repeated global lookups.
	w.line("local string_byte = string.byte")
	w.line("local utf8_len = require('utf8').len")
	w.line("local band = bit.band")
	w.line("local rshift = bit.rshift")
}

M cmd/protoc-gen-tarantool/internal/gen/inline.go => cmd/protoc-gen-tarantool/internal/gen/inline.go +58 -6
@@ 505,9 505,19 @@ func emitInlineDecodeFieldBody(w *writer, f *protogen.Field, file *protogen.File
		w.line("            %s = wire.varint_to_int32(u)", luaFieldAccess("result", fname))
	default:
		st := scalarName(f.Desc.Kind())
		w.line("            local val")
		w.line("            val, pos = wire.decode_%s(buf, pos)", st)
		w.line("            %s = val", luaFieldAccess("result", fname))
		if st == "string" || st == "bytes" {
			// Inline the 1-byte LEN fast path (length < 128). Skips the
			// `wire.decode_string` / `wire.decode_bytes` function call
			// frame for the typical short-string case; the resulting
			// straight-line code stays inside the parent JIT trace
			// instead of stitching through a child trace. (a6n)
			dst := luaFieldAccess("result", fname)
			emitInlineStringBytesScalar(w, st, dst)
		} else {
			w.line("            local val")
			w.line("            val, pos = wire.decode_%s(buf, pos)", st)
			w.line("            %s = val", luaFieldAccess("result", fname))
		}
	}

	// Oneof: clear sibling branches so callers see exactly one set field.


@@ 577,13 587,55 @@ func emitInlineDecodeRepeated(w *writer, f *protogen.Field, fname string, file *
			w.line("                %s = %s + 1; list[%s] = val", cnt, cnt, cnt)
			w.line("            end")
		} else {
			w.line("            local val")
			w.line("            val, pos = wire.decode_%s(buf, pos)", st)
			w.line("            %s = %s + 1; list[%s] = val", cnt, cnt, cnt)
			// Repeated string/bytes — inline 1-byte LEN fast path. (a6n)
			emitInlineStringBytesRepeated(w, st, cnt)
		}
	}
}

// emitInlineStringBytesScalar emits the singular string/bytes decode with the
// 1-byte LEN fast path inlined. Falls back to `wire.decode_<st>` for lengths
// >= 128. UTF-8 validation runs only for `string`, not `bytes`. (a6n)
func emitInlineStringBytesScalar(w *writer, st, dst string) {
	w.line("            local _lb = string_byte(buf, pos)")
	w.line("            if _lb ~= nil and _lb < 0x80 then")
	w.line("                local _np = pos + 1")
	w.line("                local _epos = _np + _lb")
	w.line("                if _epos - 1 > len then error(\"truncated LEN at offset \" .. pos, 0) end")
	w.line("                local _s = buf:sub(_np, _epos - 1)")
	if st == "string" {
		w.line("                if utf8_len(_s) == nil then error(\"invalid UTF-8 in string field at offset \" .. pos, 0) end")
	}
	w.line("                %s = _s", dst)
	w.line("                pos = _epos")
	w.line("            else")
	w.line("                local val")
	w.line("                val, pos = wire.decode_%s(buf, pos)", st)
	w.line("                %s = val", dst)
	w.line("            end")
}

// emitInlineStringBytesRepeated mirrors emitInlineStringBytesScalar but
// appends to the per-field list via the `_n_<f>` counter. (a6n)
func emitInlineStringBytesRepeated(w *writer, st, cnt string) {
	w.line("            local _lb = string_byte(buf, pos)")
	w.line("            if _lb ~= nil and _lb < 0x80 then")
	w.line("                local _np = pos + 1")
	w.line("                local _epos = _np + _lb")
	w.line("                if _epos - 1 > len then error(\"truncated LEN at offset \" .. pos, 0) end")
	w.line("                local _s = buf:sub(_np, _epos - 1)")
	if st == "string" {
		w.line("                if utf8_len(_s) == nil then error(\"invalid UTF-8 in string field at offset \" .. pos, 0) end")
	}
	w.line("                %s = %s + 1; list[%s] = _s", cnt, cnt, cnt)
	w.line("                pos = _epos")
	w.line("            else")
	w.line("                local val")
	w.line("                val, pos = wire.decode_%s(buf, pos)", st)
	w.line("                %s = %s + 1; list[%s] = val", cnt, cnt, cnt)
	w.line("            end")
}

// ----------------------------------------------------------------------------
// Map field codegen
// ----------------------------------------------------------------------------

M examples/expected/full/c_int64/c_int64_pb.lua => examples/expected/full/c_int64/c_int64_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift


M examples/expected/full/c_nested/c_nested_pb.lua => examples/expected/full/c_nested/c_nested_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift


M examples/expected/full/c_repeated/c_repeated_pb.lua => examples/expected/full/c_repeated/c_repeated_pb.lua +42 -9
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift



@@ 175,9 176,20 @@ function M.Inner_decode(buf)
            val, pos = wire.decode_int32(buf, pos)
            result.v = val
        elseif id == 2 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.s = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.s = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.s = val
            end
        else
            local _ebid = M.Inner_descriptor.extensions_by_id
            local _ext = _ebid and _ebid[id] or nil


@@ 715,15 727,36 @@ function M.Holder_decode(buf)
        elseif id == 30 then
            local list = result.strings
            if list == nil then list = {}; result.strings = list end
            local val
            val, pos = wire.decode_string(buf, pos)
            _n_strings = _n_strings + 1; list[_n_strings] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_strings = _n_strings + 1; list[_n_strings] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_strings = _n_strings + 1; list[_n_strings] = val
            end
        elseif id == 31 then
            local list = result.blobs
            if list == nil then list = {}; result.blobs = list end
            local val
            val, pos = wire.decode_bytes(buf, pos)
            _n_blobs = _n_blobs + 1; list[_n_blobs] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                _n_blobs = _n_blobs + 1; list[_n_blobs] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                _n_blobs = _n_blobs + 1; list[_n_blobs] = val
            end
        elseif id == 40 then
            local list = result.messages
            if list == nil then list = {}; result.messages = list end

M examples/expected/full/conformance/conformance_pb.lua => examples/expected/full/conformance/conformance_pb.lua +237 -51
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift



@@ 295,17 296,50 @@ function M.TestStatus_decode(buf)
            id, wt, pos = wire.decode_tag(buf, pos)
        end
        if id == 1 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.name = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.name = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.name = val
            end
        elseif id == 2 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.failure_message = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.failure_message = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.failure_message = val
            end
        elseif id == 3 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.matched_name = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.matched_name = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.matched_name = val
            end
        else
            local _ebid = M.TestStatus_descriptor.extensions_by_id
            local _ext = _ebid and _ebid[id] or nil


@@ 614,30 648,73 @@ function M.ConformanceRequest_decode(buf)
            id, wt, pos = wire.decode_tag(buf, pos)
        end
        if id == 1 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.protobuf_payload = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.protobuf_payload = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.protobuf_payload = val
            end
            result.json_payload = nil
            result.jspb_payload = nil
            result.text_payload = nil
        elseif id == 2 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.json_payload = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.json_payload = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.json_payload = val
            end
            result.protobuf_payload = nil
            result.jspb_payload = nil
            result.text_payload = nil
        elseif id == 7 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.jspb_payload = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.jspb_payload = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.jspb_payload = val
            end
            result.protobuf_payload = nil
            result.json_payload = nil
            result.text_payload = nil
        elseif id == 8 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.text_payload = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.text_payload = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.text_payload = val
            end
            result.protobuf_payload = nil
            result.json_payload = nil
            result.jspb_payload = nil


@@ 646,9 723,20 @@ function M.ConformanceRequest_decode(buf)
            u, pos = wire.decode_varint(buf, pos)
            result.requested_output_format = wire.varint_to_int32(u)
        elseif id == 4 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.message_type = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.message_type = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.message_type = val
            end
        elseif id == 5 then
            local u
            u, pos = wire.decode_varint(buf, pos)


@@ 872,9 960,20 @@ function M.ConformanceResponse_decode(buf)
            id, wt, pos = wire.decode_tag(buf, pos)
        end
        if id == 1 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.parse_error = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.parse_error = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.parse_error = val
            end
            result.serialize_error = nil
            result.timeout_error = nil
            result.runtime_error = nil


@@ 884,9 983,20 @@ function M.ConformanceResponse_decode(buf)
            result.jspb_payload = nil
            result.text_payload = nil
        elseif id == 6 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.serialize_error = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.serialize_error = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.serialize_error = val
            end
            result.parse_error = nil
            result.timeout_error = nil
            result.runtime_error = nil


@@ 896,9 1006,20 @@ function M.ConformanceResponse_decode(buf)
            result.jspb_payload = nil
            result.text_payload = nil
        elseif id == 9 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.timeout_error = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.timeout_error = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.timeout_error = val
            end
            result.parse_error = nil
            result.serialize_error = nil
            result.runtime_error = nil


@@ 908,9 1029,20 @@ function M.ConformanceResponse_decode(buf)
            result.jspb_payload = nil
            result.text_payload = nil
        elseif id == 2 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.runtime_error = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.runtime_error = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.runtime_error = val
            end
            result.parse_error = nil
            result.serialize_error = nil
            result.timeout_error = nil


@@ 920,9 1052,19 @@ function M.ConformanceResponse_decode(buf)
            result.jspb_payload = nil
            result.text_payload = nil
        elseif id == 3 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.protobuf_payload = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.protobuf_payload = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.protobuf_payload = val
            end
            result.parse_error = nil
            result.serialize_error = nil
            result.timeout_error = nil


@@ 932,9 1074,20 @@ function M.ConformanceResponse_decode(buf)
            result.jspb_payload = nil
            result.text_payload = nil
        elseif id == 4 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.json_payload = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.json_payload = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.json_payload = val
            end
            result.parse_error = nil
            result.serialize_error = nil
            result.timeout_error = nil


@@ 944,9 1097,20 @@ function M.ConformanceResponse_decode(buf)
            result.jspb_payload = nil
            result.text_payload = nil
        elseif id == 5 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.skipped = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.skipped = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.skipped = val
            end
            result.parse_error = nil
            result.serialize_error = nil
            result.timeout_error = nil


@@ 956,9 1120,20 @@ function M.ConformanceResponse_decode(buf)
            result.jspb_payload = nil
            result.text_payload = nil
        elseif id == 7 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.jspb_payload = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.jspb_payload = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.jspb_payload = val
            end
            result.parse_error = nil
            result.serialize_error = nil
            result.timeout_error = nil


@@ 968,9 1143,20 @@ function M.ConformanceResponse_decode(buf)
            result.skipped = nil
            result.text_payload = nil
        elseif id == 8 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.text_payload = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.text_payload = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.text_payload = val
            end
            result.parse_error = nil
            result.serialize_error = nil
            result.timeout_error = nil

M examples/expected/full/hello/hello_pb.lua => examples/expected/full/hello/hello_pb.lua +140 -30
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift



@@ 315,9 316,20 @@ function M.Result_decode(buf)
            val, pos = wire.decode_int32(buf, pos)
            result.id = val
        elseif id == 2 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.text = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.text = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.text = val
            end
            result.code = nil
            result.details = nil
        elseif id == 3 then


@@ 437,9 449,20 @@ function M.HelloRequest_decode(buf)
            id, wt, pos = wire.decode_tag(buf, pos)
        end
        if id == 1 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.name = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.name = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.name = val
            end
        else
            local _ebid = M.HelloRequest_descriptor.extensions_by_id
            local _ext = _ebid and _ebid[id] or nil


@@ 540,9 563,20 @@ function M.HelloReply_decode(buf)
            id, wt, pos = wire.decode_tag(buf, pos)
        end
        if id == 1 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.greeting = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.greeting = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.greeting = val
            end
        else
            local _ebid = M.HelloReply_descriptor.extensions_by_id
            local _ext = _ebid and _ebid[id] or nil


@@ 786,9 820,20 @@ function M.Event_decode(buf)
            id, wt, pos = wire.decode_tag(buf, pos)
        end
        if id == 1 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.title = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.title = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.title = val
            end
        elseif id == 2 then
            local payload
            payload, pos = wire.decode_len(buf, pos)


@@ 963,21 1008,54 @@ function M.Address_decode(buf)
            id, wt, pos = wire.decode_tag(buf, pos)
        end
        if id == 1 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.street = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.street = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.street = val
            end
        elseif id == 2 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.city = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.city = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.city = val
            end
        elseif id == 3 then
            local val
            val, pos = wire.decode_int32(buf, pos)
            result.zip = val
        elseif id == 4 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.apartment = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.apartment = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.apartment = val
            end
        else
            local _ebid = M.Address_descriptor.extensions_by_id
            local _ext = _ebid and _ebid[id] or nil


@@ 1272,9 1350,20 @@ function M.Person_decode(buf)
            id, wt, pos = wire.decode_tag(buf, pos)
        end
        if id == 1 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.name = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.name = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.name = val
            end
        elseif id == 2 then
            local val
            val, pos = wire.decode_int32(buf, pos)


@@ 1282,9 1371,20 @@ function M.Person_decode(buf)
        elseif id == 3 then
            local list = result.emails
            if list == nil then list = {}; result.emails = list end
            local val
            val, pos = wire.decode_string(buf, pos)
            _n_emails = _n_emails + 1; list[_n_emails] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_emails = _n_emails + 1; list[_n_emails] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_emails = _n_emails + 1; list[_n_emails] = val
            end
        elseif id == 4 then
            local u
            u, pos = wire.decode_varint(buf, pos)


@@ 1322,9 1422,19 @@ function M.Person_decode(buf)
                _n_lucky_numbers = _n_lucky_numbers + 1; list[_n_lucky_numbers] = val
            end
        elseif id == 8 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.avatar = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.avatar = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.avatar = val
            end
        elseif id == 9 then
            local val
            val, pos = wire.decode_fixed64(buf, pos)

M examples/expected/full/proto2_basic/proto2_basic_pb.lua => examples/expected/full/proto2_basic/proto2_basic_pb.lua +84 -18
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift



@@ 372,9 373,20 @@ function M.Defaults_decode(buf)
            val, pos = wire.decode_int32(buf, pos)
            result.i = val
        elseif id == 2 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.s = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.s = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.s = val
            end
        elseif id == 3 then
            local val
            val, pos = wire.decode_bool(buf, pos)


@@ 396,9 408,19 @@ function M.Defaults_decode(buf)
            val, pos = wire.decode_uint64(buf, pos)
            result.u64 = val
        elseif id == 8 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.by = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.by = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.by = val
            end
        elseif id == 9 then
            local u
            u, pos = wire.decode_varint(buf, pos)


@@ 1121,9 1143,20 @@ function M.WithGroup_SingleGroup_decode(buf)
            val, pos = wire.decode_int32(buf, pos)
            result.a = val
        elseif id == 3 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.s = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.s = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.s = val
            end
        else
            local _ebid = M.WithGroup_SingleGroup_descriptor.extensions_by_id
            local _ext = _ebid and _ebid[id] or nil


@@ 1408,9 1441,20 @@ function M.BenchPayload_decode(buf)
            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
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.name = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.name = val
            end
        elseif id == 3 then
            local val
            val, pos = wire.decode_int32(buf, pos)


@@ 1435,9 1479,20 @@ function M.BenchPayload_decode(buf)
        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)
            _n_tags = _n_tags + 1; list[_n_tags] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_tags = _n_tags + 1; list[_n_tags] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_tags = _n_tags + 1; list[_n_tags] = val
            end
        elseif id == 6 then
            local payload
            payload, pos = wire.decode_len(buf, pos)


@@ 1700,9 1755,20 @@ function M.BenchPayload_Inner_decode(buf)
            id, wt, pos = wire.decode_tag(buf, pos)
        end
        if id == 1 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.key = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.key = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.key = val
            end
        elseif id == 2 then
            local val
            val, pos = wire.decode_int32(buf, pos)

M examples/expected/full/protobuf_test_messages/proto2/test_messages_proto2_pb.lua => examples/expected/full/protobuf_test_messages/proto2/test_messages_proto2_pb.lua +303 -66
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift



@@ 2737,13 2738,34 @@ function M.TestAllTypesProto2_decode(buf)
            val, pos = wire.decode_bool(buf, pos)
            result.optional_bool = val
        elseif id == 14 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.optional_string = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.optional_string = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.optional_string = val
            end
        elseif id == 15 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.optional_bytes = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.optional_bytes = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.optional_bytes = val
            end
        elseif id == 18 then
            local payload
            payload, pos = wire.decode_len(buf, pos)


@@ 2771,13 2793,35 @@ function M.TestAllTypesProto2_decode(buf)
            u, pos = wire.decode_varint(buf, pos)
            result.optional_foreign_enum = wire.varint_to_int32(u)
        elseif id == 24 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.optional_string_piece = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.optional_string_piece = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.optional_string_piece = val
            end
        elseif id == 25 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.optional_cord = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.optional_cord = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.optional_cord = val
            end
        elseif id == 27 then
            local payload
            payload, pos = wire.decode_len(buf, pos)


@@ 3011,15 3055,36 @@ function M.TestAllTypesProto2_decode(buf)
        elseif id == 44 then
            local list = result.repeated_string
            if list == nil then list = {}; result.repeated_string = list end
            local val
            val, pos = wire.decode_string(buf, pos)
            _n_repeated_string = _n_repeated_string + 1; list[_n_repeated_string] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_repeated_string = _n_repeated_string + 1; list[_n_repeated_string] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_repeated_string = _n_repeated_string + 1; list[_n_repeated_string] = val
            end
        elseif id == 45 then
            local list = result.repeated_bytes
            if list == nil then list = {}; result.repeated_bytes = list end
            local val
            val, pos = wire.decode_bytes(buf, pos)
            _n_repeated_bytes = _n_repeated_bytes + 1; list[_n_repeated_bytes] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                _n_repeated_bytes = _n_repeated_bytes + 1; list[_n_repeated_bytes] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                _n_repeated_bytes = _n_repeated_bytes + 1; list[_n_repeated_bytes] = val
            end
        elseif id == 48 then
            local list = result.repeated_nested_message
            if list == nil then list = {}; result.repeated_nested_message = list end


@@ 3069,15 3134,37 @@ function M.TestAllTypesProto2_decode(buf)
        elseif id == 54 then
            local list = result.repeated_string_piece
            if list == nil then list = {}; result.repeated_string_piece = list end
            local val
            val, pos = wire.decode_string(buf, pos)
            _n_repeated_string_piece = _n_repeated_string_piece + 1; list[_n_repeated_string_piece] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_repeated_string_piece = _n_repeated_string_piece + 1; list[_n_repeated_string_piece] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_repeated_string_piece = _n_repeated_string_piece + 1; list[_n_repeated_string_piece] = val
            end
        elseif id == 55 then
            local list = result.repeated_cord
            if list == nil then list = {}; result.repeated_cord = list end
            local val
            val, pos = wire.decode_string(buf, pos)
            _n_repeated_cord = _n_repeated_cord + 1; list[_n_repeated_cord] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_repeated_cord = _n_repeated_cord + 1; list[_n_repeated_cord] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_repeated_cord = _n_repeated_cord + 1; list[_n_repeated_cord] = val
            end
        elseif id == 75 then
            local list = result.packed_int32
            if list == nil then list = {}; result.packed_int32 = list end


@@ 4008,9 4095,20 @@ function M.TestAllTypesProto2_decode(buf)
            result.oneof_double = nil
            result.oneof_enum = nil
        elseif id == 113 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.oneof_string = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.oneof_string = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.oneof_string = val
            end
            result.oneof_uint32 = nil
            result.oneof_nested_message = nil
            result.oneof_bytes = nil


@@ 4020,9 4118,19 @@ function M.TestAllTypesProto2_decode(buf)
            result.oneof_double = nil
            result.oneof_enum = nil
        elseif id == 114 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.oneof_bytes = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.oneof_bytes = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.oneof_bytes = val
            end
            result.oneof_uint32 = nil
            result.oneof_nested_message = nil
            result.oneof_string = nil


@@ 4162,13 4270,34 @@ function M.TestAllTypesProto2_decode(buf)
            val, pos = wire.decode_bool(buf, pos)
            result.default_bool = val
        elseif id == 254 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.default_string = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.default_string = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.default_string = val
            end
        elseif id == 255 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.default_bytes = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.default_bytes = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.default_bytes = val
            end
        elseif id == 401 then
            local val
            val, pos = wire.decode_int32(buf, pos)


@@ 5254,9 5383,20 @@ function M.UnknownToTestAllTypes_decode(buf)
            val, pos = wire.decode_int32(buf, pos)
            result.optional_int32 = val
        elseif id == 1002 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.optional_string = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.optional_string = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.optional_string = val
            end
        elseif id == 1003 then
            local payload
            payload, pos = wire.decode_len(buf, pos)


@@ 5699,9 5839,20 @@ function M.OneStringProto2_decode(buf)
            id, wt, pos = wire.decode_tag(buf, pos)
        end
        if id == 1 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.data = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.data = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.data = val
            end
        else
            local _ebid = M.OneStringProto2_descriptor.extensions_by_id
            local _ext = _ebid and _ebid[id] or nil


@@ 5834,15 5985,37 @@ function M.ProtoWithKeywords_decode(buf)
            val, pos = wire.decode_int32(buf, pos)
            result.inline = val
        elseif id == 2 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.concept = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.concept = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.concept = val
            end
        elseif id == 3 then
            local list = result.requires
            if list == nil then list = {}; result.requires = list end
            local val
            val, pos = wire.decode_string(buf, pos)
            _n_requires = _n_requires + 1; list[_n_requires] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_requires = _n_requires + 1; list[_n_requires] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_requires = _n_requires + 1; list[_n_requires] = val
            end
        else
            local _ebid = M.ProtoWithKeywords_descriptor.extensions_by_id
            local _ext = _ebid and _ebid[id] or nil


@@ 6350,13 6523,34 @@ function M.TestAllRequiredTypesProto2_decode(buf)
            val, pos = wire.decode_bool(buf, pos)
            result.required_bool = val
        elseif id == 14 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.required_string = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.required_string = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.required_string = val
            end
        elseif id == 15 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.required_bytes = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.required_bytes = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.required_bytes = val
            end
        elseif id == 18 then
            local payload
            payload, pos = wire.decode_len(buf, pos)


@@ 6384,13 6578,35 @@ function M.TestAllRequiredTypesProto2_decode(buf)
            u, pos = wire.decode_varint(buf, pos)
            result.required_foreign_enum = wire.varint_to_int32(u)
        elseif id == 24 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.required_string_piece = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.required_string_piece = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.required_string_piece = val
            end
        elseif id == 25 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.required_cord = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.required_cord = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.required_cord = val
            end
        elseif id == 27 then
            local payload
            payload, pos = wire.decode_len(buf, pos)


@@ 6471,13 6687,34 @@ function M.TestAllRequiredTypesProto2_decode(buf)
            val, pos = wire.decode_bool(buf, pos)
            result.default_bool = val
        elseif id == 254 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.default_string = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.default_string = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.default_string = val
            end
        elseif id == 255 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.default_bytes = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.default_bytes = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.default_bytes = val
            end
        else
            local _ebid = M.TestAllRequiredTypesProto2_descriptor.extensions_by_id
            local _ext = _ebid and _ebid[id] or nil

M examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua => examples/expected/full/protobuf_test_messages/proto3/test_messages_proto3_pb.lua +138 -30
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift



@@ 2908,13 2909,34 @@ function M.TestAllTypesProto3_decode(buf)
            val, pos = wire.decode_bool(buf, pos)
            result.optional_bool = val
        elseif id == 14 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.optional_string = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.optional_string = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.optional_string = val
            end
        elseif id == 15 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.optional_bytes = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.optional_bytes = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.optional_bytes = val
            end
        elseif id == 18 then
            local payload
            payload, pos = wire.decode_len(buf, pos)


@@ 2946,13 2968,35 @@ function M.TestAllTypesProto3_decode(buf)
            u, pos = wire.decode_varint(buf, pos)
            result.optional_aliased_enum = wire.varint_to_int32(u)
        elseif id == 24 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.optional_string_piece = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.optional_string_piece = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.optional_string_piece = val
            end
        elseif id == 25 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.optional_cord = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.optional_cord = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.optional_cord = val
            end
        elseif id == 27 then
            local payload
            payload, pos = wire.decode_len(buf, pos)


@@ 3186,15 3230,36 @@ function M.TestAllTypesProto3_decode(buf)
        elseif id == 44 then
            local list = result.repeated_string
            if list == nil then list = {}; result.repeated_string = list end
            local val
            val, pos = wire.decode_string(buf, pos)
            _n_repeated_string = _n_repeated_string + 1; list[_n_repeated_string] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_repeated_string = _n_repeated_string + 1; list[_n_repeated_string] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_repeated_string = _n_repeated_string + 1; list[_n_repeated_string] = val
            end
        elseif id == 45 then
            local list = result.repeated_bytes
            if list == nil then list = {}; result.repeated_bytes = list end
            local val
            val, pos = wire.decode_bytes(buf, pos)
            _n_repeated_bytes = _n_repeated_bytes + 1; list[_n_repeated_bytes] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                _n_repeated_bytes = _n_repeated_bytes + 1; list[_n_repeated_bytes] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                _n_repeated_bytes = _n_repeated_bytes + 1; list[_n_repeated_bytes] = val
            end
        elseif id == 48 then
            local list = result.repeated_nested_message
            if list == nil then list = {}; result.repeated_nested_message = list end


@@ 3244,15 3309,37 @@ function M.TestAllTypesProto3_decode(buf)
        elseif id == 54 then
            local list = result.repeated_string_piece
            if list == nil then list = {}; result.repeated_string_piece = list end
            local val
            val, pos = wire.decode_string(buf, pos)
            _n_repeated_string_piece = _n_repeated_string_piece + 1; list[_n_repeated_string_piece] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_repeated_string_piece = _n_repeated_string_piece + 1; list[_n_repeated_string_piece] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_repeated_string_piece = _n_repeated_string_piece + 1; list[_n_repeated_string_piece] = val
            end
        elseif id == 55 then
            local list = result.repeated_cord
            if list == nil then list = {}; result.repeated_cord = list end
            local val
            val, pos = wire.decode_string(buf, pos)
            _n_repeated_cord = _n_repeated_cord + 1; list[_n_repeated_cord] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_repeated_cord = _n_repeated_cord + 1; list[_n_repeated_cord] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_repeated_cord = _n_repeated_cord + 1; list[_n_repeated_cord] = val
            end
        elseif id == 75 then
            local list = result.packed_int32
            if list == nil then list = {}; result.packed_int32 = list end


@@ 4145,9 4232,20 @@ function M.TestAllTypesProto3_decode(buf)
            result.oneof_enum = nil
            result.oneof_null_value = nil
        elseif id == 113 then
            local val
            val, pos = wire.decode_string(buf, pos)
            result.oneof_string = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.oneof_string = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.oneof_string = val
            end
            result.oneof_uint32 = nil
            result.oneof_nested_message = nil
            result.oneof_bytes = nil


@@ 4158,9 4256,19 @@ function M.TestAllTypesProto3_decode(buf)
            result.oneof_enum = nil
            result.oneof_null_value = nil
        elseif id == 114 then
            local val
            val, pos = wire.decode_bytes(buf, pos)
            result.oneof_bytes = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                result.oneof_bytes = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_bytes(buf, pos)
                result.oneof_bytes = val
            end
            result.oneof_uint32 = nil
            result.oneof_nested_message = nil
            result.oneof_string = nil

M examples/expected/full/quickstart/quickstart_pb.lua => examples/expected/full/quickstart/quickstart_pb.lua +29 -6
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift



@@ 166,9 167,20 @@ function M.User_decode(buf)
            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
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                result.name = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                result.name = val
            end
        elseif id == 3 then
            local u
            u, pos = wire.decode_varint(buf, pos)


@@ 176,9 188,20 @@ function M.User_decode(buf)
        elseif id == 4 then
            local list = result.emails
            if list == nil then list = {}; result.emails = list end
            local val
            val, pos = wire.decode_string(buf, pos)
            _n_emails = _n_emails + 1; list[_n_emails] = val
            local _lb = string_byte(buf, pos)
            if _lb ~= nil and _lb < 0x80 then
                local _np = pos + 1
                local _epos = _np + _lb
                if _epos - 1 > len then error("truncated LEN at offset " .. pos, 0) end
                local _s = buf:sub(_np, _epos - 1)
                if utf8_len(_s) == nil then error("invalid UTF-8 in string field at offset " .. pos, 0) end
                _n_emails = _n_emails + 1; list[_n_emails] = _s
                pos = _epos
            else
                local val
                val, pos = wire.decode_string(buf, pos)
                _n_emails = _n_emails + 1; list[_n_emails] = val
            end
        else
            local _ebid = M.User_descriptor.extensions_by_id
            local _ext = _ebid and _ebid[id] or nil

M examples/expected/runtime/c_int64/c_int64_pb.lua => examples/expected/runtime/c_int64/c_int64_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift


M examples/expected/runtime/c_nested/c_nested_pb.lua => examples/expected/runtime/c_nested/c_nested_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift


M examples/expected/runtime/c_repeated/c_repeated_pb.lua => examples/expected/runtime/c_repeated/c_repeated_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift


M examples/expected/runtime/conformance/conformance_pb.lua => examples/expected/runtime/conformance/conformance_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift


M examples/expected/runtime/hello/hello_pb.lua => examples/expected/runtime/hello/hello_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift


M examples/expected/runtime/proto2_basic/proto2_basic_pb.lua => examples/expected/runtime/proto2_basic/proto2_basic_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift


M examples/expected/runtime/protobuf_test_messages/proto2/test_messages_proto2_pb.lua => examples/expected/runtime/protobuf_test_messages/proto2/test_messages_proto2_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift


M examples/expected/runtime/protobuf_test_messages/proto3/test_messages_proto3_pb.lua => examples/expected/runtime/protobuf_test_messages/proto3/test_messages_proto3_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift


M examples/expected/runtime/quickstart/quickstart_pb.lua => examples/expected/runtime/quickstart/quickstart_pb.lua +1 -0
@@ 6,6 6,7 @@
local pb = require("pb")
local wire = pb.wire
local string_byte = string.byte
local utf8_len = require('utf8').len
local band = bit.band
local rshift = bit.rshift