1Struct = {}
2
3function Struct.pack(format, ...)
4 local stream = {}
5 local vars = {...}
6 local endianness = true
7
8 for i = 1, format:len() do
9 local opt = format:sub(i, i)
10
11 if opt == '>' then
12 endianness = false
13 elseif opt:find('[bBhHiIlL]') then
14 local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1
15 local val = tonumber(table.remove(vars, 1))
16
17 if val < 0 then
18 val = val + 2 ^ (n * 8 - 1)
19 end
20
21 local bytes = {}
22 for j = 1, n do
23 table.insert(bytes, string.char(val % (2 ^ 8)))
24 val = math.floor(val / (2 ^ 8))
25 end
26
27 if not endianness then
28 table.insert(stream, string.reverse(table.concat(bytes)))
29 else
30 table.insert(stream, table.concat(bytes))
31 end
32 elseif opt:find('[fd]') then
33 local val = tonumber(table.remove(vars, 1))
34 local sign = 0
35
36 if val < 0 then
37 sign = 1
38 val = -val
39 end
40
41 local mantissa, exponent = math.frexp(val)
42 if val == 0 then
43 mantissa = 0
44 exponent = 0
45 else
46 mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == 'd') and 53 or 24)
47 exponent = exponent + ((opt == 'd') and 1022 or 126)
48 end
49
50 local bytes = {}
51 if opt == 'd' then
52 val = mantissa
53 for i = 1, 6 do
54 table.insert(bytes, string.char(math.floor(val) % (2 ^ 8)))
55 val = math.floor(val / (2 ^ 8))
56 end
57 else
58 table.insert(bytes, string.char(math.floor(mantissa) % (2 ^ 8)))
59 val = math.floor(mantissa / (2 ^ 8))
60 table.insert(bytes, string.char(math.floor(val) % (2 ^ 8)))
61 val = math.floor(val / (2 ^ 8))
62 end
63
64 table.insert(bytes, string.char(math.floor(exponent * ((opt == 'd') and 16 or 128) + val) % (2 ^ 8)))
65 val = math.floor((exponent * ((opt == 'd') and 16 or 128) + val) / (2 ^ 8))
66 table.insert(bytes, string.char(math.floor(sign * 128 + val) % (2 ^ 8)))
67 val = math.floor((sign * 128 + val) / (2 ^ 8))
68
69 if not endianness then
70 table.insert(stream, string.reverse(table.concat(bytes)))
71 else
72 table.insert(stream, table.concat(bytes))
73 end
74 elseif opt == 's' then
75 table.insert(stream, tostring(table.remove(vars, 1)))
76 table.insert(stream, string.char(0))
77 elseif opt == 'c' then
78 local n = format:sub(i + 1):match('%d+')
79 local length = tonumber(n)
80
81 if length > 0 then
82 local str = tostring(table.remove(vars, 1))
83 if length - str:len() > 0 then
84 str = str .. string.rep(' ', length - str:len())
85 end
86 table.insert(stream, str:sub(1, length))
87 end
88 i = i + n:len()
89 end
90 end
91
92 return table.concat(stream)
93end
94
95function Struct.unpack(format, stream)
96 local vars = {}
97 local iterator = 1
98 local endianness = true
99
100 for i = 1, format:len() do
101 local opt = format:sub(i, i)
102
103 if opt == '>' then
104 endianness = false
105 elseif opt:find('[bBhHiIlL]') then
106 local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1
107 local signed = opt:lower() == opt
108
109 local val = 0
110 for j = 1, n do
111 local byte = string.byte(stream:sub(iterator, iterator))
112 if endianness then
113 val = val + byte * (2 ^ ((j - 1) * 8))
114 else
115 val = val + byte * (2 ^ ((n - j) * 8))
116 end
117 iterator = iterator + 1
118 end
119
120 if signed then
121 val = val - 2 ^ (n * 8 - 1)
122 end
123
124 table.insert(vars, val)
125 elseif opt:find('[fd]') then
126 local n = (opt == 'd') and 8 or 4
127 local x = stream:sub(iterator, iterator + n - 1)
128 iterator = iterator + n
129
130 if not endianness then
131 x = string.reverse(x)
132 end
133
134 local sign = 1
135 local mantissa = string.byte(x, (opt == 'd') and 7 or 3) % ((opt == 'd') and 16 or 128)
136 for i = n - 2, 1, -1 do
137 mantissa = mantissa * (2 ^ 8) + string.byte(x, i)
138 end
139
140 if string.byte(x, n) > 127 then
141 sign = -1
142 end
143
144 local exponent = (string.byte(x, n) % 128) * ((opt == 'd') and 16 or 2) + math.floor(string.byte(x, n - 1) / ((opt == 'd') and 16 or 128))
145 if exponent == 0 then
146 table.insert(vars, 0.0)
147 else
148 mantissa = (math.ldexp(mantissa, (opt == 'd') and -52 or -23) + 1) * sign
149 table.insert(vars, math.ldexp(mantissa, exponent - ((opt == 'd') and 1023 or 127)))
150 end
151 elseif opt == 's' then
152 local bytes = {}
153 for j = iterator, stream:len() do
154 if stream:sub(j, j) == string.char(0) then
155 break
156 end
157
158 table.insert(bytes, stream:sub(j, j))
159 end
160
161 local str = table.concat(bytes)
162 iterator = iterator + str:len() + 1
163 table.insert(vars, str)
164 elseif opt == 'c' then
165 local n = format:sub(i + 1):match('%d+')
166 table.insert(vars, stream:sub(iterator, iterator + tonumber(n)))
167 iterator = iterator + tonumber(n)
168 i = i + n:len()
169 end
170 end
171
172 return unpack(vars)
173end
174