Read Write Format

lua-users home
wiki

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.

function read_format(little_endian, format, str)

  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 1:02 pm GMT (diff)