1-- $Id: testes/tpack.lua $
  2-- See Copyright Notice in file all.lua
  3
  4local pack = string.pack
  5local packsize = string.packsize
  6local unpack = string.unpack
  7
  8print "testing pack/unpack"
  9
 10-- maximum size for integers
 11local NB = 16
 12
 13local sizeshort = packsize("h")
 14local sizeint = packsize("i")
 15local sizelong = packsize("l")
 16local sizesize_t = packsize("T")
 17local sizeLI = packsize("j")
 18local sizefloat = packsize("f")
 19local sizedouble = packsize("d")
 20local sizenumber = packsize("n")
 21local little = (pack("i2", 1) == "\1\0")
 22local align = packsize("!xXi16")
 23
 24assert(1 <= sizeshort and sizeshort <= sizeint and sizeint <= sizelong and
 25       sizefloat <= sizedouble)
 26
 27print("platform:")
 28print(string.format(
 29  "\tshort %d, int %d, long %d, size_t %d, float %d, double %d,\n\z
 30   \tlua Integer %d, lua Number %d",
 31   sizeshort, sizeint, sizelong, sizesize_t, sizefloat, sizedouble,
 32   sizeLI, sizenumber))
 33print("\t" .. (little and "little" or "big") .. " endian")
 34print("\talignment: " .. align)
 35
 36
 37-- check errors in arguments
 38local function checkerror (msg, f, ...)
 39  local status, err = pcall(f, ...)
 40  -- print(status, err, msg)
 41  assert(not status and string.find(err, msg))
 42end
 43
 44-- minimum behavior for integer formats
 45assert(unpack("B", pack("B", 0xff)) == 0xff)
 46assert(unpack("b", pack("b", 0x7f)) == 0x7f)
 47assert(unpack("b", pack("b", -0x80)) == -0x80)
 48
 49assert(unpack("H", pack("H", 0xffff)) == 0xffff)
 50assert(unpack("h", pack("h", 0x7fff)) == 0x7fff)
 51assert(unpack("h", pack("h", -0x8000)) == -0x8000)
 52
 53assert(unpack("L", pack("L", 0xffffffff)) == 0xffffffff)
 54assert(unpack("l", pack("l", 0x7fffffff)) == 0x7fffffff)
 55assert(unpack("l", pack("l", -0x80000000)) == -0x80000000)
 56
 57
 58for i = 1, NB do
 59  -- small numbers with signal extension ("\xFF...")
 60  local s = string.rep("\xff", i)
 61  assert(pack("i" .. i, -1) == s)
 62  assert(packsize("i" .. i) == #s)
 63  assert(unpack("i" .. i, s) == -1)
 64
 65  -- small unsigned number ("\0...\xAA")
 66  s = "\xAA" .. string.rep("\0", i - 1)
 67  assert(pack("<I" .. i, 0xAA) == s)
 68  assert(unpack("<I" .. i, s) == 0xAA)
 69  assert(pack(">I" .. i, 0xAA) == s:reverse())
 70  assert(unpack(">I" .. i, s:reverse()) == 0xAA)
 71end
 72
 73do
 74  local lnum = 0x13121110090807060504030201
 75  local s = pack("<j", lnum)
 76  assert(unpack("<j", s) == lnum)
 77  assert(unpack("<i" .. sizeLI + 1, s .. "\0") == lnum)
 78  assert(unpack("<i" .. sizeLI + 1, s .. "\0") == lnum)
 79
 80  for i = sizeLI + 1, NB do
 81    local s = pack("<j", -lnum)
 82    assert(unpack("<j", s) == -lnum)
 83    -- strings with (correct) extra bytes
 84    assert(unpack("<i" .. i, s .. ("\xFF"):rep(i - sizeLI)) == -lnum)
 85    assert(unpack(">i" .. i, ("\xFF"):rep(i - sizeLI) .. s:reverse()) == -lnum)
 86    assert(unpack("<I" .. i, s .. ("\0"):rep(i - sizeLI)) == -lnum)
 87
 88    -- overflows
 89    checkerror("does not fit", unpack, "<I" .. i, ("\x00"):rep(i - 1) .. "\1")
 90    checkerror("does not fit", unpack, ">i" .. i, "\1" .. ("\x00"):rep(i - 1))
 91  end
 92end
 93
 94for i = 1, sizeLI do
 95  local lstr = "\1\2\3\4\5\6\7\8\9\10\11\12\13"
 96  local lnum = 0x13121110090807060504030201
 97  local n = lnum & (~(-1 << (i * 8)))
 98  local s = string.sub(lstr, 1, i)
 99  assert(pack("<i" .. i, n) == s)
100  assert(pack(">i" .. i, n) == s:reverse())
101  assert(unpack(">i" .. i, s:reverse()) == n)
102end
103
104-- sign extension
105do
106  local u = 0xf0
107  for i = 1, sizeLI - 1 do
108    assert(unpack("<i"..i, "\xf0"..("\xff"):rep(i - 1)) == -16)
109    assert(unpack(">I"..i, "\xf0"..("\xff"):rep(i - 1)) == u)
110    u = u * 256 + 0xff
111  end
112end
113
114-- mixed endianness
115do
116  assert(pack(">i2 <i2", 10, 20) == "\0\10\20\0")
117  local a, b = unpack("<i2 >i2", "\10\0\0\20")
118  assert(a == 10 and b == 20)
119  assert(pack("=i4", 2001) == pack("i4", 2001))
120end
121
122print("testing invalid formats")
123
124checkerror("out of limits", pack, "i0", 0)
125checkerror("out of limits", pack, "i" .. NB + 1, 0)
126checkerror("out of limits", pack, "!" .. NB + 1, 0)
127checkerror("%(17%) out of limits %[1,16%]", pack, "Xi" .. NB + 1)
128checkerror("invalid format option 'r'", pack, "i3r", 0)
129checkerror("16%-byte integer", unpack, "i16", string.rep('\3', 16))
130checkerror("not power of 2", pack, "!4i3", 0);
131checkerror("missing size", pack, "c", "")
132checkerror("variable%-length format", packsize, "s")
133checkerror("variable%-length format", packsize, "z")
134
135-- overflow in option size  (error will be in digit after limit)
136checkerror("invalid format", packsize, "c1" .. string.rep("0", 40))
137
138if packsize("i") == 4 then
139  -- result would be 2^31  (2^3 repetitions of 2^28 strings)
140  local s = string.rep("c268435456", 2^3)
141  checkerror("too large", packsize, s)
142  -- one less is OK
143  s = string.rep("c268435456", 2^3 - 1) .. "c268435455"
144  assert(packsize(s) == 0x7fffffff)
145end
146
147-- overflow in packing
148for i = 1, sizeLI - 1 do
149  local umax = (1 << (i * 8)) - 1
150  local max = umax >> 1
151  local min = ~max
152  checkerror("overflow", pack, "<I" .. i, -1)
153  checkerror("overflow", pack, "<I" .. i, min)
154  checkerror("overflow", pack, ">I" .. i, umax + 1)
155
156  checkerror("overflow", pack, ">i" .. i, umax)
157  checkerror("overflow", pack, ">i" .. i, max + 1)
158  checkerror("overflow", pack, "<i" .. i, min - 1)
159
160  assert(unpack(">i" .. i, pack(">i" .. i, max)) == max)
161  assert(unpack("<i" .. i, pack("<i" .. i, min)) == min)
162  assert(unpack(">I" .. i, pack(">I" .. i, umax)) == umax)
163end
164
165-- Lua integer size
166assert(unpack(">j", pack(">j", math.maxinteger)) == math.maxinteger)
167assert(unpack("<j", pack("<j", math.mininteger)) == math.mininteger)
168assert(unpack("<J", pack("<j", -1)) == -1)   -- maximum unsigned integer
169
170if little then
171  assert(pack("f", 24) == pack("<f", 24))
172else
173  assert(pack("f", 24) == pack(">f", 24))
174end
175
176print "testing pack/unpack of floating-point numbers" 
177
178for _, n in ipairs{0, -1.1, 1.9, 1/0, -1/0, 1e20, -1e20, 0.1, 2000.7} do
179    assert(unpack("n", pack("n", n)) == n)
180    assert(unpack("<n", pack("<n", n)) == n)
181    assert(unpack(">n", pack(">n", n)) == n)
182    assert(pack("<f", n) == pack(">f", n):reverse())
183    assert(pack(">d", n) == pack("<d", n):reverse())
184end
185
186-- for non-native precisions, test only with "round" numbers
187for _, n in ipairs{0, -1.5, 1/0, -1/0, 1e10, -1e9, 0.5, 2000.25} do
188  assert(unpack("<f", pack("<f", n)) == n)
189  assert(unpack(">f", pack(">f", n)) == n)
190  assert(unpack("<d", pack("<d", n)) == n)
191  assert(unpack(">d", pack(">d", n)) == n)
192end
193
194print "testing pack/unpack of strings"
195do
196  local s = string.rep("abc", 1000)
197  assert(pack("zB", s, 247) == s .. "\0\xF7")
198  local s1, b = unpack("zB", s .. "\0\xF9")
199  assert(b == 249 and s1 == s)
200  s1 = pack("s", s)
201  assert(unpack("s", s1) == s)
202
203  checkerror("does not fit", pack, "s1", s)
204
205  checkerror("contains zeros", pack, "z", "alo\0");
206
207  checkerror("unfinished string", unpack, "zc10000000", "alo")
208
209  for i = 2, NB do
210    local s1 = pack("s" .. i, s)
211    assert(unpack("s" .. i, s1) == s and #s1 == #s + i)
212  end
213end
214
215do
216  local x = pack("s", "alo")
217  checkerror("too short", unpack, "s", x:sub(1, -2))
218  checkerror("too short", unpack, "c5", "abcd")
219  checkerror("out of limits", pack, "s100", "alo")
220end
221
222do
223  assert(pack("c0", "") == "")
224  assert(packsize("c0") == 0)
225  assert(unpack("c0", "") == "")
226  assert(pack("<! c3", "abc") == "abc")
227  assert(packsize("<! c3") == 3)
228  assert(pack(">!4 c6", "abcdef") == "abcdef")
229  assert(pack("c3", "123") == "123")
230  assert(pack("c0", "") == "")
231  assert(pack("c8", "123456") == "123456\0\0")
232  assert(pack("c88", "") == string.rep("\0", 88))
233  assert(pack("c188", "ab") == "ab" .. string.rep("\0", 188 - 2))
234  local a, b, c = unpack("!4 z c3", "abcdefghi\0xyz")
235  assert(a == "abcdefghi" and b == "xyz" and c == 14)
236  checkerror("longer than", pack, "c3", "1234")
237end
238
239
240-- testing multiple types and sequence
241do
242  local x = pack("<b h b f d f n i", 1, 2, 3, 4, 5, 6, 7, 8)
243  assert(#x == packsize("<b h b f d f n i"))
244  local a, b, c, d, e, f, g, h = unpack("<b h b f d f n i", x)
245  assert(a == 1 and b == 2 and c == 3 and d == 4 and e == 5 and f == 6 and
246         g == 7 and h == 8) 
247end
248
249print "testing alignment"
250do
251  assert(pack(" < i1 i2 ", 2, 3) == "\2\3\0")   -- no alignment by default
252  local x = pack(">!8 b Xh i4 i8 c1 Xi8", -12, 100, 200, "\xEC")
253  assert(#x == packsize(">!8 b Xh i4 i8 c1 Xi8"))
254  assert(x == "\xf4" .. "\0\0\0" ..
255              "\0\0\0\100" ..
256              "\0\0\0\0\0\0\0\xC8" .. 
257              "\xEC" .. "\0\0\0\0\0\0\0")
258  local a, b, c, d, pos = unpack(">!8 c1 Xh i4 i8 b Xi8 XI XH", x)
259  assert(a == "\xF4" and b == 100 and c == 200 and d == -20 and (pos - 1) == #x)
260
261  x = pack(">!4 c3 c4 c2 z i4 c5 c2 Xi4",
262                  "abc", "abcd", "xz", "hello", 5, "world", "xy")
263  assert(x == "abcabcdxzhello\0\0\0\0\0\5worldxy\0")
264  local a, b, c, d, e, f, g, pos = unpack(">!4 c3 c4 c2 z i4 c5 c2 Xh Xi4", x)
265  assert(a == "abc" and b == "abcd" and c == "xz" and d == "hello" and
266         e == 5 and f == "world" and g == "xy" and (pos - 1) % 4 == 0)
267
268  x = pack(" b b Xd b Xb x", 1, 2, 3)
269  assert(packsize(" b b Xd b Xb x") == 4)
270  assert(x == "\1\2\3\0")
271  a, b, c, pos = unpack("bbXdb", x)
272  assert(a == 1 and b == 2 and c == 3 and pos == #x)
273
274  -- only alignment
275  assert(packsize("!8 xXi8") == 8)
276  local pos = unpack("!8 xXi8", "0123456701234567"); assert(pos == 9)
277  assert(packsize("!8 xXi2") == 2)
278  local pos = unpack("!8 xXi2", "0123456701234567"); assert(pos == 3)
279  assert(packsize("!2 xXi2") == 2)
280  local pos = unpack("!2 xXi2", "0123456701234567"); assert(pos == 3)
281  assert(packsize("!2 xXi8") == 2)
282  local pos = unpack("!2 xXi8", "0123456701234567"); assert(pos == 3)
283  assert(packsize("!16 xXi16") == 16)
284  local pos = unpack("!16 xXi16", "0123456701234567"); assert(pos == 17)
285
286  checkerror("invalid next option", pack, "X")
287  checkerror("invalid next option", unpack, "XXi", "")
288  checkerror("invalid next option", unpack, "X i", "")
289  checkerror("invalid next option", pack, "Xc1")
290end
291
292do    -- testing initial position
293  local x = pack("i4i4i4i4", 1, 2, 3, 4)
294  for pos = 1, 16, 4 do
295    local i, p = unpack("i4", x, pos)
296    assert(i == pos//4 + 1 and p == pos + 4)
297  end
298
299  -- with alignment
300  for pos = 0, 12 do    -- will always round position to power of 2
301    local i, p = unpack("!4 i4", x, pos + 1)
302    assert(i == (pos + 3)//4 + 1 and p == i*4 + 1)
303  end
304
305  -- negative indices
306  local i, p = unpack("!4 i4", x, -4)
307  assert(i == 4 and p == 17)
308  local i, p = unpack("!4 i4", x, -7)
309  assert(i == 4 and p == 17)
310  local i, p = unpack("!4 i4", x, -#x)
311  assert(i == 1 and p == 5)
312
313  -- limits
314  for i = 1, #x + 1 do
315    assert(unpack("c0", x, i) == "")
316  end
317  checkerror("out of string", unpack, "c0", x, #x + 2)
318 
319end
320
321print "OK"
322