1 | /** |
2 | * Copyright (c) 2006-2023 LOVE Development Team |
3 | * |
4 | * This software is provided 'as-is', without any express or implied |
5 | * warranty. In no event will the authors be held liable for any damages |
6 | * arising from the use of this software. |
7 | * |
8 | * Permission is granted to anyone to use this software for any purpose, |
9 | * including commercial applications, and to alter it and redistribute it |
10 | * freely, subject to the following restrictions: |
11 | * |
12 | * 1. The origin of this software must not be misrepresented; you must not |
13 | * claim that you wrote the original software. If you use this software |
14 | * in a product, an acknowledgment in the product documentation would be |
15 | * appreciated but is not required. |
16 | * 2. Altered source versions must be plainly marked as such, and must not be |
17 | * misrepresented as being the original software. |
18 | * 3. This notice may not be removed or altered from any source distribution. |
19 | **/ |
20 | |
21 | // LOVE |
22 | #include "wrap_DataModule.h" |
23 | #include "wrap_Data.h" |
24 | #include "wrap_ByteData.h" |
25 | #include "wrap_DataView.h" |
26 | #include "wrap_CompressedData.h" |
27 | #include "DataModule.h" |
28 | #include "common/b64.h" |
29 | |
30 | // Lua 5.3 |
31 | #include "libraries/lua53/lstrlib.h" |
32 | |
33 | // C |
34 | #include <cmath> |
35 | #include <iostream> |
36 | #include <algorithm> |
37 | #include <limits> |
38 | |
39 | namespace love |
40 | { |
41 | namespace data |
42 | { |
43 | |
44 | #define instance() (Module::getInstance<DataModule>(Module::M_DATA)) |
45 | |
46 | ContainerType luax_checkcontainertype(lua_State *L, int idx) |
47 | { |
48 | const char *str = luaL_checkstring(L, idx); |
49 | ContainerType ctype = CONTAINER_STRING; |
50 | if (!getConstant(str, ctype)) |
51 | luax_enumerror(L, "container type" , getConstants(ctype), str); |
52 | return ctype; |
53 | } |
54 | |
55 | int w_newDataView(lua_State *L) |
56 | { |
57 | Data *data = luax_checkdata(L, 1); |
58 | |
59 | lua_Integer offset = luaL_checkinteger(L, 2); |
60 | lua_Integer size = luaL_checkinteger(L, 3); |
61 | |
62 | if (offset < 0 || size < 0) |
63 | return luaL_error(L, "DataView offset and size must not be negative." ); |
64 | |
65 | DataView *d; |
66 | luax_catchexcept(L, [&]() { d = instance()->newDataView(data, (size_t) offset, (size_t) size); }); |
67 | luax_pushtype(L, d); |
68 | d->release(); |
69 | |
70 | return 1; |
71 | } |
72 | |
73 | int w_newByteData(lua_State *L) |
74 | { |
75 | ByteData *d = nullptr; |
76 | |
77 | if (luax_istype(L, 1, Data::type)) |
78 | { |
79 | Data *data = luax_checkdata(L, 1); |
80 | |
81 | if (data->getSize() > std::numeric_limits<lua_Integer>::max()) |
82 | return luaL_error(L, "Data's size is too large!" ); |
83 | |
84 | lua_Integer offset = luaL_optinteger(L, 2, 0); |
85 | if (offset < 0) |
86 | return luaL_error(L, "Offset argument must not be negative." ); |
87 | |
88 | lua_Integer size = luaL_optinteger(L, 3, data->getSize() - offset); |
89 | if (size <= 0) |
90 | return luaL_error(L, "Size argument must be greater than zero." ); |
91 | else if ((size_t)(offset + size) > data->getSize()) |
92 | return luaL_error(L, "Offset and size arguments must fit within the given Data's size." ); |
93 | |
94 | const char *bytes = (const char *) data->getData() + offset; |
95 | luax_catchexcept(L, [&]() { d = instance()->newByteData(bytes, (size_t) size); }); |
96 | } |
97 | else if (lua_type(L, 1) == LUA_TSTRING) |
98 | { |
99 | size_t size = 0; |
100 | const char *data = luaL_checklstring(L, 1, &size); |
101 | luax_catchexcept(L, [&]() { d = instance()->newByteData(data, size); }); |
102 | } |
103 | else |
104 | { |
105 | lua_Integer size = luaL_checkinteger(L, 1); |
106 | if (size <= 0) |
107 | return luaL_error(L, "Data size must be a positive number." ); |
108 | luax_catchexcept(L, [&]() { d = instance()->newByteData((size_t) size); }); |
109 | } |
110 | |
111 | luax_pushtype(L, d); |
112 | d->release(); |
113 | return 1; |
114 | } |
115 | |
116 | int w_compress(lua_State *L) |
117 | { |
118 | ContainerType ctype = luax_checkcontainertype(L, 1); |
119 | |
120 | const char *fstr = luaL_checkstring(L, 2); |
121 | Compressor::Format format = Compressor::FORMAT_LZ4; |
122 | |
123 | if (!Compressor::getConstant(fstr, format)) |
124 | return luax_enumerror(L, "compressed data format" , Compressor::getConstants(format), fstr); |
125 | |
126 | int level = (int) luaL_optinteger(L, 4, -1); |
127 | size_t rawsize = 0; |
128 | const char *rawbytes = nullptr; |
129 | |
130 | if (lua_isstring(L, 3)) |
131 | rawbytes = luaL_checklstring(L, 3, &rawsize); |
132 | else |
133 | { |
134 | Data *rawdata = luax_checktype<Data>(L, 3); |
135 | rawsize = rawdata->getSize(); |
136 | rawbytes = (const char *) rawdata->getData(); |
137 | } |
138 | |
139 | CompressedData *cdata = nullptr; |
140 | luax_catchexcept(L, [&](){ cdata = compress(format, rawbytes, rawsize, level); }); |
141 | |
142 | if (ctype == CONTAINER_DATA) |
143 | luax_pushtype(L, cdata); |
144 | else |
145 | lua_pushlstring(L, (const char *) cdata->getData(), cdata->getSize()); |
146 | |
147 | cdata->release(); |
148 | return 1; |
149 | } |
150 | |
151 | int w_decompress(lua_State *L) |
152 | { |
153 | ContainerType ctype = luax_checkcontainertype(L, 1); |
154 | |
155 | char *rawbytes = nullptr; |
156 | size_t rawsize = 0; |
157 | |
158 | if (luax_istype(L, 2, CompressedData::type)) |
159 | { |
160 | CompressedData *data = luax_checkcompresseddata(L, 2); |
161 | rawsize = data->getDecompressedSize(); |
162 | luax_catchexcept(L, [&](){ rawbytes = decompress(data, rawsize); }); |
163 | } |
164 | else |
165 | { |
166 | Compressor::Format format = Compressor::FORMAT_LZ4; |
167 | const char *fstr = luaL_checkstring(L, 2); |
168 | |
169 | if (!Compressor::getConstant(fstr, format)) |
170 | return luax_enumerror(L, "compressed data format" , Compressor::getConstants(format), fstr); |
171 | |
172 | size_t compressedsize = 0; |
173 | const char *cbytes = nullptr; |
174 | |
175 | if (luax_istype(L, 3, Data::type)) |
176 | { |
177 | Data *data = luax_checktype<Data>(L, 3); |
178 | cbytes = (const char *) data->getData(); |
179 | compressedsize = data->getSize(); |
180 | } |
181 | else |
182 | cbytes = luaL_checklstring(L, 3, &compressedsize); |
183 | |
184 | luax_catchexcept(L, [&](){ rawbytes = decompress(format, cbytes, compressedsize, rawsize); }); |
185 | } |
186 | |
187 | if (ctype == CONTAINER_DATA) |
188 | { |
189 | ByteData *data = nullptr; |
190 | luax_catchexcept(L, [&]() { data = instance()->newByteData(rawbytes, rawsize, true); }); |
191 | luax_pushtype(L, Data::type, data); |
192 | data->release(); |
193 | } |
194 | else |
195 | { |
196 | lua_pushlstring(L, rawbytes, rawsize); |
197 | delete[] rawbytes; |
198 | } |
199 | |
200 | return 1; |
201 | } |
202 | |
203 | int w_encode(lua_State *L) |
204 | { |
205 | ContainerType ctype = luax_checkcontainertype(L, 1); |
206 | |
207 | const char *formatstr = luaL_checkstring(L, 2); |
208 | EncodeFormat format; |
209 | if (!getConstant(formatstr, format)) |
210 | return luax_enumerror(L, "encode format" , getConstants(format), formatstr); |
211 | |
212 | size_t srclen = 0; |
213 | const char *src = nullptr; |
214 | |
215 | if (luax_istype(L, 3, Data::type)) |
216 | { |
217 | Data *data = luax_totype<Data>(L, 3); |
218 | src = (const char *) data->getData(); |
219 | srclen = data->getSize(); |
220 | } |
221 | else |
222 | src = luaL_checklstring(L, 3, &srclen); |
223 | |
224 | size_t linelen = (size_t) luaL_optinteger(L, 4, 0); |
225 | |
226 | size_t dstlen = 0; |
227 | char *dst = nullptr; |
228 | luax_catchexcept(L, [&](){ dst = encode(format, src, srclen, dstlen, linelen); }); |
229 | |
230 | if (ctype == CONTAINER_DATA) |
231 | { |
232 | ByteData *data = nullptr; |
233 | if (dst != nullptr) |
234 | luax_catchexcept(L, [&]() { data = instance()->newByteData(dst, dstlen, true); }); |
235 | else |
236 | luax_catchexcept(L, [&]() { data = instance()->newByteData(0); }); |
237 | |
238 | luax_pushtype(L, Data::type, data); |
239 | data->release(); |
240 | } |
241 | else |
242 | { |
243 | if (dst != nullptr) |
244 | lua_pushlstring(L, dst, dstlen); |
245 | else |
246 | lua_pushstring(L, "" ); |
247 | |
248 | delete[] dst; |
249 | } |
250 | |
251 | return 1; |
252 | } |
253 | |
254 | int w_decode(lua_State *L) |
255 | { |
256 | ContainerType ctype = luax_checkcontainertype(L, 1); |
257 | |
258 | const char *formatstr = luaL_checkstring(L, 2); |
259 | EncodeFormat format; |
260 | if (!getConstant(formatstr, format)) |
261 | return luax_enumerror(L, "decode format" , getConstants(format), formatstr); |
262 | |
263 | size_t srclen = 0; |
264 | const char *src = nullptr; |
265 | |
266 | if (luax_istype(L, 3, Data::type)) |
267 | { |
268 | Data *data = luax_totype<Data>(L, 3); |
269 | src = (const char *) data->getData(); |
270 | srclen = data->getSize(); |
271 | } |
272 | else |
273 | src = luaL_checklstring(L, 3, &srclen); |
274 | |
275 | size_t dstlen = 0; |
276 | char *dst = nullptr; |
277 | luax_catchexcept(L, [&](){ dst = decode(format, src, srclen, dstlen); }); |
278 | |
279 | if (ctype == CONTAINER_DATA) |
280 | { |
281 | ByteData *data = nullptr; |
282 | if (dst != nullptr) |
283 | luax_catchexcept(L, [&]() { data = instance()->newByteData(dst, dstlen, true); }); |
284 | else |
285 | luax_catchexcept(L, [&]() { data = instance()->newByteData(0); }); |
286 | |
287 | luax_pushtype(L, Data::type, data); |
288 | data->release(); |
289 | } |
290 | else |
291 | { |
292 | if (dst != nullptr) |
293 | lua_pushlstring(L, dst, dstlen); |
294 | else |
295 | lua_pushstring(L, "" ); |
296 | |
297 | delete[] dst; |
298 | } |
299 | |
300 | return 1; |
301 | } |
302 | |
303 | int w_hash(lua_State *L) |
304 | { |
305 | const char *fstr = luaL_checkstring(L, 1); |
306 | HashFunction::Function function; |
307 | if (!HashFunction::getConstant(fstr, function)) |
308 | return luax_enumerror(L, "hash function" , HashFunction::getConstants(function), fstr); |
309 | |
310 | HashFunction::Value hashvalue; |
311 | if (lua_isstring(L, 2)) |
312 | { |
313 | size_t rawsize = 0; |
314 | const char *rawbytes = luaL_checklstring(L, 2, &rawsize); |
315 | luax_catchexcept(L, [&](){ love::data::hash(function, rawbytes, rawsize, hashvalue); }); |
316 | } |
317 | else |
318 | { |
319 | Data *rawdata = luax_checktype<Data>(L, 2); |
320 | luax_catchexcept(L, [&](){ love::data::hash(function, rawdata, hashvalue); }); |
321 | } |
322 | |
323 | lua_pushlstring(L, hashvalue.data, hashvalue.size); |
324 | return 1; |
325 | } |
326 | |
327 | int w_pack(lua_State *L) |
328 | { |
329 | ContainerType ctype = luax_checkcontainertype(L, 1); |
330 | const char *fmt = luaL_checkstring(L, 2); |
331 | luaL_Buffer_53 b; |
332 | lua53_str_pack(L, fmt, 3, &b); |
333 | |
334 | if (ctype == CONTAINER_DATA) |
335 | { |
336 | Data *d = nullptr; |
337 | luax_catchexcept(L, [&]() { d = instance()->newByteData(b.nelems); }); |
338 | memcpy(d->getData(), b.ptr, d->getSize()); |
339 | |
340 | lua53_cleanupbuffer(&b); |
341 | luax_pushtype(L, Data::type, d); |
342 | d->release(); |
343 | } |
344 | else |
345 | lua53_pushresult(&b); |
346 | |
347 | return 1; |
348 | } |
349 | |
350 | int w_unpack(lua_State *L) |
351 | { |
352 | const char *fmt = luaL_checkstring(L, 1); |
353 | |
354 | const char *data = nullptr; |
355 | size_t datasize = 0; |
356 | |
357 | if (luax_istype(L, 2, Data::type)) |
358 | { |
359 | Data *d = luax_checkdata(L, 2); |
360 | data = (const char *) d->getData(); |
361 | datasize = d->getSize(); |
362 | } |
363 | else |
364 | data = luaL_checklstring(L, 2, &datasize); |
365 | |
366 | return lua53_str_unpack(L, fmt, data, datasize, 2, 3); |
367 | } |
368 | |
369 | // List of functions to wrap. |
370 | static const luaL_Reg functions[] = |
371 | { |
372 | { "newDataView" , w_newDataView }, |
373 | { "newByteData" , w_newByteData }, |
374 | { "compress" , w_compress }, |
375 | { "decompress" , w_decompress }, |
376 | { "encode" , w_encode }, |
377 | { "decode" , w_decode }, |
378 | { "hash" , w_hash }, |
379 | |
380 | { "pack" , w_pack }, |
381 | { "unpack" , w_unpack }, |
382 | { "getPackedSize" , lua53_str_packsize }, |
383 | |
384 | { 0, 0 } |
385 | }; |
386 | |
387 | static const lua_CFunction types[] = |
388 | { |
389 | luaopen_data, |
390 | luaopen_bytedata, |
391 | luaopen_dataview, |
392 | luaopen_compresseddata, |
393 | nullptr |
394 | }; |
395 | |
396 | extern "C" int luaopen_love_data(lua_State *L) |
397 | { |
398 | DataModule *instance = instance(); |
399 | if (instance == nullptr) |
400 | { |
401 | luax_catchexcept(L, [&](){ instance = new DataModule(); }); |
402 | } |
403 | else |
404 | instance->retain(); |
405 | |
406 | WrappedModule w; |
407 | w.module = instance; |
408 | w.name = "data" ; |
409 | w.type = &Module::type; |
410 | w.functions = functions; |
411 | w.types = types; |
412 | |
413 | int n = luax_register_module(L, w); |
414 | return n; |
415 | } |
416 | |
417 | } // data |
418 | } // love |
419 | |