-
Notifications
You must be signed in to change notification settings - Fork 3
/
bytes.lua
319 lines (262 loc) · 5.85 KB
/
bytes.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
local bytes = {}
---@class Buffer
---@field private buf string
---@field private pos integer
---@field public size integer
local Buffer = {}
Buffer.__index = Buffer
bytes.Buffer = Buffer
---Create a new Buffer from a byte string.
---@param buf string
---@return Buffer
function Buffer.new(buf)
local b = {
buf = buf or "",
pos = 1,
size = #buf,
e = bytes.le,
}
setmetatable(b, Buffer)
return b
end
---
---Sets and gets the buffer position, measured from the beginning of the buffer.
---
---@param whence? seekwhence
---@param offset? integer
---@return integer offset
function Buffer:seek(whence, offset)
whence = whence or "cur"
offset = offset or 0
if whence == "cur" then
self.pos = self.pos + offset
elseif whence == "set" then
self.pos = offset + 1
elseif whence == "end" then
self.pos = self.size
else
error("bad argument #1 to 'seek' (invalid option 'foo'")
end
assert(self.pos >= 1)
return self.pos - 1
end
---Read a single byte from the buffer.
---@return integer
function Buffer:read_byte()
if self.pos > self.size then error("end of buffer") end
local b = string.byte(self.buf, self.pos)
self.pos = self.pos + 1
return b
end
---Get a byte from the buffer without advancing the cursor.
---@param whence? seekwhence
---@param offset? integer
---@return integer?
function Buffer:peek(whence, offset)
whence = whence or "cur"
offset = offset or 0
local pos = 1
if whence == "cur" then
pos = self.pos + offset
elseif whence == "set" then
pos = offset + 1
elseif whence == "end" then
pos = self.size
else
error("bad argument #1 to 'seek' (invalid option 'foo'")
end
assert(pos >= 1)
return string.byte(self.buf, pos)
end
---Read up to n bytes from the buffer.
---@param n integer
---@return string
function Buffer:read(n)
if self.pos > self.size then error("end of buffer") end
local b = string.sub(self.buf, self.pos, self.pos + (n-1))
self.pos = self.pos + #b
return b
end
---Skip forward N bytes
---@param n integer?
---@return integer
function Buffer:skip(n)
self.pos = self.pos + (n or 1)
return self.pos
end
---Get the underlying bytes backing the buffer.
---@return string
function Buffer:bytes()
return self.buf
end
---@return Buffer
function Buffer:slice(i, j)
i = i or 0
j = j or self.size
return Buffer.new(self.buf:sub(i, j))
end
---Get the number of bytes remaining to be read, based on the current position.
---@return integer
function Buffer:remaining()
return self.size - (self.pos - 1)
end
---Set the default endianness.
function Buffer:endian(byte_order)
if byte_order == "<" then
self.e = bytes.le
elseif byte_order == ">" then
self.e = bytes.be
else
error(string.format("unknown byte order '%s'", byte_order))
end
end
---@return integer
function Buffer:u8()
return self:read_byte()
end
---@return integer
function Buffer:i8()
return bytes.tosigned(self:read_byte(), 8)
end
---@return integer
function Buffer:u16()
return self.e.u16(self:read(2))
end
---@return integer
function Buffer:i16()
return self.e.i16(self:read(2))
end
---@return integer
function Buffer:u32()
return self.e.u32(self:read(4))
end
---@return integer
function Buffer:i32()
return self.e.i32(self:read(4))
end
---@return integer
function Buffer:u64()
return self.e.u64(self:read(2))
end
---@return integer
function Buffer:i64()
return self.e.i64(self:read(2))
end
---@param n integer
---@return string
function Buffer:cstr(n)
local str = self:read(n)
local i = str:find("\00", 1, true)
if i then return str:sub(1, i - 1) else return str end
end
---Converts an integer to signed using twos complement.
---@param x integer the unsigned integer
---@param n integer the number of bits
---@return integer
function bytes.tosigned(x, n)
n = n or 8
-- if sign bit is set (128 - 255 for 8 bit)
if bit.band(x, bit.lshift(1, (n - 1))) ~= 0 then
return x - bit.lshift(1, n)
end
return x
end
---@param x integer
---@return integer
function bytes.low_byte(x)
return bit.band(x, 0xF)
end
---@param x integer
---@return integer
function bytes.high_byte(x)
return bit.rshift(x, 8)
end
---Little endian binary encoding.
---@type table
local le = {}
bytes.le = le
---@param buf string
---@return integer
function le.u16(buf)
local b1, b2 = string.byte(buf, 1, 2)
return bit.bor(
b1,
bit.lshift(b2, 8)
)
end
---@param buf string
---@return integer
function le.i16(buf)
return bytes.tosigned(le.u16(buf), 16)
end
---@param buf string
---@return integer
function le.u32(buf)
local b1, b2, b3, b4 = string.byte(buf, 1, 4)
return bit.bor(
b1,
bit.lshift(b2, 8),
bit.lshift(b3, 16),
bit.lshift(b4, 24)
)
end
---@param buf string
---@return integer
function le.i32(buf)
return bytes.tosigned(le.u32(buf), 32)
end
---@param buf string
---@return integer
function le.u64(buf)
local b1, b2, b3, b4, b5, b6, b7, b8 = string.byte(buf, 1, 8)
return bit.bor(
b1,
bit.lshift(b2, 8),
bit.lshift(b3, 16),
bit.lshift(b4, 24),
bit.lshift(b5, 32),
bit.lshift(b6, 40),
bit.lshift(b7, 48),
bit.lshift(b8, 56)
)
end
---@param buf string
---@return integer
function le.i64(buf)
return bytes.tosigned(le.u32(buf), 64)
end
---Big endian binary encoding.
---@type table
local be = {}
bytes.be = be
---@param buf string
---@return integer
function be.u16(buf)
return bit.bswap(le.u16(buf))
end
---@param buf string
---@return integer
function be.i16(buf)
return bytes.tosigned(le.i16(buf), 16)
end
---@param buf string
---@return integer
function be.u32(buf)
return bit.bswap(le.u32(buf))
end
---@param buf string
---@return integer
function be.i32(buf)
return bytes.tosigned(le.i32(buf), 32)
end
---@param buf string
---@return integer
function be.u64(buf)
return bit.bswap(le.u64(buf))
end
---@param buf string
---@return integer
function be.i64(buf)
return bytes.tosigned(le.i64(buf), 64)
end
return bytes