Skip to content

Commit 65caf6c

Browse files
committed
Optimize binary conversion functions
1 parent 1353a53 commit 65caf6c

File tree

3 files changed

+22
-63
lines changed

3 files changed

+22
-63
lines changed

bint.lua

Lines changed: 19 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ local math_min = math.min
116116
local string_format = string.format
117117
local table_insert = table.insert
118118
local table_concat = table.concat
119+
local table_unpack = table.unpack
119120

120121
local memo = {}
121122

@@ -142,8 +143,8 @@ end
142143
assert(bits % wordbits == 0, 'bitsize is not multiple of word bitsize')
143144
assert(2*wordbits <= intbits, 'word bitsize must be half of the lua integer bitsize')
144145
assert(bits >= 64, 'bitsize must be >= 64')
146+
assert(wordbits >= 8, 'wordbits must be at least 8')
145147
assert(bits % 8 == 0, 'bitsize must be multiple of 8')
146-
local bytes = bits // 8
147148

148149
-- Create bint module
149150
local bint = {}
@@ -154,10 +155,12 @@ bint.bits = bits
154155

155156
-- Constants used internally
156157
local BINT_BITS = bits
158+
local BINT_BYTES = bits // 8
157159
local BINT_WORDBITS = wordbits
158160
local BINT_SIZE = BINT_BITS // BINT_WORDBITS
159161
local BINT_WORDMAX = (1 << BINT_WORDBITS) - 1
160162
local BINT_WORDMSB = (1 << (BINT_WORDBITS - 1))
163+
local BINT_LEPACKFMT = '<'..('I'..(wordbits // 8)):rep(BINT_SIZE)
161164
local BINT_MATHMININTEGER, BINT_MATHMAXINTEGER
162165
local BINT_MININTEGER
163166

@@ -346,62 +349,32 @@ function bint.fromstring(s)
346349
end
347350
local bint_fromstring = bint.fromstring
348351

349-
local function getendianstep()
350-
if intbits >= 64 and bytes % 8 == 0 then
351-
return 8
352-
elseif bytes % 4 == 0 then
353-
return 4
354-
elseif bytes % 2 == 0 then
355-
return 2
356-
else
357-
return 1
358-
end
359-
end
360-
361352
--- Create a new bint from a buffer of little-endian bytes.
362-
-- @param buffer Buffer of bytes, extra bytes are trimmed from the right, missing bytes are padded to the left.
353+
-- @param buffer Buffer of bytes, extra bytes are trimmed from the right, missing bytes are padded to the right.
363354
-- @raise An assert is thrown in case buffer is not an string.
364355
-- @return A bint.
365356
function bint.fromle(buffer)
366357
assert(type(buffer) == 'string', 'buffer is not a string')
367-
if #buffer > bytes then -- trim extra bytes from the right
368-
buffer = buffer:sub(1, bytes)
369-
elseif #buffer < bytes then -- add missing bytes to the right
370-
buffer = buffer..string.rep('\x00', bytes - #buffer)
358+
if #buffer > BINT_BYTES then -- trim extra bytes from the right
359+
buffer = buffer:sub(1, BINT_BYTES)
360+
elseif #buffer < BINT_BYTES then -- add missing bytes to the right
361+
buffer = buffer..('\x00'):rep(BINT_BYTES - #buffer)
371362
end
372-
local step = getendianstep()
373-
local v = bint.zero()
374-
local nmax = bytes//step-1
375-
for n=0,nmax do
376-
local i = 1+n*step
377-
local d = bint_fromuinteger(('<I'..step):unpack(buffer:sub(i, i+step)))
378-
d = d << (n*step*8)
379-
v = v + d
380-
end
381-
return v
363+
return setmetatable({BINT_LEPACKFMT:unpack(buffer)}, bint)
382364
end
383365

384366
--- Create a new bint from a buffer of big-endian bytes.
385-
-- @param buffer Buffer of bytes, extra bytes are trimmed from the left, missing bytes are padded to the right.
367+
-- @param buffer Buffer of bytes, extra bytes are trimmed from the left, missing bytes are padded to the left.
386368
-- @raise An assert is thrown in case buffer is not an string.
387369
-- @return A bint.
388370
function bint.frombe(buffer)
389371
assert(type(buffer) == 'string', 'buffer is not a string')
390-
if #buffer > bytes then -- trim extra bytes from the left
391-
buffer = buffer:sub(-bytes, #buffer)
392-
elseif #buffer < bytes then -- add missing bytes to the left
393-
buffer = string.rep('\x00', bytes - #buffer)..buffer
394-
end
395-
local step = getendianstep()
396-
local v = bint.zero()
397-
local nmax = bytes//step-1
398-
for n=0,nmax do
399-
local i = 1+(nmax-n)*step
400-
local d = bint_fromuinteger(('>I'..step):unpack(buffer:sub(i, i+step)))
401-
d = d << (n*step*8)
402-
v = v + d
372+
if #buffer > BINT_BYTES then -- trim extra bytes from the left
373+
buffer = buffer:sub(-BINT_BYTES, #buffer)
374+
elseif #buffer < BINT_BYTES then -- add missing bytes to the left
375+
buffer = ('\x00'):rep(BINT_BYTES - #buffer)..buffer
403376
end
404-
return v
377+
return setmetatable({BINT_LEPACKFMT:unpack(buffer:reverse())}, bint)
405378
end
406379

407380
--- Create a new bint from a value.
@@ -646,13 +619,7 @@ end
646619
-- @raise Asserts in case input is not convertible to an integer.
647620
function bint.tole(x, trim)
648621
x = bint_assert_convert(x)
649-
local s = x:tobase(16, true)
650-
if #s < 2*bytes then
651-
s = string.rep('0', 2*bytes - #s)..s
652-
end
653-
s = s:gsub('..', function(c)
654-
return string.char(tonumber(c, 16))
655-
end):reverse()
622+
local s = BINT_LEPACKFMT:pack(table_unpack(x))
656623
if trim then
657624
s = s:gsub('\x00+$', '')
658625
if s == '' then
@@ -669,13 +636,7 @@ end
669636
-- @raise Asserts in case input is not convertible to an integer.
670637
function bint.tobe(x, trim)
671638
x = bint_assert_convert(x)
672-
local s = x:tobase(16, true)
673-
if #s < 2*bytes then
674-
s = string.rep('0', 2*bytes - #s)..s
675-
end
676-
s = s:gsub('..', function(c)
677-
return string.char(tonumber(c, 16))
678-
end)
639+
local s = BINT_LEPACKFMT:pack(table_unpack(x)):reverse()
679640
if trim then
680641
s = s:gsub('^\x00+', '')
681642
if s == '' then
@@ -895,7 +856,7 @@ function bint:_dec()
895856
local tmp = self[i]
896857
local v = (tmp - 1) & BINT_WORDMAX
897858
self[i] = v
898-
if not (v > tmp) then
859+
if v <= tmp then
899860
break
900861
end
901862
end

docs/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,7 @@ <h3>See also:</h3>
695695
<h3>Parameters:</h3>
696696
<ul>
697697
<li><span class="parameter">buffer</span>
698-
Buffer of bytes, extra bytes are trimmed from the right, missing bytes are padded to the left.
698+
Buffer of bytes, extra bytes are trimmed from the right, missing bytes are padded to the right.
699699
</li>
700700
</ul>
701701

@@ -722,7 +722,7 @@ <h3>Raises:</h3>
722722
<h3>Parameters:</h3>
723723
<ul>
724724
<li><span class="parameter">buffer</span>
725-
Buffer of bytes, extra bytes are trimmed from the left, missing bytes are padded to the right.
725+
Buffer of bytes, extra bytes are trimmed from the left, missing bytes are padded to the left.
726726
</li>
727727
</ul>
728728

@@ -2653,7 +2653,7 @@ <h2 class="section-header "><a name="Fields"></a>Fields</h2>
26532653
</div> <!-- id="main" -->
26542654
<div id="about">
26552655
<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
2656-
<i style="float:right;">Last updated 2023-06-04 19:40:29 </i>
2656+
<i style="float:right;">Last updated 2023-06-26 08:51:09 </i>
26572657
</div> <!-- id="about" -->
26582658
</div> <!-- id="container" -->
26592659
</body>

tests.lua

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -735,8 +735,6 @@ test(64)
735735
test(64, 16)
736736
test(64, 8)
737737
test(72, 8)
738-
test(64, 4)
739-
test(80, 4)
740738
test(128)
741739
test(160)
742740
test(256)

0 commit comments

Comments
 (0)