rewrite(phase4): f16 support

This commit is contained in:
khtsly 2026-02-13 23:00:43 +07:00
parent 81d3c540c1
commit 4d983a0756

View file

@ -10,6 +10,9 @@ export type Writer = {
local DEFAULT_CAPACITY: number = 64
local F16_SUBNORMAL_MULT = 2 ^ (-14)
local F16_SUBNORMAL_SCALE = 2 ^ 24
-- 0x00-0x7F: + fixint (0-127) - single byte
-- 0x80-0x9F: - fixint (-32 to -1) - single byte
local T_NIL = 0xA0
@ -21,6 +24,7 @@ local T_U32 = 0xA5
local T_I8 = 0xA6
local T_I16 = 0xA7
local T_I32 = 0xA8
local T_F16 = 0xAB
local T_F32 = 0xA9
local T_F64 = 0xAA
@ -48,7 +52,7 @@ local T_BUFFER = 0xDC
local T_VEC3 = 0xE0 -- f32 precision (12 bytes)
local T_VEC2 = 0xE2 -- f32 precision (8 bytes)
local T_CFRAME = 0xE4 -- f32 pos + f32 orient (24 bytes)
local T_CFRAME = 0xE4 -- f32 pos (12 bytes) + f16 orient (6 bytes) = 18 bytes total
local T_COLOR3 = 0xE6 -- RGB bytes (3 bytes)
local T_COLOR3_F = 0xE7 -- RGB floats (12 bytes)
local T_BRICKCOLOR = 0xE8
@ -64,32 +68,31 @@ local T_COLSEQ = 0xF2 -- ColorSequence
local T_NUMSEQ = 0xF3 -- NumberSequence
local T_BOOL_ARR = 0xDD
local TYPED_READERS = {
[1] = function(b, o)
return buffer.readu8(b, o), o + 1
end,
[2] = function(b, o)
return buffer.readi8(b, o), o + 1
end,
[3] = function(b, o)
return buffer.readu16(b, o), o + 2
end,
[4] = function(b, o)
return buffer.readi16(b, o), o + 2
end,
[5] = function(b, o)
return buffer.readu32(b, o), o + 4
end,
[6] = function(b, o)
return buffer.readi32(b, o), o + 4
end,
[7] = function(b, o)
return buffer.readf32(b, o), o + 4
end,
[8] = function(b, o)
return buffer.readf64(b, o), o + 8
end,
}
local F16_LOOKUP = table.create(65536)
do -- precomputes for readf16
for raw = 0, 65535 do
local sign = bit32.btest(raw, 0x8000) and -1 or 1
local exponent = bit32.extract(raw, 10, 5)
local mantissa = bit32.band(raw, 0x03FF)
local value
if exponent == 0 then
if mantissa == 0 then
value = 0 * sign
else
value = sign * (mantissa / 1024) * 6.103515625e-05
end
elseif exponent == 31 then
if mantissa == 0 then
value = sign * math.huge
else
value = 0/0 -- nan
end
else
value = sign * (1 + mantissa / 1024) * math.ldexp(1, exponent - 15)
end
F16_LOOKUP[raw + 1] = value
end
end
local function varUIntSize(value: number): number
if value < 0x80 then
@ -186,6 +189,54 @@ local function wI32(w: Writer, v: number)
w.cursor += 4
end
local function wF16(w: Writer, value: number)
ensureSpace(w, 2)
local raw: number
if value ~= value then
raw = 0x7E00 -- NaN
elseif value == 0 then
raw = if 1/value < 0 then 0x8000 else 0
else
local sign = 0
if value < 0 then
sign = 0x8000
value = -value
end
if value >= 65504 then
raw = sign + 0x7C00 -- Infinity
elseif value < 6.103515625e-05 then
-- Subnormal range
local f = math.floor(value * 16777216 + 0.5)
if f >= 1024 then
raw = sign + 0x0400 -- Clamp to smallest normal
else
raw = sign + f
end
else
local m, e = math.frexp(value)
local biasedExp = e + 14
local f = math.floor(m * 2048 - 1024 + 0.5)
if f >= 1024 then
f = 0
biasedExp += 1
end
-- Add overflow protection (was missing!)
if biasedExp >= 31 then
raw = sign + 0x7C00 -- Overflow to infinity
else
raw = sign + biasedExp * 1024 + f
end
end
end
buffer.writeu16(w.buf, w.cursor, raw)
w.cursor += 2
end
local function wF32(w: Writer, v: number)
ensureSpace(w, 4)
buffer.writef32(w.buf, w.cursor, v)
@ -198,6 +249,11 @@ local function wF64(w: Writer, v: number)
w.cursor += 8
end
local function readF16(b: buffer, pos: number): number
local raw = buffer.readu16(b, pos)
return F16_LOOKUP[raw + 1]
end
local function wVarUInt(w: Writer, v: number)
ensureSpace(w, varUIntSize(v))
w.cursor = writeVarUInt(w.buf, w.cursor, v)
@ -212,6 +268,35 @@ end
local packValue: (w: Writer, v: any) -> ()
local F32_TEST_BUF = buffer.create(4)
local TYPED_READERS = {
[1] = function(b, o)
return buffer.readu8(b, o), o + 1
end,
[2] = function(b, o)
return buffer.readi8(b, o), o + 1
end,
[3] = function(b, o)
return buffer.readu16(b, o), o + 2
end,
[4] = function(b, o)
return buffer.readi16(b, o), o + 2
end,
[5] = function(b, o)
return buffer.readu32(b, o), o + 4
end,
[6] = function(b, o)
return buffer.readi32(b, o), o + 4
end,
[7] = function(b, o)
return buffer.readf32(b, o), o + 4
end,
[8] = function(b, o)
return buffer.readf64(b, o), o + 8
end,
[9] = function(b, o)
return readF16(b, o), o + 2
end,
}
local function packNumber(w: Writer, n: number)
if n ~= n then
@ -361,8 +446,8 @@ local function analyzeArray(t: { any }, count: number): (string?, string?, numbe
return "number", "f64", count
end
local TYPED_CODES = { u8 = 1, i8 = 2, u16 = 3, i16 = 4, u32 = 5, i32 = 6, f32 = 7, f64 = 8 }
local TYPED_SIZES = { u8 = 1, i8 = 1, u16 = 2, i16 = 2, u32 = 4, i32 = 4, f32 = 4, f64 = 8 }
local TYPED_CODES = { u8 = 1, i8 = 2, u16 = 3, i16 = 4, u32 = 5, i32 = 6, f32 = 7, f64 = 8, f16 = 9 }
local TYPED_SIZES = { u8 = 1, i8 = 1, u16 = 2, i16 = 2, u32 = 4, i32 = 4, f32 = 4, f64 = 8, f16 = 2 }
local TYPED_WRITERS = {
u8 = buffer.writeu8,
i8 = buffer.writei8,
@ -372,6 +457,49 @@ local TYPED_WRITERS = {
i32 = buffer.writei32,
f32 = buffer.writef32,
f64 = buffer.writef64,
f16 = function(b: buffer, offset: number, value: number)
local raw: number
if value ~= value then
raw = 0x7E00
elseif value == 0 then
raw = if 1/value < 0 then 0x8000 else 0
else
local sign = 0
if value < 0 then
sign = 0x8000
value = -value
end
if value >= 65504 then
raw = sign + 0x7C00
elseif value < 6.103515625e-05 then
local f = math.floor(value * 16777216 + 0.5)
if f >= 1024 then
raw = sign + 0x0400
else
raw = sign + f
end
else
local m, e = math.frexp(value)
local biasedExp = e + 14
local f = math.floor(m * 2048 - 1024 + 0.5)
if f >= 1024 then
f = 0
biasedExp += 1
end
if biasedExp >= 31 then
raw = sign + 0x7C00
else
raw = sign + biasedExp * 1024 + f
end
end
end
buffer.writeu16(b, offset, raw)
end,
}
local function packTable(w: Writer, t: { [any]: any })
@ -479,15 +607,15 @@ end
local function packVector3(w: Writer, v: Vector3)
wByte(w, T_VEC3)
wF32(w, v.X)
wF32(w, v.Y)
wF32(w, v.Z)
wF16(w, v.X)
wF16(w, v.Y)
wF16(w, v.Z)
end
local function packVector2(w: Writer, v: Vector2)
wByte(w, T_VEC2)
wF32(w, v.X)
wF32(w, v.Y)
wF16(w, v.X)
wF16(w, v.Y)
end
local function packCFrame(w: Writer, cf: CFrame)
@ -498,9 +626,9 @@ local function packCFrame(w: Writer, cf: CFrame)
wF32(w, pos.X)
wF32(w, pos.Y)
wF32(w, pos.Z)
wF32(w, rx)
wF32(w, ry)
wF32(w, rz)
wF16(w, rx)
wF16(w, ry)
wF16(w, rz)
end
local function packColor3(w: Writer, c: Color3)
@ -665,6 +793,9 @@ unpackValue = function(buf: buffer, pos: number, refs: { any }?): (any, number)
if t == T_I32 then
return buffer.readi32(buf, pos), pos + 4
end
if t == T_F16 then
return readF16(buf, pos), pos + 2
end
if t == T_F32 then
return buffer.readf32(buf, pos), pos + 4
end
@ -814,17 +945,17 @@ unpackValue = function(buf: buffer, pos: number, refs: { any }?): (any, number)
-- Vector3
if t == T_VEC3 then
local x = buffer.readf32(buf, pos)
local y = buffer.readf32(buf, pos + 4)
local z = buffer.readf32(buf, pos + 8)
return Vector3.new(x, y, z), pos + 12
local x = readF16(buf, pos)
local y = readF16(buf, pos + 2)
local z = readF16(buf, pos + 4)
return Vector3.new(x, y, z), pos + 6
end
-- Vector2
if t == T_VEC2 then
local x = buffer.readf32(buf, pos)
local y = buffer.readf32(buf, pos + 4)
return Vector2.new(x, y), pos + 8
local x = readF16(buf, pos)
local y = readF16(buf, pos + 2)
return Vector2.new(x, y), pos + 4
end
-- CFrame
@ -832,10 +963,10 @@ unpackValue = function(buf: buffer, pos: number, refs: { any }?): (any, number)
local px = buffer.readf32(buf, pos)
local py = buffer.readf32(buf, pos + 4)
local pz = buffer.readf32(buf, pos + 8)
local rx = buffer.readf32(buf, pos + 12)
local ry = buffer.readf32(buf, pos + 16)
local rz = buffer.readf32(buf, pos + 20)
return CFrame.new(px, py, pz) * CFrame.fromOrientation(rx, ry, rz), pos + 24
local rx = readF16(buf, pos + 12)
local ry = readF16(buf, pos + 14)
local rz = readF16(buf, pos + 16)
return CFrame.new(px, py, pz) * CFrame.fromOrientation(rx, ry, rz), pos + 18
end
-- Color3
@ -978,6 +1109,7 @@ Schema.u16 = { type = "u16" }
Schema.i16 = { type = "i16" }
Schema.u32 = { type = "u32" }
Schema.i32 = { type = "i32" }
Schema.f16 = { type = "f16" }
Schema.f32 = { type = "f32" }
Schema.f64 = { type = "f64" }
Schema.boolean = { type = "boolean" }
@ -1035,6 +1167,9 @@ local function compilePacker(s: SchemaType): (Writer, any) -> ()
if schema_type == "i32" then
return wI32
end
if schema_type == "f16" then
return wF16
end
if schema_type == "f32" then
return wF32
end
@ -1056,15 +1191,15 @@ local function compilePacker(s: SchemaType): (Writer, any) -> ()
if schema_type == "vector3" then
return function(w, v)
wF32(w, v.X)
wF32(w, v.Y)
wF32(w, v.Z)
wF16(w, v.X)
wF16(w, v.Y)
wF16(w, v.Z)
end
end
if schema_type == "vector2" then
return function(w, v)
wF32(w, v.X)
wF32(w, v.Y)
wF16(w, v.X)
wF16(w, v.Y)
end
end
@ -1075,9 +1210,9 @@ local function compilePacker(s: SchemaType): (Writer, any) -> ()
wF32(w, pos.X)
wF32(w, pos.Y)
wF32(w, pos.Z)
wF32(w, rx)
wF32(w, ry)
wF32(w, rz)
wF16(w, rx)
wF16(w, ry)
wF16(w, rz)
end
end
@ -1280,6 +1415,11 @@ local function compileReader(s: SchemaType): (buffer, number, { any }?) -> (any,
return buffer.readi32(b, c), c + 4
end
end
if schema_type == "f16" then
return function(b, c)
return readF16(b, c), c + 2
end
end
if schema_type == "f32" then
return function(b, c)
return buffer.readf32(b, c), c + 4
@ -1304,18 +1444,18 @@ local function compileReader(s: SchemaType): (buffer, number, { any }?) -> (any,
end
if schema_type == "vector3" then
return function(b, c)
local x = buffer.readf32(b, c)
local y = buffer.readf32(b, c + 4)
local z = buffer.readf32(b, c + 8)
return Vector3.new(x, y, z), c + 12
local x = readF16(b, c)
local y = readF16(b, c + 2)
local z = readF16(b, c + 4)
return Vector3.new(x, y, z), c + 6
end
end
if schema_type == "vector2" then
return function(b, c)
local x = buffer.readf32(b, c)
local y = buffer.readf32(b, c + 4)
return Vector2.new(x, y), c + 8
local x = readF16(b, c)
local y = readF16(b, c + 2)
return Vector2.new(x, y), c + 4
end
end
@ -1333,10 +1473,10 @@ local function compileReader(s: SchemaType): (buffer, number, { any }?) -> (any,
local px = buffer.readf32(b, c)
local py = buffer.readf32(b, c + 4)
local pz = buffer.readf32(b, c + 8)
local rx = buffer.readf32(b, c + 12)
local ry = buffer.readf32(b, c + 16)
local rz = buffer.readf32(b, c + 20)
return CFrame.new(px, py, pz) * CFrame.fromOrientation(rx, ry, rz), c + 24
local rx = readF16(b, c + 12)
local ry = readF16(b, c + 14)
local rz = readF16(b, c + 16)
return CFrame.new(px, py, pz) * CFrame.fromOrientation(rx, ry, rz), c + 18
end
end
if schema_type == "instance" then