Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/VM/src/lbuflib.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "lualib.h"
3
4
#include "lcommon.h"
5
#include "lbuffer.h"
6
7
#if defined(LUAU_BIG_ENDIAN)
8
#include <endian.h>
9
#endif
10
11
LUAU_FASTFLAG(LuauIntegerType)
12
LUAU_FASTFLAG(LuauIntegerLibrary)
13
14
#include <string.h>
15
16
// while C API returns 'size_t' for binary compatibility in case of future extensions,
17
// in the current implementation, length and offset are limited to 31 bits
18
// because offset is limited to an integer, a single 64bit comparison can be used and will not overflow
19
#define isoutofbounds(offset, len, accessize) (uint64_t(unsigned(offset)) + (accessize) > uint64_t(len))
20
21
static_assert(MAX_BUFFER_SIZE <= INT_MAX, "current implementation can't handle a larger limit");
22
23
#if defined(LUAU_BIG_ENDIAN)
24
template<typename T>
25
inline T buffer_swapbe(T v)
26
{
27
if (sizeof(T) == 8)
28
return htole64(v);
29
else if (sizeof(T) == 4)
30
return htole32(v);
31
else if (sizeof(T) == 2)
32
return htole16(v);
33
else
34
return v;
35
}
36
#endif
37
38
static int buffer_create(lua_State* L)
39
{
40
int size = luaL_checkinteger(L, 1);
41
42
luaL_argcheck(L, size >= 0, 1, "size");
43
44
lua_newbuffer(L, size);
45
return 1;
46
}
47
48
static int buffer_fromstring(lua_State* L)
49
{
50
size_t len = 0;
51
const char* val = luaL_checklstring(L, 1, &len);
52
53
void* data = lua_newbuffer(L, len);
54
memcpy(data, val, len);
55
return 1;
56
}
57
58
static int buffer_tostring(lua_State* L)
59
{
60
size_t len = 0;
61
void* data = luaL_checkbuffer(L, 1, &len);
62
63
lua_pushlstring(L, (char*)data, len);
64
return 1;
65
}
66
67
template<typename T>
68
static int buffer_readinteger(lua_State* L)
69
{
70
size_t len = 0;
71
void* buf = luaL_checkbuffer(L, 1, &len);
72
int offset = luaL_checkinteger(L, 2);
73
74
if (isoutofbounds(offset, len, sizeof(T)))
75
luaL_error(L, "buffer access out of bounds");
76
77
T val;
78
memcpy(&val, (char*)buf + offset, sizeof(T));
79
80
#if defined(LUAU_BIG_ENDIAN)
81
val = buffer_swapbe(val);
82
#endif
83
84
lua_pushnumber(L, double(val));
85
return 1;
86
}
87
88
template<typename T>
89
static int buffer_writeinteger(lua_State* L)
90
{
91
size_t len = 0;
92
void* buf = luaL_checkbuffer(L, 1, &len);
93
int offset = luaL_checkinteger(L, 2);
94
int value = luaL_checkunsigned(L, 3);
95
96
if (isoutofbounds(offset, len, sizeof(T)))
97
luaL_error(L, "buffer access out of bounds");
98
99
T val = T(value);
100
101
#if defined(LUAU_BIG_ENDIAN)
102
val = buffer_swapbe(val);
103
#endif
104
105
memcpy((char*)buf + offset, &val, sizeof(T));
106
return 0;
107
}
108
109
static int buffer_readlong(lua_State* L)
110
{
111
size_t len = 0;
112
void* buf = luaL_checkbuffer(L, 1, &len);
113
int offset = luaL_checkinteger(L, 2);
114
115
if (isoutofbounds(offset, len, sizeof(uint64_t)))
116
luaL_error(L, "buffer access out of bounds");
117
118
int64_t val;
119
memcpy(&val, (char*)buf + offset, sizeof(int64_t));
120
121
#if defined(LUAU_BIG_ENDIAN)
122
val = buffer_swapbe(val);
123
#endif
124
125
lua_pushinteger64(L, val);
126
return 1;
127
}
128
129
static int buffer_writelong(lua_State* L)
130
{
131
size_t len = 0;
132
void* buf = luaL_checkbuffer(L, 1, &len);
133
int offset = luaL_checkinteger(L, 2);
134
int64_t value = luaL_checkinteger64(L, 3);
135
136
if (isoutofbounds(offset, len, sizeof(int64_t)))
137
luaL_error(L, "buffer access out of bounds");
138
139
#if defined(LUAU_BIG_ENDIAN)
140
value = buffer_swapbe(value);
141
#endif
142
143
memcpy((char*)buf + offset, &value, sizeof(int64_t));
144
return 0;
145
}
146
147
template<typename T, typename StorageType>
148
static int buffer_readfp(lua_State* L)
149
{
150
size_t len = 0;
151
void* buf = luaL_checkbuffer(L, 1, &len);
152
int offset = luaL_checkinteger(L, 2);
153
154
if (isoutofbounds(offset, len, sizeof(T)))
155
luaL_error(L, "buffer access out of bounds");
156
157
T val;
158
159
#if defined(LUAU_BIG_ENDIAN)
160
static_assert(sizeof(T) == sizeof(StorageType), "type size must match to reinterpret data");
161
StorageType tmp;
162
memcpy(&tmp, (char*)buf + offset, sizeof(tmp));
163
tmp = buffer_swapbe(tmp);
164
165
memcpy(&val, &tmp, sizeof(tmp));
166
#else
167
memcpy(&val, (char*)buf + offset, sizeof(T));
168
#endif
169
170
lua_pushnumber(L, double(val));
171
return 1;
172
}
173
174
template<typename T, typename StorageType>
175
static int buffer_writefp(lua_State* L)
176
{
177
size_t len = 0;
178
void* buf = luaL_checkbuffer(L, 1, &len);
179
int offset = luaL_checkinteger(L, 2);
180
double value = luaL_checknumber(L, 3);
181
182
if (isoutofbounds(offset, len, sizeof(T)))
183
luaL_error(L, "buffer access out of bounds");
184
185
T val = T(value);
186
187
#if defined(LUAU_BIG_ENDIAN)
188
static_assert(sizeof(T) == sizeof(StorageType), "type size must match to reinterpret data");
189
StorageType tmp;
190
memcpy(&tmp, &val, sizeof(tmp));
191
tmp = buffer_swapbe(tmp);
192
193
memcpy((char*)buf + offset, &tmp, sizeof(tmp));
194
#else
195
memcpy((char*)buf + offset, &val, sizeof(T));
196
#endif
197
198
return 0;
199
}
200
201
static int buffer_readstring(lua_State* L)
202
{
203
size_t len = 0;
204
void* buf = luaL_checkbuffer(L, 1, &len);
205
int offset = luaL_checkinteger(L, 2);
206
int size = luaL_checkinteger(L, 3);
207
208
luaL_argcheck(L, size >= 0, 3, "size");
209
210
if (isoutofbounds(offset, len, unsigned(size)))
211
luaL_error(L, "buffer access out of bounds");
212
213
lua_pushlstring(L, (char*)buf + offset, size);
214
return 1;
215
}
216
217
static int buffer_writestring(lua_State* L)
218
{
219
size_t len = 0;
220
void* buf = luaL_checkbuffer(L, 1, &len);
221
int offset = luaL_checkinteger(L, 2);
222
size_t size = 0;
223
const char* val = luaL_checklstring(L, 3, &size);
224
int count = luaL_optinteger(L, 4, int(size));
225
226
luaL_argcheck(L, count >= 0, 4, "count");
227
228
if (size_t(count) > size)
229
luaL_error(L, "string length overflow");
230
231
// string size can't exceed INT_MAX at this point
232
if (isoutofbounds(offset, len, unsigned(count)))
233
luaL_error(L, "buffer access out of bounds");
234
235
memcpy((char*)buf + offset, val, count);
236
return 0;
237
}
238
239
static int buffer_len(lua_State* L)
240
{
241
size_t len = 0;
242
luaL_checkbuffer(L, 1, &len);
243
244
lua_pushnumber(L, double(unsigned(len)));
245
return 1;
246
}
247
248
static int buffer_copy(lua_State* L)
249
{
250
size_t tlen = 0;
251
void* tbuf = luaL_checkbuffer(L, 1, &tlen);
252
int toffset = luaL_checkinteger(L, 2);
253
254
size_t slen = 0;
255
void* sbuf = luaL_checkbuffer(L, 3, &slen);
256
int soffset = luaL_optinteger(L, 4, 0);
257
258
int size = luaL_optinteger(L, 5, int(slen) - soffset);
259
260
if (size < 0)
261
luaL_error(L, "buffer access out of bounds");
262
263
if (isoutofbounds(soffset, slen, unsigned(size)))
264
luaL_error(L, "buffer access out of bounds");
265
266
if (isoutofbounds(toffset, tlen, unsigned(size)))
267
luaL_error(L, "buffer access out of bounds");
268
269
memmove((char*)tbuf + toffset, (char*)sbuf + soffset, size);
270
return 0;
271
}
272
273
static int buffer_fill(lua_State* L)
274
{
275
size_t len = 0;
276
void* buf = luaL_checkbuffer(L, 1, &len);
277
int offset = luaL_checkinteger(L, 2);
278
unsigned value = luaL_checkunsigned(L, 3);
279
int size = luaL_optinteger(L, 4, int(len) - offset);
280
281
if (size < 0)
282
luaL_error(L, "buffer access out of bounds");
283
284
if (isoutofbounds(offset, len, unsigned(size)))
285
luaL_error(L, "buffer access out of bounds");
286
287
memset((char*)buf + offset, value & 0xff, size);
288
return 0;
289
}
290
291
static int buffer_readbits(lua_State* L)
292
{
293
size_t len = 0;
294
void* buf = luaL_checkbuffer(L, 1, &len);
295
int64_t bitoffset = (int64_t)luaL_checknumber(L, 2);
296
int bitcount = luaL_checkinteger(L, 3);
297
298
if (bitoffset < 0)
299
luaL_error(L, "buffer access out of bounds");
300
301
if (unsigned(bitcount) > 32)
302
luaL_error(L, "bit count is out of range of [0; 32]");
303
304
if (uint64_t(bitoffset + bitcount) > uint64_t(len) * 8)
305
luaL_error(L, "buffer access out of bounds");
306
307
unsigned startbyte = unsigned(bitoffset / 8);
308
unsigned endbyte = unsigned((bitoffset + bitcount + 7) / 8);
309
310
uint64_t data = 0;
311
312
#if defined(LUAU_BIG_ENDIAN)
313
for (int i = int(endbyte) - 1; i >= int(startbyte); i--)
314
data = (data << 8) + uint8_t(((char*)buf)[i]);
315
#else
316
memcpy(&data, (char*)buf + startbyte, endbyte - startbyte);
317
#endif
318
319
uint64_t subbyteoffset = bitoffset & 0x7;
320
uint64_t mask = (1ull << bitcount) - 1;
321
322
lua_pushunsigned(L, unsigned((data >> subbyteoffset) & mask));
323
return 1;
324
}
325
326
static int buffer_writebits(lua_State* L)
327
{
328
size_t len = 0;
329
void* buf = luaL_checkbuffer(L, 1, &len);
330
int64_t bitoffset = (int64_t)luaL_checknumber(L, 2);
331
int bitcount = luaL_checkinteger(L, 3);
332
unsigned value = luaL_checkunsigned(L, 4);
333
334
if (bitoffset < 0)
335
luaL_error(L, "buffer access out of bounds");
336
337
if (unsigned(bitcount) > 32)
338
luaL_error(L, "bit count is out of range of [0; 32]");
339
340
if (uint64_t(bitoffset + bitcount) > uint64_t(len) * 8)
341
luaL_error(L, "buffer access out of bounds");
342
343
unsigned startbyte = unsigned(bitoffset / 8);
344
unsigned endbyte = unsigned((bitoffset + bitcount + 7) / 8);
345
346
uint64_t data = 0;
347
348
#if defined(LUAU_BIG_ENDIAN)
349
for (int i = int(endbyte) - 1; i >= int(startbyte); i--)
350
data = data * 256 + uint8_t(((char*)buf)[i]);
351
#else
352
memcpy(&data, (char*)buf + startbyte, endbyte - startbyte);
353
#endif
354
355
uint64_t subbyteoffset = bitoffset & 0x7;
356
uint64_t mask = ((1ull << bitcount) - 1) << subbyteoffset;
357
358
data = (data & ~mask) | ((uint64_t(value) << subbyteoffset) & mask);
359
360
#if defined(LUAU_BIG_ENDIAN)
361
for (int i = int(startbyte); i < int(endbyte); i++)
362
{
363
((char*)buf)[i] = data & 0xff;
364
data >>= 8;
365
}
366
#else
367
memcpy((char*)buf + startbyte, &data, endbyte - startbyte);
368
#endif
369
return 0;
370
}
371
372
static const luaL_Reg bufferlib[] = {
373
{"create", buffer_create},
374
{"fromstring", buffer_fromstring},
375
{"tostring", buffer_tostring},
376
{"readi8", buffer_readinteger<int8_t>},
377
{"readu8", buffer_readinteger<uint8_t>},
378
{"readi16", buffer_readinteger<int16_t>},
379
{"readu16", buffer_readinteger<uint16_t>},
380
{"readi32", buffer_readinteger<int32_t>},
381
{"readu32", buffer_readinteger<uint32_t>},
382
{"readf32", buffer_readfp<float, uint32_t>},
383
{"readf64", buffer_readfp<double, uint64_t>},
384
{"writei8", buffer_writeinteger<int8_t>},
385
{"writeu8", buffer_writeinteger<uint8_t>},
386
{"writei16", buffer_writeinteger<int16_t>},
387
{"writeu16", buffer_writeinteger<uint16_t>},
388
{"writei32", buffer_writeinteger<int32_t>},
389
{"writeu32", buffer_writeinteger<uint32_t>},
390
{"writef32", buffer_writefp<float, uint32_t>},
391
{"writef64", buffer_writefp<double, uint64_t>},
392
{"readstring", buffer_readstring},
393
{"writestring", buffer_writestring},
394
{"len", buffer_len},
395
{"copy", buffer_copy},
396
{"fill", buffer_fill},
397
{"readbits", buffer_readbits},
398
{"writebits", buffer_writebits},
399
{"readinteger", buffer_readlong},
400
{"writeinteger", buffer_writelong},
401
{NULL, NULL},
402
};
403
404
static const luaL_Reg bufferlib_NOINTEGER[] = {
405
{"create", buffer_create},
406
{"fromstring", buffer_fromstring},
407
{"tostring", buffer_tostring},
408
{"readi8", buffer_readinteger<int8_t>},
409
{"readu8", buffer_readinteger<uint8_t>},
410
{"readi16", buffer_readinteger<int16_t>},
411
{"readu16", buffer_readinteger<uint16_t>},
412
{"readi32", buffer_readinteger<int32_t>},
413
{"readu32", buffer_readinteger<uint32_t>},
414
{"readf32", buffer_readfp<float, uint32_t>},
415
{"readf64", buffer_readfp<double, uint64_t>},
416
{"writei8", buffer_writeinteger<int8_t>},
417
{"writeu8", buffer_writeinteger<uint8_t>},
418
{"writei16", buffer_writeinteger<int16_t>},
419
{"writeu16", buffer_writeinteger<uint16_t>},
420
{"writei32", buffer_writeinteger<int32_t>},
421
{"writeu32", buffer_writeinteger<uint32_t>},
422
{"writef32", buffer_writefp<float, uint32_t>},
423
{"writef64", buffer_writefp<double, uint64_t>},
424
{"readstring", buffer_readstring},
425
{"writestring", buffer_writestring},
426
{"len", buffer_len},
427
{"copy", buffer_copy},
428
{"fill", buffer_fill},
429
{"readbits", buffer_readbits},
430
{"writebits", buffer_writebits},
431
{NULL, NULL},
432
};
433
434
int luaopen_buffer(lua_State* L)
435
{
436
if (FFlag::LuauIntegerType && FFlag::LuauIntegerLibrary)
437
luaL_register(L, LUA_BUFFERLIBNAME, bufferlib);
438
else
439
luaL_register(L, LUA_BUFFERLIBNAME, bufferlib_NOINTEGER);
440
441
return 1;
442
}
443
444