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