-- decode_unsafe: codegen emits a sister _decode_unsafe alongside -- _decode that skips the per-string utf8_len validation. Intended -- for re-decoding bytes from a trusted producer (own encoder, JSON/text -- round-trip, in-process typed RPC). Emitted in full mode only; runtime -- mode does not currently expose the unsafe path (compiled f._reader -- closures capture handler.decode by value, so a swap-on-call would not -- reach them — a proper runtime-mode unsafe path would need parallel -- _reader_unsafe closures and is intentionally deferred). (6bb) local t = require('luatest') local hello = require('full.hello.hello_pb') local g = t.group('decode_unsafe.full') -- Hand-rolled wire bytes for hello.Address{street=}. Tag for field 1 -- (wire 2, LEN) is 0x0A; length-prefix is one varint byte for len<128. local function address_with_street(s) return string.char(0x0A, #s) .. s end -- Hand-rolled wire bytes for hello.Person{name=}. Tag for field 1 -- is identical (0x0A). local function person_with_name(s) return string.char(0x0A, #s) .. s end g.test_valid_string_matches_safe_decode = function() local addr = {street = 'Pushkina 1', city = 'Moscow', zip = 123456} local bytes = hello.Address_encode(addr) t.assert_equals(hello.Address_decode_unsafe(bytes), hello.Address_decode(bytes)) end g.test_safe_decode_rejects_invalid_utf8 = function() -- 0xC0 0x80 is the classic overlong NUL — rejected by RFC 3629 -- (also banned in proto3 strings). local bytes = address_with_street('\xC0\x80') t.assert_error_msg_contains( 'invalid UTF-8', function() hello.Address_decode(bytes) end) end g.test_unsafe_decode_accepts_invalid_utf8 = function() local bytes = address_with_street('\xC0\x80') local dec = hello.Address_decode_unsafe(bytes) t.assert_equals(dec.street, '\xC0\x80') end g.test_unsafe_decode_repeated_string = function() -- Person.emails is a repeated string; two entries, second is invalid. -- Tag 0x1A = field 3 (emails), wire 2. local good = 'alice@example.com' local bad = '\xFF\xFE' local bytes = string.char(0x1A, #good) .. good .. string.char(0x1A, #bad) .. bad t.assert_error_msg_contains( 'invalid UTF-8', function() hello.Person_decode(bytes) end) local dec = hello.Person_decode_unsafe(bytes) t.assert_equals(dec.emails, {good, bad}) end g.test_unsafe_decode_recurses_into_sub_messages = function() -- Person{address = Address{street = '\xC0\x80'}}. -- Tag 0x2A = field 5 (address), wire 2; payload is the Address bytes. local inner = address_with_street('\xC0\x80') local bytes = string.char(0x2A, #inner) .. inner -- Safe path: nested string rejected (proves nested validation runs by -- default). t.assert_error_msg_contains( 'invalid UTF-8', function() hello.Person_decode(bytes) end) -- Unsafe path: nested call must also be the unsafe variant. If -- Person_decode_unsafe were to call Address_decode (the safe variant) -- for sub-messages, this would still error. The recursive dispatch is -- emitted by inline.go and pinned by this assertion. local dec = hello.Person_decode_unsafe(bytes) t.assert_equals(dec.address.street, '\xC0\x80') end g.test_unsafe_decode_handles_long_string_fallback = function() -- >=128 byte payload exits the 1-byte LEN inline fast path and falls -- through to wire.decode_bytes (instead of wire.decode_string) on -- the unsafe path. Exercises the fallback branch in the emitted code. local big = string.rep('x', 200) .. '\xFF' -- 201 bytes, trailing bad local bytes = string.char(0x0A) .. string.char(0xC9, 0x01) .. big -- 201 in varint = 0xC9 0x01. t.assert_error_msg_contains( 'invalid UTF-8', function() hello.Address_decode(bytes) end) local dec = hello.Address_decode_unsafe(bytes) t.assert_equals(#dec.street, 201) t.assert_equals(dec.street:byte(201), 0xFF) end -- Person.name omitted from the above explicitly to keep tests focused; -- the singular-string scalar path is already covered by Address.street. _ = person_with_name -- silence unused-local under future trimming