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