## Lists of Integers

It is often necessary to pack a list of integers into a binary string of data, for example for reading/writing binary files or to implement communication protocols. There are plenty of ways to do this. This implementation has only the pretension to be relatively easy to use.

This function fails for negative singed values i.e. -86000 !

```-- This format function packs a list of integers into a binary string.
-- The sizes of the integers can be specified, both little and big endian
-- ordering are supported. The format parameter is a string composed of
-- ASCII digit numbers, the size in bytes of the corresponding value.
-- Example:
--   write_format(true, "421", 0x12345678, 0x432931, 0x61) returns "xV41)a",
--     a 7 bytes string whose characters are in hex: 78 56 34 12 31 29 61
function write_format(little_endian, format, ...)
local res = ''
local values = {...}
for i=1,#format do
local size = tonumber(format:sub(i,i))
local value = values[i]
local str = ""
for j=1,size do
str = str .. string.char(value % 256)
value = math.floor(value / 256)
end
if not little_endian then
str = string.reverse(str)
end
res = res .. str
end
return res
end

-- This format function does the inverse of write_format. It unpacks a binary
-- string into a list of integers of specified size, supporting big and little
-- endian ordering. Example:
--   read_format(true, "421", "xV4.1)a") returns 0x12345678, 0x2931 and 0x61.
local idx = 0
local res = {}
for i=1,#format do
local size = tonumber(format:sub(i,i))
local val = str:sub(idx+1,idx+size)
local value = 0
idx = idx + size
if little_endian then
val = string.reverse(val)
end
for j=1,size do
value = value * 256 + val:byte(j)
end
res[i] = value
end
return unpack(res)
end
```

## More Integers

This functions fail for negative singed values i.e. -86000 !

Another way to read and write integers, written by Tom N Harris.

```-- Read an integer in LSB order.
function stringtonumber(str)
local function _b2n(exp, num, digit, ...)
if not digit then return num end
return _b2n(exp*256, num + digit*exp, ...)
end
return _b2n(256, string.byte(str, 1, -1))
end

-- Read an integer in MSB order.
function stringtonumber(str)
local function _b2n(num, digit, ...)
if not digit then return num end
return _b2n(num*256 + digit, ...)
end
return _b2n(0, string.byte(str, 1, -1))
end

-- Write an integer in LSB order using width bytes.
function numbertobytes(num, width)
local function _n2b(width, num, rem)
rem = rem * 256
if width == 0 then return rem end
return rem, _n2b(width-1, math.modf(num/256))
end
return string.char(_n2b(width-1, math.modf(num/256)))
end

-- Write an integer in MSB order using width bytes.
function numbertobytes(num, width)
local function _n2b(t, width, num, rem)
if width == 0 then return table.concat(t) end
table.insert(t, 1, string.char(rem * 256))
return _n2b(t, width-1, math.modf(num/256))
end
return _n2b({}, width, math.modf(num/256))
end
```

## Bitfields

A function that unpacks the bits in a number to booleans.
```function unpackbits(bits, width)
local fl = {}
local num,rem = flags
for i = 1,width do
num,rem = math.modf(num/2)
fl[#fl+1] = rem>=0.5
end
return unpack(fl)
end
```

## Floating-Point

Convert an IEEE-754 float in MSB order.
```function convertfloat(str)
-- Change to b4,b3,b2,b1 to unpack an LSB float
local b1,b2,b3,b4 = string.byte(str, 1, 4)
local exponent = (b1 % 128) * 2 + math.floor(b2 / 128)
if exponent == 0 then return 0 end
local sign = (b1 > 127) and -1 or 1
local mantissa = ((b2 % 128) * 256 + b3) * 256 + b4
mantissa = (math.ldexp(mantissa, -23) + 1) * sign
return math.ldexp(mantissa, exponent - 127)
end
```

## Libraries

There are C libraries for packing and unpacking binary data. They can handle integers of sizes from 8 to 32 bits, signed or unsigned, IEEE-754 floating point, plus strings in several forms, etc. See StructurePacking.

Original citation, Sirmabus: September 13, 2009

RecentChanges · preferences
edit · history
Last edited September 18, 2013 2:02 pm GMT (diff)