lazy: zero-copy decode view (decode_lazy) with passthrough re-encode Adds pb.decode_lazy(desc, bytes) returning a MessageView/ArrayView/MapView that indexes the wire bytes in a single pass and decodes individual fields only on :get / :at access. Nested messages return more lazy sub-views; WKT descriptors (those carrying desc.decode) are eager-wrapped so the API stays uniform. Surface (see runtime/pb/lazy.lua): - MessageView: :get / :has / :which / :iter / :names / :set / :encode - ArrayView: :len / :at / :iter / :tolist - MapView: :get / :has / :keys / :iter / :totable :encode is three-modes: WKT delegates to desc.encode on the materialized table; untouched views return their original bytes verbatim (passthrough); mixed views walk fields in id order, splicing clean segments and re-emitting dirty ones. Sub-MessageView mutations propagate to parent encode via a flat _sub_msg_views array (walked with ipairs, so :is_dirty stays on a single JIT trace — pairs over a hash is NYI in LuaJIT 2.1). Codegen emits M.<Type>_decode_lazy in both modes as a one-line delegation to pb.decode_lazy(<desc>, b); no inline expansion. codec.encode_field is exposed so the lazy passthrough emitter can splice fresh bytes for a single dirty field without rebuilding the whole message. Tests: 40 new lazy_test.lua cases parameterized over both codegen modes; all 11 interop fixtures round-trip byte-equal through decode_lazy(b):encode() in both modes. Total: 300/300 luatest, up from 226.
Initial commit: protoc-gen-tarantool plugin + pb runtime A protoc plugin (Go) and a pure-Lua + LuaJIT-FFI runtime that give Tarantool a complete proto3 + gRPC stack. Two codegen modes (full inline / runtime descriptor), 226-test luatest suite, 18-fixture mainline-protoc interop corpus, JSON codec, well-known types, gRPC client/server factories, runtime .proto parser, microbench harness with allocation regression gate. Covers PLAN.md M1-M5. Module is `pb` (not `protobuf`) to avoid colliding with Tarantool's built-in encode-only `protobuf` module.