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
39namespace love
40{
41namespace data
42{
43
44#define instance() (Module::getInstance<DataModule>(Module::M_DATA))
45
46ContainerType 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
55int 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
73int 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
116int 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
151int 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
203int 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
254int 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
303int 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
327int 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
350int 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.
370static 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
387static const lua_CFunction types[] =
388{
389 luaopen_data,
390 luaopen_bytedata,
391 luaopen_dataview,
392 luaopen_compresseddata,
393 nullptr
394};
395
396extern "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