Skip to content

Commit 3457b5b

Browse files
committed
Upgrade bitn to v0.3.0 and use its utility methods for Int64 operations
1 parent 6f760d0 commit 3457b5b

2 files changed

Lines changed: 160 additions & 10 deletions

File tree

src/protobuf/init.lua

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,39 +152,38 @@ end
152152
--- @param value Int64HighLow The {high_32, low_32} pair.
153153
--- @return string hex The 16-character hexadecimal string (e.g., "0000180000001000").
154154
function Protobuf.int64_to_hex(value)
155-
return string.format("%08X%08X", value[1], value[2])
155+
return bit64.to_hex(value)
156156
end
157157

158158
--- Converts a {high, low} pair to a Lua number.
159159
--- Warning: Values exceeding 53-bit precision will lose precision.
160160
--- @param value Int64HighLow The {high_32, low_32} pair.
161-
--- @return number num The value as a Lua number.
162-
function Protobuf.int64_to_number(value)
163-
return value[1] * 0x100000000 + value[2]
161+
--- @param strict? boolean If true, errors when value exceeds 53-bit precision.
162+
--- @return number result The value as a Lua number (may lose precision for large values unless strict).
163+
function Protobuf.int64_to_number(value, strict)
164+
return bit64.to_number(value, strict)
164165
end
165166

166167
--- Creates a {high, low} pair from a Lua number.
167168
--- @param value number The number to convert.
168169
--- @return Int64HighLow pair The {high_32, low_32} pair.
169170
function Protobuf.int64_from_number(value)
170-
local low = value % 0x100000000
171-
local high = math.floor(value / 0x100000000)
172-
return bit64.new(high, low)
171+
return bit64.from_number(value)
173172
end
174173

175174
--- Checks if two {high, low} pairs are equal.
176175
--- @param a Int64HighLow The first {high_32, low_32} pair.
177176
--- @param b Int64HighLow The second {high_32, low_32} pair.
178177
--- @return boolean equal True if the values are equal.
179178
function Protobuf.int64_equals(a, b)
180-
return a[1] == b[1] and a[2] == b[2]
179+
return bit64.eq(a, b)
181180
end
182181

183182
--- Checks if a {high, low} pair is zero.
184183
--- @param value Int64HighLow The {high_32, low_32} pair.
185184
--- @return boolean is_zero True if the value is zero.
186185
function Protobuf.int64_is_zero(value)
187-
return value[1] == 0 and value[2] == 0
186+
return bit64.is_zero(value)
188187
end
189188

190189
--- Encodes a 32-bit integer into a fixed-length 4-byte sequence.

vendor/bitn.lua

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,55 @@ function bit64.le_bytes_to_u64(str, offset)
12251225
return bit64.new(high, low)
12261226
end
12271227

1228+
--------------------------------------------------------------------------------
1229+
-- Utility functions
1230+
--------------------------------------------------------------------------------
1231+
1232+
--- Converts a {high, low} pair to a 16-character hexadecimal string.
1233+
--- @param value Int64HighLow The {high_32, low_32} pair.
1234+
--- @return string hex The hexadecimal string (e.g., "0000180000001000").
1235+
function bit64.to_hex(value)
1236+
return string.format("%08X%08X", value[1], value[2])
1237+
end
1238+
1239+
--- Converts a {high, low} pair to a Lua number.
1240+
--- Warning: Lua numbers use 64-bit IEEE 754 doubles with 53-bit mantissa precision.
1241+
--- Values exceeding 53 bits (greater than 9007199254740991) will lose precision.
1242+
--- To maintain full 64-bit precision, keep values in {high, low} format.
1243+
--- @param value Int64HighLow The {high_32, low_32} pair.
1244+
--- @param strict? boolean If true, errors when value exceeds 53-bit precision.
1245+
--- @return number result The value as a Lua number (may lose precision for large values unless strict).
1246+
function bit64.to_number(value, strict)
1247+
if strict and value[1] > 0x001FFFFF then
1248+
error("Value exceeds 53-bit precision (max: 9007199254740991)", 2)
1249+
end
1250+
return value[1] * 0x100000000 + value[2]
1251+
end
1252+
1253+
--- Creates a {high, low} pair from a Lua number.
1254+
--- @param value number The number to convert.
1255+
--- @return Int64HighLow pair The {high_32, low_32} pair.
1256+
function bit64.from_number(value)
1257+
local low = value % 0x100000000
1258+
local high = math.floor(value / 0x100000000)
1259+
return bit64.new(high, low)
1260+
end
1261+
1262+
--- Checks if two {high, low} pairs are equal.
1263+
--- @param a Int64HighLow The first {high_32, low_32} pair.
1264+
--- @param b Int64HighLow The second {high_32, low_32} pair.
1265+
--- @return boolean equal True if the values are equal.
1266+
function bit64.eq(a, b)
1267+
return a[1] == b[1] and a[2] == b[2]
1268+
end
1269+
1270+
--- Checks if a {high, low} pair is zero.
1271+
--- @param value Int64HighLow The {high_32, low_32} pair.
1272+
--- @return boolean is_zero True if the value is zero.
1273+
function bit64.is_zero(value)
1274+
return value[1] == 0 and value[2] == 0
1275+
end
1276+
12281277
--------------------------------------------------------------------------------
12291278
-- Aliases for compatibility
12301279
--------------------------------------------------------------------------------
@@ -1531,6 +1580,90 @@ function bit64.selftest()
15311580
inputs = { string.char(0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12) },
15321581
expected = { 0x12345678, 0x9ABCDEF0 },
15331582
},
1583+
1584+
-- to_hex tests
1585+
{
1586+
name = "to_hex({0x00001800, 0x00001000})",
1587+
fn = bit64.to_hex,
1588+
inputs = { { 0x00001800, 0x00001000 } },
1589+
expected = "0000180000001000",
1590+
},
1591+
{
1592+
name = "to_hex({0xFFFFFFFF, 0xFFFFFFFF})",
1593+
fn = bit64.to_hex,
1594+
inputs = { { 0xFFFFFFFF, 0xFFFFFFFF } },
1595+
expected = "FFFFFFFFFFFFFFFF",
1596+
},
1597+
{
1598+
name = "to_hex({0x00000000, 0x00000000})",
1599+
fn = bit64.to_hex,
1600+
inputs = { { 0x00000000, 0x00000000 } },
1601+
expected = "0000000000000000",
1602+
},
1603+
1604+
-- to_number tests
1605+
{
1606+
name = "to_number({0x00000000, 0x00000001})",
1607+
fn = bit64.to_number,
1608+
inputs = { { 0x00000000, 0x00000001 } },
1609+
expected = 1,
1610+
},
1611+
{
1612+
name = "to_number({0x00000000, 0xFFFFFFFF})",
1613+
fn = bit64.to_number,
1614+
inputs = { { 0x00000000, 0xFFFFFFFF } },
1615+
expected = 4294967295,
1616+
},
1617+
{
1618+
name = "to_number({0x00000001, 0x00000000})",
1619+
fn = bit64.to_number,
1620+
inputs = { { 0x00000001, 0x00000000 } },
1621+
expected = 4294967296,
1622+
},
1623+
1624+
-- from_number tests
1625+
{
1626+
name = "from_number(1)",
1627+
fn = bit64.from_number,
1628+
inputs = { 1 },
1629+
expected = { 0x00000000, 0x00000001 },
1630+
},
1631+
{
1632+
name = "from_number(4294967296)",
1633+
fn = bit64.from_number,
1634+
inputs = { 4294967296 },
1635+
expected = { 0x00000001, 0x00000000 },
1636+
},
1637+
{
1638+
name = "from_number(0)",
1639+
fn = bit64.from_number,
1640+
inputs = { 0 },
1641+
expected = { 0x00000000, 0x00000000 },
1642+
},
1643+
1644+
-- eq tests
1645+
{ name = "eq({1,2}, {1,2})", fn = bit64.eq, inputs = { { 1, 2 }, { 1, 2 } }, expected = true },
1646+
{ name = "eq({1,2}, {1,3})", fn = bit64.eq, inputs = { { 1, 2 }, { 1, 3 } }, expected = false },
1647+
{ name = "eq({1,2}, {2,2})", fn = bit64.eq, inputs = { { 1, 2 }, { 2, 2 } }, expected = false },
1648+
1649+
-- is_zero tests
1650+
{ name = "is_zero({0,0})", fn = bit64.is_zero, inputs = { { 0, 0 } }, expected = true },
1651+
{ name = "is_zero({0,1})", fn = bit64.is_zero, inputs = { { 0, 1 } }, expected = false },
1652+
{ name = "is_zero({1,0})", fn = bit64.is_zero, inputs = { { 1, 0 } }, expected = false },
1653+
1654+
-- to_number strict mode tests (values within 53-bit range)
1655+
{
1656+
name = "to_number({0x001FFFFF, 0xFFFFFFFF}, true) -- max 53-bit",
1657+
fn = bit64.to_number,
1658+
inputs = { { 0x001FFFFF, 0xFFFFFFFF }, true },
1659+
expected = 9007199254740991,
1660+
},
1661+
{
1662+
name = "to_number({0, 1}, true)",
1663+
fn = bit64.to_number,
1664+
inputs = { { 0, 1 }, true },
1665+
expected = 1,
1666+
},
15341667
}
15351668

15361669
for _, test in ipairs(test_vectors) do
@@ -1705,6 +1838,24 @@ function bit64.selftest()
17051838
end
17061839
end
17071840

1841+
-- Test to_number strict mode error case
1842+
print("\nRunning to_number strict mode tests...")
1843+
total = total + 1
1844+
local ok, err = pcall(function()
1845+
bit64.to_number({ 0x00200000, 0x00000000 }, true) -- 2^53, exceeds 53-bit
1846+
end)
1847+
if not ok and string.find(err, "53%-bit precision") then
1848+
print(" PASS: to_number strict mode errors on values > 53 bits")
1849+
passed = passed + 1
1850+
else
1851+
print(" FAIL: to_number strict mode errors on values > 53 bits")
1852+
if ok then
1853+
print(" Expected error but got success")
1854+
else
1855+
print(" Expected '53-bit precision' error but got: " .. tostring(err))
1856+
end
1857+
end
1858+
17081859
print(string.format("\n64-bit operations: %d/%d tests passed\n", passed, total))
17091860
return passed == total
17101861
end
@@ -1741,7 +1892,7 @@ local bitn = {
17411892
}
17421893

17431894
--- Library version (injected at build time for releases).
1744-
local VERSION = "v0.2.0"
1895+
local VERSION = "v0.3.0"
17451896

17461897
--- Get the library version string.
17471898
--- @return string version Version string (e.g., "v1.0.0" or "dev")

0 commit comments

Comments
 (0)