Skip to content

Commit 6f85485

Browse files
authored
Rename isInt64 to is_int64 and add input validation to to_number (#5)
- Rename bit64.isInt64 to bit64.is_int64 for Lua naming conventions (old name kept as alias for backwards compatibility) - bit64.to_number now validates input and accepts only numbers or proper Int64 values created with bit64.new() - bit64.from_number now accepts Int64 values as pass-through
1 parent 3ecbfa3 commit 6f85485

2 files changed

Lines changed: 64 additions & 40 deletions

File tree

src/bitn/bit16.lua

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ function bit16.arshift(a, n)
151151
-- If original was negative, fill high bits with 1s
152152
if is_negative then
153153
-- Create mask for high bits that need to be 1
154-
local fill_mask = MASK16 - (math.pow(2, 16 - n) - 1)
154+
local fill_mask = MASK16 - (math.floor(2 ^ (16 - n)) - 1)
155155
result = bit16.bor(result, fill_mask)
156156
end
157157

@@ -417,15 +417,20 @@ function bit16.selftest()
417417
else
418418
print(" FAIL: " .. test.name)
419419
if type(test.expected) == "string" then
420-
local exp_hex, got_hex = "", ""
421-
for i = 1, #test.expected do
422-
exp_hex = exp_hex .. string.format("%02X", string.byte(test.expected, i))
420+
if type(result) ~= "string" then
421+
print(" Expected: string")
422+
print(" Got: " .. type(result))
423+
else
424+
local exp_hex, got_hex = "", ""
425+
for i = 1, #test.expected do
426+
exp_hex = exp_hex .. string.format("%02X", string.byte(test.expected, i))
427+
end
428+
for i = 1, #result do
429+
got_hex = got_hex .. string.format("%02X", string.byte(result, i))
430+
end
431+
print(" Expected: " .. exp_hex)
432+
print(" Got: " .. got_hex)
423433
end
424-
for i = 1, #result do
425-
got_hex = got_hex .. string.format("%02X", string.byte(result, i))
426-
end
427-
print(" Expected: " .. exp_hex)
428-
print(" Got: " .. got_hex)
429434
else
430435
print(string.format(" Expected: 0x%04X", test.expected))
431436
print(string.format(" Got: 0x%04X", result))

src/bitn/bit64.lua

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ end
2929

3030
--- Check if a value is an Int64 (created by bit64 functions).
3131
--- @param value any Value to check
32-
--- @return boolean isInt64 True if value is an Int64
33-
function bit64.isInt64(value)
32+
--- @return boolean is_int64 True if value is an Int64
33+
function bit64.is_int64(value)
3434
return type(value) == "table" and getmetatable(value) == Int64Meta
3535
end
3636

@@ -292,17 +292,28 @@ end
292292
--- @param strict? boolean If true, errors when value exceeds 53-bit precision.
293293
--- @return number result The value as a Lua number (may lose precision for large values unless strict).
294294
function bit64.to_number(value, strict)
295+
if type(value) == "number" then
296+
return value
297+
end
298+
if not bit64.is_int64(value) then
299+
error("Value is not a valid Int64HighLow pair", 2)
300+
end
295301
if strict and value[1] > 0x001FFFFF then
296302
error("Value exceeds 53-bit precision (max: 9007199254740991)", 2)
297303
end
298304
return value[1] * 0x100000000 + value[2]
299305
end
300306

301307
--- Creates a {high, low} pair from a Lua number.
302-
--- @param value number The number to convert.
308+
--- @param value number|Int64HighLow The number to convert (or Int64HighLow to pass through).
303309
--- @return Int64HighLow pair The {high_32, low_32} pair.
304310
function bit64.from_number(value)
305-
local low = value % 0x100000000
311+
if bit64.is_int64(value) then
312+
--- @cast value Int64HighLow
313+
return value
314+
end
315+
--- @cast value -Int64HighLow
316+
local low = math.floor(value % 0x100000000)
306317
local high = math.floor(value / 0x100000000)
307318
return bit64.new(high, low)
308319
end
@@ -338,6 +349,9 @@ bit64.lsl = bit64.lshift
338349
--- Alias for arshift (compatibility with older API).
339350
bit64.asr = bit64.arshift
340351

352+
--- Alias for is_int64 (compatibility with older API).
353+
bit64.isInt64 = bit64.is_int64
354+
341355
--------------------------------------------------------------------------------
342356
-- Self-test
343357
--------------------------------------------------------------------------------
@@ -653,19 +667,19 @@ function bit64.selftest()
653667
{
654668
name = "to_number({0x00000000, 0x00000001})",
655669
fn = bit64.to_number,
656-
inputs = { { 0x00000000, 0x00000001 } },
670+
inputs = { bit64.new(0x00000000, 0x00000001) },
657671
expected = 1,
658672
},
659673
{
660674
name = "to_number({0x00000000, 0xFFFFFFFF})",
661675
fn = bit64.to_number,
662-
inputs = { { 0x00000000, 0xFFFFFFFF } },
676+
inputs = { bit64.new(0x00000000, 0xFFFFFFFF) },
663677
expected = 4294967295,
664678
},
665679
{
666680
name = "to_number({0x00000001, 0x00000000})",
667681
fn = bit64.to_number,
668-
inputs = { { 0x00000001, 0x00000000 } },
682+
inputs = { bit64.new(0x00000001, 0x00000000) },
669683
expected = 4294967296,
670684
},
671685

@@ -703,13 +717,13 @@ function bit64.selftest()
703717
{
704718
name = "to_number({0x001FFFFF, 0xFFFFFFFF}, true) -- max 53-bit",
705719
fn = bit64.to_number,
706-
inputs = { { 0x001FFFFF, 0xFFFFFFFF }, true },
720+
inputs = { bit64.new(0x001FFFFF, 0xFFFFFFFF), true },
707721
expected = 9007199254740991,
708722
},
709723
{
710724
name = "to_number({0, 1}, true)",
711725
fn = bit64.to_number,
712-
inputs = { { 0, 1 }, true },
726+
inputs = { bit64.new(0, 1), true },
713727
expected = 1,
714728
},
715729
}
@@ -734,16 +748,21 @@ function bit64.selftest()
734748
print(" PASS: " .. test.name)
735749
passed = passed + 1
736750
else
737-
local exp_hex, got_hex = "", ""
738-
for i = 1, #test.expected do
739-
exp_hex = exp_hex .. string.format("%02X", string.byte(test.expected, i))
740-
end
741-
for i = 1, #result do
742-
got_hex = got_hex .. string.format("%02X", string.byte(result, i))
743-
end
744751
print(" FAIL: " .. test.name)
745-
print(" Expected: " .. exp_hex)
746-
print(" Got: " .. got_hex)
752+
if type(result) ~= "string" then
753+
print(" Expected: string")
754+
print(" Got: " .. type(result))
755+
else
756+
local exp_hex, got_hex = "", ""
757+
for i = 1, #test.expected do
758+
exp_hex = exp_hex .. string.format("%02X", string.byte(test.expected, i))
759+
end
760+
for i = 1, #result do
761+
got_hex = got_hex .. string.format("%02X", string.byte(result, i))
762+
end
763+
print(" Expected: " .. exp_hex)
764+
print(" Got: " .. got_hex)
765+
end
747766
end
748767
else
749768
if result == test.expected then
@@ -763,7 +782,7 @@ function bit64.selftest()
763782
-- Test bit64.new() creates Int64 values
764783
total = total + 1
765784
local new_val = bit64.new(0x12345678, 0x9ABCDEF0)
766-
if bit64.isInt64(new_val) and new_val[1] == 0x12345678 and new_val[2] == 0x9ABCDEF0 then
785+
if bit64.is_int64(new_val) and new_val[1] == 0x12345678 and new_val[2] == 0x9ABCDEF0 then
767786
print(" PASS: new() creates Int64 with correct values")
768787
passed = passed + 1
769788
else
@@ -773,30 +792,30 @@ function bit64.selftest()
773792
-- Test bit64.new() with defaults
774793
total = total + 1
775794
local zero_val = bit64.new()
776-
if bit64.isInt64(zero_val) and zero_val[1] == 0 and zero_val[2] == 0 then
795+
if bit64.is_int64(zero_val) and zero_val[1] == 0 and zero_val[2] == 0 then
777796
print(" PASS: new() with no args creates {0, 0}")
778797
passed = passed + 1
779798
else
780799
print(" FAIL: new() with no args creates {0, 0}")
781800
end
782801

783-
-- Test isInt64() returns false for regular tables
802+
-- Test is_int64() returns false for regular tables
784803
total = total + 1
785804
local plain_table = { 0x12345678, 0x9ABCDEF0 }
786-
if not bit64.isInt64(plain_table) then
787-
print(" PASS: isInt64() returns false for plain table")
805+
if not bit64.is_int64(plain_table) then
806+
print(" PASS: is_int64() returns false for plain table")
788807
passed = passed + 1
789808
else
790-
print(" FAIL: isInt64() returns false for plain table")
809+
print(" FAIL: is_int64() returns false for plain table")
791810
end
792811

793-
-- Test isInt64() returns false for non-tables
812+
-- Test is_int64() returns false for non-tables
794813
total = total + 1
795-
if not bit64.isInt64(123) and not bit64.isInt64("string") and not bit64.isInt64(nil) then
796-
print(" PASS: isInt64() returns false for non-tables")
814+
if not bit64.is_int64(123) and not bit64.is_int64("string") and not bit64.is_int64(nil) then
815+
print(" PASS: is_int64() returns false for non-tables")
797816
passed = passed + 1
798817
else
799-
print(" FAIL: isInt64() returns false for non-tables")
818+
print(" FAIL: is_int64() returns false for non-tables")
800819
end
801820

802821
-- Test all operations return Int64 values
@@ -878,7 +897,7 @@ function bit64.selftest()
878897
for _, op in ipairs(ops_returning_int64) do
879898
total = total + 1
880899
local result = op.fn()
881-
if bit64.isInt64(result) then
900+
if bit64.is_int64(result) then
882901
print(" PASS: " .. op.name .. "() returns Int64")
883902
passed = passed + 1
884903
else
@@ -890,9 +909,9 @@ function bit64.selftest()
890909
print("\nRunning to_number strict mode tests...")
891910
total = total + 1
892911
local ok, err = pcall(function()
893-
bit64.to_number({ 0x00200000, 0x00000000 }, true) -- 2^53, exceeds 53-bit
912+
bit64.to_number(bit64.new(0x00200000, 0x00000000), true) -- 2^53, exceeds 53-bit
894913
end)
895-
if not ok and string.find(err, "53%-bit precision") then
914+
if not ok and type(err) == "string" and string.find(err, "53%-bit precision") then
896915
print(" PASS: to_number strict mode errors on values > 53 bits")
897916
passed = passed + 1
898917
else

0 commit comments

Comments
 (0)