diff --git a/runtime/pb/wire.lua b/runtime/pb/wire.lua index 20dbd4241ae896e11520c3ff99850855311b8a4a..cb1cef8c976768d9950830d3b54fc2e2a3888b2e 100644 --- a/runtime/pb/wire.lua +++ b/runtime/pb/wire.lua @@ -276,6 +276,20 @@ local function decode_float(buf, pos) if pos + 3 > #buf then error("truncated float", 0) end ffi.copy(F32.b, buf:sub(pos, pos + 3), 4) + -- Detect Inf/NaN from the raw bit pattern before going through + -- tonumber(). LuaJIT 2.1 NaN-boxes Lua values, so some IEEE NaN + -- payloads collide with internal type tags (nil/function/etc.) and + -- `tonumber(F32.f)` yields a non-number. Reading via F32.u (uint32) + -- keeps us in the integer domain until we decide what to return. + local u = F32.u + local exp = bit.band(bit.rshift(u, 23), 0xff) + if exp == 0xff then + if bit.band(u, 0x7fffff) == 0 then + if bit.band(u, 0x80000000) ~= 0 then return -math.huge, pos + 4 end + return math.huge, pos + 4 + end + return 0/0, pos + 4 + end return tonumber(F32.f), pos + 4 end M.decode_float = decode_float @@ -286,9 +300,28 @@ return ffi.string(F64.b, 8) end M.encode_double = encode_double +local F64_EXP_MASK = UINT64(0x7ff) +local F64_FRAC_MASK = UINT64(0xfffffffffffff) +local F64_SIGN_BIT = bit.lshift(UINT64(1), 63) + local function decode_double(buf, pos) if pos + 7 > #buf then error("truncated double", 0) end ffi.copy(F64.b, buf:sub(pos, pos + 7), 8) + -- Detect Inf/NaN from the raw bit pattern before going through + -- tonumber(). LuaJIT 2.1 NaN-boxes Lua values, so some IEEE NaN + -- payloads collide with internal type tags (nil/function/etc.) and + -- `tonumber(F64.d)` yields a non-number. Reading via F64.u (uint64) + -- keeps us in the integer domain until we decide what to return. + local u = F64.u + if bit.band(bit.rshift(u, 52), F64_EXP_MASK) == F64_EXP_MASK then + if bit.band(u, F64_FRAC_MASK) == UINT64_ZERO then + if bit.band(u, F64_SIGN_BIT) ~= UINT64_ZERO then + return -math.huge, pos + 8 + end + return math.huge, pos + 8 + end + return 0/0, pos + 8 + end return tonumber(F64.d), pos + 8 end M.decode_double = decode_double