1. lua 5.3 string.pack 和 string.unpack
http://cloudwu.github.io/lua53doc/manual.html#6.4.2
用于
string.pack
,
string.packsize
,
string.unpack
的第一个参数。 它是一个描述了需要创建或读取的结构之布局。
格式串是由转换选项构成的序列。 这些转换选项列在后面:
-
: 设为小端编码<
-
: 设为大端编码>
-
: 大小端遵循本地设置=
-
: 将最大对齐数设为![n]
(默认遵循本地对齐设置)n
-
: 一个有符号字节 (b
)char
-
: 一个无符号字节 (B
)char
-
: 一个有符号h
(本地大小)short
-
: 一个无符号H
(本地大小)short
-
: 一个有符号l
(本地大小)long
-
: 一个无符号L
(本地大小)long
-
: 一个j
lua_Integer
-
: 一个J
lua_Unsigned
-
: 一个T
(本地大小)size_t
-
: 一个i[n]
字节长(默认为本地大小)的有符号n
int
-
: 一个I[n]
字节长(默认为本地大小)的无符号n
int
-
: 一个f
(本地大小)float
-
: 一个d
(本地大小)double
-
: 一个n
lua_Number
-
:cn
字节固定长度的字符串n
-
: 零结尾的字符串z
-
: 长度加内容的字符串,其长度编码为一个s[n]
字节(默认是个n
) 长的无符号整数。size_t
-
: 一个字节的填充x
-
: 按选项Xop
的方式对齐(忽略它的其它方面)的一个空条目op
- ' ': (空格)忽略
( "
[n]
" 表示一个可选的整数。) 除填充、空格、配置项(选项 "
xX <=>!
")外, 每个选项都关联一个参数(对于
string.pack
) 或结果(对于
string.unpack
)。
对于选项 "
!n
", "
sn
", "
in
", "
In
",
n
可以是 1 到 16 间的整数。 所有的整数选项都将做溢出检查;
string.pack
检查提供的值是否能用指定的字长表示;
string.unpack
检查读出的值能否置入 Lua 整数中。
任何格式串都假设有一个 "
!1=
" 前缀, 即最大对齐为 1 (无对齐)且采用本地大小端设置。
对齐行为按如下规则工作: 对每个选项,格式化时都会填充一些字节直到数据从一个特定偏移处开始, 这个位置是该选项的大小和最大对齐数中较小的那个数的倍数; 这个较小值必须是 2 个整数次方。 选项 "
c
" 及 "
z
" 不做对齐处理; 选项 "
s
" 对对齐遵循其开头的整数。
string.pack
用零去填充 (
string.unpack
则忽略它)。
2. lpack
http://www.luteus.biz/Download/LoriotPro_Doc/LUA/LUA_For_Windows/lpack/
z
: zero-terminated string
p
: string preceded by length byte
P
: string preceded by length word
a
: string preceded by length size_t
A
: string
f
: float
d
: double
n
: Lua number
c
: char
b
: byte (unsigned char)
h
: short
H
: unsigned short
i
: int
I
: unsigned int
l
: long
L
: unsigned long
<
: little endian
>
: big endian
=
: native endian
3.查看了lpack和 lua5.3 的string.pack,string.unpack后发现两者 有些差别
3.1 lpack.pack 的fmt b10 在 string.pack fmt bbbbbbbbbb
3.2 lpack.unpack 和 string.unpack 参数位置不一样, lpack fmt 在后面, string.pack在前面
3.3 unpack 在5.3移到table下了,所以unpack 修改成 table.unpack
要想使用zrong的ByteArray必需修改
下面是修改后代码 暂时没有报错。主要是
string.pack 和 string.unpack的修改
--[[
Serialzation bytes stream like ActionScript flash.utils.ByteArray.
It depends on lpack.
A sample: https://github.com/zrong/lua#ByteArray
@see http://underpop.free.fr/l/lua/lpack/
@see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/ByteArray.html
@author zrong(zengrong.net)
Creation 2013-11-14
Last Modification 2014-07-09
]]
local ByteArray = cc.class("ByteArray")
ByteArray.ENDIAN_LITTLE = "ENDIAN_LITTLE"
ByteArray.ENDIAN_BIG = "ENDIAN_BIG"
ByteArray.radix = {[10]="%03u",[8]="%03o",[16]="%02X"}
-- require("pack")
--- Return a string to display.
-- If self is ByteArray, read string from self.
-- Else, treat self as byte string.
-- @param __radix radix of display, value is 8, 10 or 16, default is 10.
-- @param __separator default is " ".
-- @return string, number
function ByteArray.toString(self, __radix, __separator)
__radix = __radix or 16
__radix = ByteArray.radix[__radix] or "%02X"
__separator = __separator or " "
local __fmt = __radix..__separator
local __format = function(__s)
return string.format(__fmt, string.byte(__s))
end
if type(self) == "string" then
return string.gsub(self, "(.)", __format)
end
local __bytes = {}
for i=1,#self._buf do
__bytes[i] = __format(self._buf[i])
end
return table.concat(__bytes) ,#__bytes
end
function ByteArray:ctor(__endian)
self._endian = __endian
self._buf = {}
self._pos = 1
end
function ByteArray:getLen()
return #self._buf
end
function ByteArray:getAvailable()
return #self._buf - self._pos + 1
end
function ByteArray:getPos()
return self._pos
end
function ByteArray:setPos(__pos)
self._pos = __pos
return self
end
function ByteArray:getEndian()
return self._endian
end
function ByteArray:setEndian(__endian)
self._endian = __endian
end
--- Get all byte array as a lua string.
-- Do not update position.
function ByteArray:getBytes(__offset, __length)
__offset = __offset or 1
__length = __length or #self._buf
--printf("getBytes,offset:%u, length:%u", __offset, __length)
return table.concat(self._buf, "", __offset, __length)
end
--- Get pack style string by lpack.
-- The result use ByteArray.getBytes to get is unavailable for lua socket.
-- E.g. the #self:_buf is 18, but #ByteArray.getBytes is 63.
-- I think the cause is the table.concat treat every item in ByteArray._buf as a general string, not a char.
-- So, I use lpack repackage the ByteArray._buf, theretofore, I must convert them to a byte number.
function ByteArray:getPack(__offset, __length)
__offset = __offset or 1
__length = __length or #self._buf
local __fmt = self:_getLC("c" .. (__length - __offset + 1))
-- print("fmt:", __fmt, #__t)
local __s = string.pack(__fmt, self:getBytes(__offset, __length))
return __s
end
--- rawUnPack perform like lpack.pack, but return the ByteArray.
function ByteArray:rawPack(__fmt, ...)
local __s = string.pack(__fmt, ...)
self:writeBuf(__s)
return self
end
--- rawUnPack perform like lpack.unpack, but it is only support FORMAT parameter.
-- Because ByteArray include a position itself, so we haven't to save another.
function ByteArray:rawUnPack(__fmt)
-- read all of bytes.
local __s = self:getBytes(self._pos)
local __next, __val = string.unpack(__fmt, __s)
-- update position of the ByteArray
self._pos = self._pos + __next
-- Alternate value and next
return __val, __next
end
function ByteArray:readBool()
-- When char > 256, the readByte method will show an error.
-- So, we have to use readChar
return self:readChar() ~= 0
end
function ByteArray:writeBool(__bool)
if __bool then
self:writeByte(1)
else
self:writeByte(0)
end
return self
end
function ByteArray:readDouble()
local __v, __ = string.unpack(self:_getLC("d"), self:readBuf(8))
return __v
end
function ByteArray:writeDouble(__double)
local __s = string.pack( self:_getLC("d"), __double)
self:writeBuf(__s)
return self
end
function ByteArray:readFloat()
local __v, __ = string.unpack(self:_getLC("f"), self:readBuf(4))
return __v
end
function ByteArray:writeFloat(__float)
local __s = string.pack( self:_getLC("f"), __float)
self:writeBuf(__s)
return self
end
function ByteArray:readInt()
local __v, __ = string.unpack(self:_getLC("i"), self:readBuf(4))
return __v
end
function ByteArray:writeInt(__int)
local __s = string.pack( self:_getLC("i"), __int)
self:writeBuf(__s)
return self
end
function ByteArray:readUInt()
local __v, __ = string.unpack(self:_getLC("I"), self:readBuf(4))
return __v
end
function ByteArray:writeUInt(__uint)
local __s = string.pack(self:_getLC("I"), __uint)
self:writeBuf(__s)
return self
end
function ByteArray:readShort()
local __v, __ = string.unpack(self:_getLC("h"), self:readBuf(2))
return __v
end
function ByteArray:writeShort(__short)
local __s = string.pack( self:_getLC("h"), __short)
self:writeBuf(__s)
return self
end
function ByteArray:readUShort()
local __v, __ = string.unpack(self:_getLC("H"), self:readBuf(2))
return __v
end
function ByteArray:writeUShort(__ushort)
local __s = string.pack(self:_getLC("H"), __ushort)
self:writeBuf(__s)
return self
end
--[[
-- 2014-07-09 Remove all of methods about Long in ByteArray.
-- @see http://zengrong.net/post/2134.htm
function ByteArray:readLong()
local __v, __ = string.unpack(self:_getLC("l"), self:readBuf(8))
return __v
end
function ByteArray:writeLong(__long)
local __s = string.pack( self:_getLC("l"), __long)
self:writeBuf(__s)
return self
end
function ByteArray:readULong()
local __v, __ = string.unpack(self:_getLC("L"), self:readBuf(4))
return __v
end
function ByteArray:writeULong(__ulong)
local __s = string.pack( self:_getLC("L"), __ulong)
self:writeBuf(__s)
return self
end
]]
function ByteArray:readUByte()
local __v, __al = string.unpack("b", self:readRawByte())
return __val
end
function ByteArray:writeUByte(__ubyte)
local __s = string.pack("b", __ubyte)
self:writeBuf(__s)
return self
end
function ByteArray:readLuaNumber(__number)
local __v, __ = string.unpack(self:_getLC("n"), self:readBuf(8))
return __v
end
function ByteArray:writeLuaNumber(__number)
local __s = string.pack(self:_getLC("n"), __number)
self:writeBuf(__s)
return self
end
--- The differently about (read/write)StringBytes and (read/write)String
-- are use pack libraty or not.
function ByteArray:readStringBytes(__len)
assert(__len, "Need a length of the string!")
if __len == 0 then return "" end
self:_checkAvailable()
local __v, __ = string.unpack(self:_getLC("A"..__len), self:readBuf(__len))
return __v
end
function ByteArray:writeStringBytes(__string)
local __s = string.pack(self:_getLC("A"), __string)
self:writeBuf(__s)
return self
end
function ByteArray:readString(__len)
assert(__len, "Need a length of the string!")
if __len == 0 then return "" end
self:_checkAvailable()
return self:readBuf(__len)
end
function ByteArray:writeString(__string)
self:writeBuf(__string)
return self
end
function ByteArray:readStringUInt()
self:_checkAvailable()
local __len = self:readUInt()
return self:readStringBytes(__len)
end
function ByteArray:writeStringUInt(__string)
self:writeUInt(#__string)
self:writeStringBytes(__string)
return self
end
--- The length of size_t in C/C++ is mutable.
-- In 64bit os, it is 8 bytes.
-- In 32bit os, it is 4 bytes.
function ByteArray:readStringSizeT()
self:_checkAvailable()
local __s = self:rawUnPack(self:_getLC("a"))
return __s
end
--- Perform rawPack() simply.
function ByteArray:writeStringSizeT(__string)
self:rawPack(self:_getLC("a"), __string)
return self
end
function ByteArray:readStringUShort()
self:_checkAvailable()
local __len = self:readUShort()
return self:readStringBytes(__len)
end
function ByteArray:writeStringUShort(__string)
local __s = string.pack(self:_getLC("P"), __string)
self:writeBuf(__s)
return self
end
--- Read some bytes from buf
-- @return a bit string
function ByteArray:readBytes(__bytes, __offset, __length)
assert(cc.iskindof(__bytes, "ByteArray"), "Need a ByteArray instance!")
local __selfLen = #self._buf
local __availableLen = __selfLen - self._pos
__offset = __offset or 1
if __offset > __selfLen then __offset = 1 end
__length = __length or 0
if __length == 0 or __length > __availableLen then __length = __availableLen end
__bytes:setPos(__offset)
for i=__offset,__offset+__length do
__bytes:writeRawByte(self:readRawByte())
end
end
--- Write some bytes into buf
function ByteArray:writeBytes(__bytes, __offset, __length)
assert(cc.iskindof(__bytes, "ByteArray"), "Need a ByteArray instance!")
local __bytesLen = __bytes:getLen()
if __bytesLen == 0 then return end
__offset = __offset or 1
if __offset > __bytesLen then __offset = 1 end
local __availableLen = __bytesLen - __offset
__length = __length or __availableLen
if __length == 0 or __length > __availableLen then __length = __availableLen end
local __oldPos = __bytes:getPos()
__bytes:setPos(__offset)
for i=__offset,__offset+__length do
self:writeRawByte(__bytes:readRawByte())
end
__bytes:setPos(__oldPos)
return self
end
--- Actionscript3 readByte == lpack readChar
-- A signed char
function ByteArray:readChar()
local __v, __al = string.unpack("c", self:readRawByte())
return __val
end
function ByteArray:writeChar(__char)
self:writeRawByte(string.pack("c", __char))
return self
end
--- Use the lua string library to get a byte
-- A unsigned char
function ByteArray:readByte()
return string.byte(self:readRawByte())
end
--- Use the lua string library to write a byte.
-- The byte is a number between 0 and 255, otherwise, the lua will get an error.
function ByteArray:writeByte(__byte)
self:writeRawByte(string.char(__byte))
return self
end
function ByteArray:readRawByte()
self:_checkAvailable()
local __byte = self._buf[self._pos]
self._pos = self._pos + 1
return __byte
end
function ByteArray:writeRawByte(__rawByte)
if self._pos > #self._buf+1 then
for i=#self._buf+1,self._pos-1 do
self._buf[i] = string.char(0)
end
end
self._buf[self._pos] = string.sub(__rawByte, 1,1)
self._pos = self._pos + 1
return self
end
--- Read a byte array as string from current position, then update the position.
function ByteArray:readBuf(__len)
--printf("readBuf,len:%u, pos:%u", __len, self._pos)
local __ba = self:getBytes(self._pos, self._pos + __len - 1)
self._pos = self._pos + __len
return __ba
end
--- Write a encoded char array into buf
function ByteArray:writeBuf(__s)
for i=1,#__s do
self:writeRawByte(string.sub(__s,i,i))
end
return self
end
----------------------------------------
-- private
----------------------------------------
function ByteArray:_checkAvailable()
assert(#self._buf >= self._pos, string.format("End of file was encountered. pos: %d, len: %d.", self._pos, #self._buf))
end
--- Get Letter Code
function ByteArray:_getLC(__fmt)
__fmt = __fmt or ""
if self._endian == ByteArray.ENDIAN_LITTLE then
return "<"..__fmt
elseif self._endian == ByteArray.ENDIAN_BIG then
return ">"..__fmt
end
return "="..__fmt
end
return ByteArray