Advertisement
Guest User

BinIO.lua

a guest
Feb 1st, 2013
247
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.90 KB | None | 0 0
  1. -- version 0.1.2
  2. BinIO = {}
  3. BinIO.File = nil
  4. -- fileNameOrHandle: string or file handle, file name to open or file handle to set.
  5. -- [mode]: string, open mode, always in binary mode, see io.open.
  6. -- return filehandle: file handle, return nil when operation fail
  7. function BinIO.Open(fileNameOrHandle, mode)
  8.     if( mode == nil ) then mode = "rb" end
  9.     local filehandle = nil
  10.     if( mode:sub(-1) ~= "b" ) then mode = mode.."b" end -- Always in binary mode
  11.     if( type(fileNameOrHandle) == "string" ) then -- File name
  12.         BinIO.File = io.open(fileNameOrHandle, mode)
  13.         filehandle = BinIO.File
  14.     elseif( io.type(fileNameOrHandle) == "file" ) then
  15.         io.input(fileNameOrHandle)
  16.         BinIO.File = fileNameOrHandle
  17.         filehandle = BinIO.File
  18.     end
  19.     return filehandle
  20. end
  21.  
  22. -- [fileHandle]: file handle, file handle to close.
  23. function BinIO.Close(fileHandle)
  24.     if( fileHandle == nil ) then fileHandle = BinIO.File end
  25.     if( io.type(fileHandle) == "file" ) then
  26.         io.close(fileHandle)
  27.     end
  28.     if( fileHandle == BinIO.File ) then BinIO.File = nil end -- In case of unwanted modify of closed file handle.
  29. end
  30.  
  31. -- val: object, value converting to binary string, can be string, number.
  32. -- [length]: number, how many bytes to convert, default 1. 0x00 was fiiled to empty bytes. Only last [length] bytes are used.
  33. -- return byteList : byte table, converted byte[] array.
  34. function BinIO.ToByteList(val, length)
  35.     local byteList = {}
  36.     local valType = type(val)
  37.     if( valType == "number" ) then
  38.         local BYTE_MASK  = 0xFF
  39.         local BYTE_SHIFT = 0x100
  40.         if( length == nil ) then length = 1 end
  41.         for i = 1, length do
  42.             byteList[length + 1 - i] = bit.band( val, BYTE_MASK )
  43.             val = math.floor( val / BYTE_SHIFT )
  44.         end
  45.     elseif( valType == "string" ) then
  46.         for i = 1, length do
  47.             local char = val:sub(i, i)
  48.             if( char ~= "" ) then
  49.                 byteList[i] = string.byte(char)
  50.             else
  51.                 byteList[i] = 0
  52.             end
  53.         end
  54.     end
  55.     return byteList
  56. end
  57.  
  58. -- val: object, value to write as binary. Can be signed number, bool(0 = false, other = true), string (encoded in ASCII only, sadly) or byte[] array. Number should be 0x0 ~ 0xFFFFFFFF(-2147483648~2147483647), or data loss will occurred in division, however number within range 0x0 ~ 0xFFFFFFFFFFFF seems works too. "wb" mode is required for string, if not, 0x0A will become 0x0D 0x0A, that'll ruin everything. If the val is a table, it must be a byte[] array, or it will be ignored.
  59. -- [length]: number, how many bytes to write, 0x00 was fiiled to empty bytes, default 1(for bool) or string.len(val)(for string) or proper length for number or #val(for bytes table). Only last [length] bytes are used for number, only the first [length] bytes are used for stirng and table.
  60. -- [fileHandle]: file handle, file to operate. Default is BinIO.File.
  61. -- return size : number, how many bytes are used theoretically, return 0 when no writing or failed to write.
  62. function BinIO.Write(val, length, fileHandle)
  63.     local size = 0
  64.     if( fileHandle == nil ) then fileHandle = BinIO.File end
  65.     if( val ~= nil and fileHandle ~= nil ) then
  66.         local isByteArray  = false
  67.         local writeAll     = false
  68.         local byteList     = {}
  69.         local byteStr      = ""
  70.         local valType      = type(val)
  71.         if( valType == "string" ) then -- String can be write directly
  72.             if( length == nil ) then length = val:len() end
  73.             byteStr = val:sub(1, length)
  74.             isByteArray  = false
  75.         elseif( valType == "number" ) then
  76.             local absVal = val
  77.             if( length == nil ) then
  78.                 if( val < 0 ) then absVal = -1 - val end
  79.                 length = math.floor( ( math.log(absVal) / math.log(2) + 1 ) / 8 ) + 1
  80.             end
  81.             byteList = BinIO.ToByteList(val, length)
  82.             isByteArray  = true
  83.         elseif( valType == "boolean" ) then
  84.             if( length == nil ) then length = 1 end
  85.             if( val ) then
  86.                 byteStr = string.char(1)
  87.             else
  88.                 byteStr = string.char(0)
  89.             end
  90.             if( length > 1) then
  91.                 byteStr = string.rep(string.char(0), length - 1)..byteStr
  92.             end
  93.             isByteArray  = false
  94.         elseif( valType == "table" ) then
  95.             isByteArray  = true
  96.             if( length == nil ) then
  97.                 writeAll = true
  98.                 length = 1 -- This should not be set, but I've no idea why (writeAll or (size < length)) will throw error when length is nil
  99.             end
  100.         else
  101.             isByteArray  = false
  102.         end
  103.         if( isByteArray ) then
  104.             if( valType == "table" ) then -- Performance enhance by table reuse
  105.                 for i = 1, #val do
  106.                     if( writeAll or (size < length) ) then
  107.                         byteList[i] = string.char(val[i])      -- No check for better performance
  108.                         size = size + 1
  109.                     else
  110.                         break
  111.                     end
  112.                 end
  113.             else
  114.                 for i = 1, #byteList do
  115.                     if( writeAll or (size < length) ) then
  116.                         byteList[i] = string.char(byteList[i]) -- No check for better performance
  117.                         size = size + 1
  118.                     else
  119.                         break
  120.                     end
  121.                 end
  122.             end
  123.             byteStr = table.concat(byteList)
  124.         end
  125.         local byteStrLength = byteStr:len()
  126.         if( byteStrLength < length ) then
  127.             if( valType == "string" ) then  -- String are stored from the high end, for it can be read by simple file:read()
  128.                 byteStr = byteStr..string.rep(string.char(0), length - byteStrLength)
  129.             else
  130.                 byteStr = string.rep(string.char(0), length - byteStrLength)..byteStr
  131.             end
  132.             size = size + length - byteStrLength
  133.         end
  134.         fileHandle:write(byteStr)
  135.     end
  136.     return size
  137. end
  138.  
  139. -- length: number, length in bytes to read. If it's larger than file length, return all read data. Only the last byte is used for "bool" [readType]
  140. -- [readType]: string, what kind of data should treat readed bytes as, can be "b"(default, return byte[]) or "n"(return number) or "s"(return string) or "bool"(return true/false)
  141. -- [fileHandle]: file handle, file to operate. Default is BinIO.File.
  142. -- return data: byte table/number/bool/string, read byte[] array or signed number or string, depends on [readType]. Return nil if fail or exceed file length. Return empty table/0/nil/empty string if length is 0.
  143. function BinIO.Read(length, readType, fileHandle)
  144.     local data = {}
  145.     if( fileHandle == nil ) then fileHandle = BinIO.File end
  146.     if( readType == nil ) then readType = "b" end
  147.     if( fileHandle ~= nil ) then
  148.         if( readType == "s" ) then -- String can be read directly
  149.             data = fileHandle:read(length)
  150.         else
  151.             local byteChar = nil
  152.             for ptr = 1, length do
  153.                 byteChar = fileHandle:read(1)
  154.                 if( byteChar == nil ) then
  155.                     data = nil
  156.                     break
  157.                 else
  158.                     table.insert(data, string.byte(byteChar))
  159.                 end
  160.             end
  161.         end
  162.     else
  163.         data = nil
  164.     end
  165.     if( data ~= nil and readType ~= "b" and readType ~= "s") then -- Convert the result
  166.         local len = #data
  167.         if( readType == "n" ) then
  168.             local num = 0
  169.             if( next(data) ~= nil ) then
  170.                 local BYTE_MASK = 0xFF
  171.                 local BYTE_SIZE = 0x100
  172.                 local sign = bit.band(data[1], 0x80)
  173.                 if( sign == 0 ) then
  174.                     for i = 1, len do
  175.                         num = num + data[len - i + 1] * BYTE_SIZE^(i-1)
  176.                     end
  177.                 else
  178.                     for i = 1, len do
  179.                         num = num + (BYTE_MASK - data[len - i + 1]) * BYTE_SIZE^(i-1)
  180.                     end
  181.                     num = -num - 1
  182.                 end
  183.             end
  184.             data = num
  185.         elseif( readType == "bool" ) then
  186.             local bool = nil
  187.             if( len > 0 ) then
  188.                 bool = not (data[len] == 0)
  189.             end
  190.             data = bool
  191.         end
  192.     end
  193.     return data
  194. end
  195.  
  196. -- [whence]: string, sets and gets the file position, see file:seek
  197. -- [offset]: number, sets and gets the file position, see file:seek
  198. -- [fileHandle]: file handle, file to operate. Default is BinIO.File.
  199. -- return pos: number, return the current position, return nil if fail
  200. function BinIO.Seek(whence, offset, fileHandle)
  201.     local pos = nil
  202.     if( fileHandle == nil ) then fileHandle = BinIO.File end
  203.     if( fileHandle ~= nil ) then
  204.         pos = fileHandle:seek(whence, offset)
  205.     end
  206.     return pos
  207. end
  208.  
  209. -- mode: buffering mode for file handle, see file:setvbuf
  210. -- [size]: buffering handle, only functional for "full" and "line" mode, see file:setvbuf
  211. function BinIO.SetBuffer(mode, size)
  212.     if( fileHandle == nil ) then fileHandle = BinIO.File end
  213.     if( fileHandle ~= nil ) then
  214.         fileHandle:setvbuf(mode, size)
  215.     end
  216. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement