@@ -116,6 +116,7 @@ local math_min = math.min
116116local string_format = string.format
117117local table_insert = table.insert
118118local table_concat = table.concat
119+ local table_unpack = table.unpack
119120
120121local memo = {}
121122
142143assert (bits % wordbits == 0 , ' bitsize is not multiple of word bitsize' )
143144assert (2 * wordbits <= intbits , ' word bitsize must be half of the lua integer bitsize' )
144145assert (bits >= 64 , ' bitsize must be >= 64' )
146+ assert (wordbits >= 8 , ' wordbits must be at least 8' )
145147assert (bits % 8 == 0 , ' bitsize must be multiple of 8' )
146- local bytes = bits // 8
147148
148149-- Create bint module
149150local bint = {}
@@ -154,10 +155,12 @@ bint.bits = bits
154155
155156-- Constants used internally
156157local BINT_BITS = bits
158+ local BINT_BYTES = bits // 8
157159local BINT_WORDBITS = wordbits
158160local BINT_SIZE = BINT_BITS // BINT_WORDBITS
159161local BINT_WORDMAX = (1 << BINT_WORDBITS ) - 1
160162local BINT_WORDMSB = (1 << (BINT_WORDBITS - 1 ))
163+ local BINT_LEPACKFMT = ' <' .. (' I' .. (wordbits // 8 )):rep (BINT_SIZE )
161164local BINT_MATHMININTEGER , BINT_MATHMAXINTEGER
162165local BINT_MININTEGER
163166
@@ -346,62 +349,32 @@ function bint.fromstring(s)
346349end
347350local 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.
365356function 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 )
382364end
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.
388370function 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 )
405378end
406379
407380--- Create a new bint from a value.
646619-- @raise Asserts in case input is not convertible to an integer.
647620function 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
669636-- @raise Asserts in case input is not convertible to an integer.
670637function 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
0 commit comments