mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Add love.data module.
- Moved love.math.compress / decompress / decode / encode / hash to love.data. - Changed love.data.compress/decompress to take the format argument first instead of second. - Added love.data.newDataView. Returns a read-only subsection of an existing Data object. - Added love.data.newByteData. Mostly useful in combination with LuaJIT's FFI as it has no extra methods currently. - Added implementations of Lua 5.3's data packing APIs: love.data.packString / packData / unpack / getPackedSize. Resolves issue #1336. Resolves issue #1331. --HG-- branch : minor
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
** $Id: lprefix.h,v 1.2 2014/12/29 16:54:13 roberto Exp $
|
||||
** Definitions for Lua code that must come before any other header file
|
||||
** See Copyright Notice in lutf8lib.c
|
||||
*/
|
||||
|
||||
#ifndef lprefix_h
|
||||
#define lprefix_h
|
||||
|
||||
|
||||
/*
|
||||
** Allows POSIX/XSI stuff
|
||||
*/
|
||||
#if !defined(LUA_USE_C89) /* { */
|
||||
|
||||
#if !defined(_XOPEN_SOURCE)
|
||||
#define _XOPEN_SOURCE 600
|
||||
#elif _XOPEN_SOURCE == 0
|
||||
#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Allows manipulation of large files in gcc and some other compilers
|
||||
*/
|
||||
#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS)
|
||||
#define _LARGEFILE_SOURCE 1
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** Windows stuff
|
||||
*/
|
||||
#if defined(_WIN32) /* { */
|
||||
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */
|
||||
#endif
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif
|
||||
|
||||
Executable
+585
@@ -0,0 +1,585 @@
|
||||
/*
|
||||
** $Id: lstrlib.c,v 1.254 2016/12/22 13:08:50 roberto Exp $
|
||||
** Standard library for string operations and pattern-matching
|
||||
** Modified by the Kepler Project and the LOVE Development Team to work with
|
||||
** Lua 5.1's API
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Kepler Project.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
|
||||
/*********************************************************************
|
||||
* This file contains parts of Lua 5.2's and Lua 5.3's source code:
|
||||
*
|
||||
* Copyright (C) 1994-2014 Lua.org, PUC-Rio.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <ctype.h>
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "lstrlib.h"
|
||||
|
||||
typedef size_t lua_Unsigned;
|
||||
|
||||
|
||||
static void luaL_buffinit_53 (lua_State *L, luaL_Buffer_53 *B) {
|
||||
/* make it crash if used via pointer to a 5.1-style luaL_Buffer */
|
||||
B->b.p = NULL;
|
||||
B->b.L = NULL;
|
||||
B->b.lvl = 0;
|
||||
/* reuse the buffer from the 5.1-style luaL_Buffer though! */
|
||||
B->ptr = B->b.buffer;
|
||||
B->capacity = LUAL_BUFFERSIZE;
|
||||
B->nelems = 0;
|
||||
B->L2 = L;
|
||||
}
|
||||
|
||||
|
||||
static char *luaL_prepbuffsize_53 (luaL_Buffer_53 *B, size_t s) {
|
||||
if (B->capacity - B->nelems < s) { /* needs to grow */
|
||||
char* newptr = NULL;
|
||||
size_t newcap = B->capacity * 2;
|
||||
if (newcap - B->nelems < s)
|
||||
newcap = B->nelems + s;
|
||||
if (newcap < B->capacity) /* overflow */
|
||||
luaL_error(B->L2, "buffer too large");
|
||||
newptr = (char*)lua_newuserdata(B->L2, newcap);
|
||||
memcpy(newptr, B->ptr, B->nelems);
|
||||
if (B->ptr != B->b.buffer)
|
||||
lua_replace(B->L2, -2); /* remove old buffer */
|
||||
B->ptr = newptr;
|
||||
B->capacity = newcap;
|
||||
}
|
||||
return B->ptr+B->nelems;
|
||||
}
|
||||
|
||||
|
||||
#define luaL_addsize_53(B, s) \
|
||||
((B)->nelems += (s))
|
||||
|
||||
#define luaL_addchar_53(B, c) \
|
||||
((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize_53((B), 1)), \
|
||||
((B)->ptr[(B)->nelems++] = (c)))
|
||||
|
||||
|
||||
static void luaL_addlstring_53 (luaL_Buffer_53 *B, const char *s, size_t l) {
|
||||
memcpy(luaL_prepbuffsize_53(B, l), s, l);
|
||||
luaL_addsize_53(B, l);
|
||||
}
|
||||
|
||||
|
||||
void lua53_pushresult (luaL_Buffer_53 *B) {
|
||||
lua_pushlstring(B->L2, B->ptr, B->nelems);
|
||||
if (B->ptr != B->b.buffer)
|
||||
lua_replace(B->L2, -2); /* remove userdata buffer */
|
||||
}
|
||||
|
||||
void lua53_cleanupbuffer (luaL_Buffer_53 *B) {
|
||||
if (B->ptr != B->b.buffer)
|
||||
lua_replace(B->L2, -1); /* remove userdata buffer */
|
||||
}
|
||||
|
||||
/*
|
||||
** Some sizes are better limited to fit in 'int', but must also fit in
|
||||
** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.)
|
||||
*/
|
||||
#define MAX_SIZET ((size_t)(~(size_t)0))
|
||||
|
||||
#define MAXSIZE \
|
||||
(sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX))
|
||||
|
||||
|
||||
/* translate a relative string position: negative means back from end */
|
||||
static lua_Integer posrelat (lua_Integer pos, size_t len) {
|
||||
if (pos >= 0) return pos;
|
||||
else if (0u - (size_t)pos > len) return 0;
|
||||
else return (lua_Integer)len + pos + 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** PACK/UNPACK
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
/* value used for padding */
|
||||
#if !defined(LUAL_PACKPADBYTE)
|
||||
#define LUAL_PACKPADBYTE 0x00
|
||||
#endif
|
||||
|
||||
/* maximum size for the binary representation of an integer */
|
||||
#define MAXINTSIZE 16
|
||||
|
||||
/* number of bits in a character */
|
||||
#define NB CHAR_BIT
|
||||
|
||||
/* mask for one character (NB 1's) */
|
||||
#define MC ((1 << NB) - 1)
|
||||
|
||||
/* size of a lua_Integer */
|
||||
#define SZINT ((int)sizeof(lua_Integer))
|
||||
|
||||
|
||||
/* dummy union to get native endianness */
|
||||
static const union {
|
||||
int dummy;
|
||||
char little; /* true iff machine is little endian */
|
||||
} nativeendian = {1};
|
||||
|
||||
|
||||
/* dummy structure to get native alignment requirements */
|
||||
struct cD {
|
||||
char c;
|
||||
union { double d; void *p; lua_Integer i; lua_Number n; } u;
|
||||
};
|
||||
|
||||
#define MAXALIGN (offsetof(struct cD, u))
|
||||
|
||||
|
||||
/*
|
||||
** Union for serializing floats
|
||||
*/
|
||||
typedef union Ftypes {
|
||||
float f;
|
||||
double d;
|
||||
lua_Number n;
|
||||
char buff[5 * sizeof(lua_Number)]; /* enough for any float type */
|
||||
} Ftypes;
|
||||
|
||||
|
||||
/*
|
||||
** information to pack/unpack stuff
|
||||
*/
|
||||
typedef struct Header {
|
||||
lua_State *L;
|
||||
int islittle;
|
||||
int maxalign;
|
||||
} Header;
|
||||
|
||||
|
||||
/*
|
||||
** options for pack/unpack
|
||||
*/
|
||||
typedef enum KOption {
|
||||
Kint, /* signed integers */
|
||||
Kuint, /* unsigned integers */
|
||||
Kfloat, /* floating-point numbers */
|
||||
Kchar, /* fixed-length strings */
|
||||
Kstring, /* strings with prefixed length */
|
||||
Kzstr, /* zero-terminated strings */
|
||||
Kpadding, /* padding */
|
||||
Kpaddalign, /* padding for alignment */
|
||||
Knop /* no-op (configuration or spaces) */
|
||||
} KOption;
|
||||
|
||||
|
||||
/*
|
||||
** Read an integer numeral from string 'fmt' or return 'df' if
|
||||
** there is no numeral
|
||||
*/
|
||||
static int digit (int c) { return '0' <= c && c <= '9'; }
|
||||
|
||||
static int getnum (const char **fmt, int df) {
|
||||
if (!digit(**fmt)) /* no number? */
|
||||
return df; /* return default value */
|
||||
else {
|
||||
int a = 0;
|
||||
do {
|
||||
a = a*10 + (*((*fmt)++) - '0');
|
||||
} while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10);
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read an integer numeral and raises an error if it is larger
|
||||
** than the maximum size for integers.
|
||||
*/
|
||||
static int getnumlimit (Header *h, const char **fmt, int df) {
|
||||
int sz = getnum(fmt, df);
|
||||
if (sz > MAXINTSIZE || sz <= 0)
|
||||
luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
|
||||
sz, MAXINTSIZE);
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Initialize Header
|
||||
*/
|
||||
static void initheader (lua_State *L, Header *h) {
|
||||
h->L = L;
|
||||
h->islittle = nativeendian.little;
|
||||
h->maxalign = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read and classify next option. 'size' is filled with option's size.
|
||||
*/
|
||||
static KOption getoption (Header *h, const char **fmt, int *size) {
|
||||
int opt = *((*fmt)++);
|
||||
*size = 0; /* default */
|
||||
switch (opt) {
|
||||
case 'b': *size = sizeof(char); return Kint;
|
||||
case 'B': *size = sizeof(char); return Kuint;
|
||||
case 'h': *size = sizeof(short); return Kint;
|
||||
case 'H': *size = sizeof(short); return Kuint;
|
||||
case 'l': *size = sizeof(long); return Kint;
|
||||
case 'L': *size = sizeof(long); return Kuint;
|
||||
case 'j': *size = sizeof(lua_Integer); return Kint;
|
||||
case 'J': *size = sizeof(lua_Integer); return Kuint;
|
||||
case 'T': *size = sizeof(size_t); return Kuint;
|
||||
case 'f': *size = sizeof(float); return Kfloat;
|
||||
case 'd': *size = sizeof(double); return Kfloat;
|
||||
case 'n': *size = sizeof(lua_Number); return Kfloat;
|
||||
case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint;
|
||||
case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
|
||||
case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
|
||||
case 'c':
|
||||
*size = getnum(fmt, -1);
|
||||
if (*size == -1)
|
||||
luaL_error(h->L, "missing size for format option 'c'");
|
||||
return Kchar;
|
||||
case 'z': return Kzstr;
|
||||
case 'x': *size = 1; return Kpadding;
|
||||
case 'X': return Kpaddalign;
|
||||
case ' ': break;
|
||||
case '<': h->islittle = 1; break;
|
||||
case '>': h->islittle = 0; break;
|
||||
case '=': h->islittle = nativeendian.little; break;
|
||||
case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break;
|
||||
default: luaL_error(h->L, "invalid format option '%c'", opt);
|
||||
}
|
||||
return Knop;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read, classify, and fill other details about the next option.
|
||||
** 'psize' is filled with option's size, 'notoalign' with its
|
||||
** alignment requirements.
|
||||
** Local variable 'size' gets the size to be aligned. (Kpadal option
|
||||
** always gets its full alignment, other options are limited by
|
||||
** the maximum alignment ('maxalign'). Kchar option needs no alignment
|
||||
** despite its size.
|
||||
*/
|
||||
static KOption getdetails (Header *h, size_t totalsize,
|
||||
const char **fmt, int *psize, int *ntoalign) {
|
||||
KOption opt = getoption(h, fmt, psize);
|
||||
int align = *psize; /* usually, alignment follows size */
|
||||
if (opt == Kpaddalign) { /* 'X' gets alignment from following option */
|
||||
if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0)
|
||||
luaL_argerror(h->L, 1, "invalid next option for option 'X'");
|
||||
}
|
||||
if (align <= 1 || opt == Kchar) /* need no alignment? */
|
||||
*ntoalign = 0;
|
||||
else {
|
||||
if (align > h->maxalign) /* enforce maximum alignment */
|
||||
align = h->maxalign;
|
||||
if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */
|
||||
luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
|
||||
*ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Pack integer 'n' with 'size' bytes and 'islittle' endianness.
|
||||
** The final 'if' handles the case when 'size' is larger than
|
||||
** the size of a Lua integer, correcting the extra sign-extension
|
||||
** bytes if necessary (by default they would be zeros).
|
||||
*/
|
||||
static void packint (luaL_Buffer_53 *b, lua_Unsigned n,
|
||||
int islittle, int size, int neg) {
|
||||
char *buff = luaL_prepbuffsize_53(b, size);
|
||||
int i;
|
||||
buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */
|
||||
for (i = 1; i < size; i++) {
|
||||
n >>= NB;
|
||||
buff[islittle ? i : size - 1 - i] = (char)(n & MC);
|
||||
}
|
||||
if (neg && size > SZINT) { /* negative number need sign extension? */
|
||||
for (i = SZINT; i < size; i++) /* correct extra bytes */
|
||||
buff[islittle ? i : size - 1 - i] = (char)MC;
|
||||
}
|
||||
luaL_addsize_53(b, size); /* add result to buffer */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Copy 'size' bytes from 'src' to 'dest', correcting endianness if
|
||||
** given 'islittle' is different from native endianness.
|
||||
*/
|
||||
static void copywithendian (volatile char *dest, volatile const char *src,
|
||||
int size, int islittle) {
|
||||
if (islittle == nativeendian.little) {
|
||||
while (size-- != 0)
|
||||
*(dest++) = *(src++);
|
||||
}
|
||||
else {
|
||||
dest += size - 1;
|
||||
while (size-- != 0)
|
||||
*(dest--) = *(src++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void lua53_str_pack (lua_State *L, const char *fmt, int startidx, luaL_Buffer_53 *b) {
|
||||
Header h;
|
||||
int arg = startidx - 1; /* current argument to pack */
|
||||
size_t totalsize = 0; /* accumulate total size of result */
|
||||
initheader(L, &h);
|
||||
lua_pushnil(L); /* mark to separate arguments from string buffer */
|
||||
luaL_buffinit_53(L, b);
|
||||
while (*fmt != '\0') {
|
||||
int size, ntoalign;
|
||||
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
|
||||
totalsize += ntoalign + size;
|
||||
while (ntoalign-- > 0)
|
||||
luaL_addchar_53(b, LUAL_PACKPADBYTE); /* fill alignment */
|
||||
arg++;
|
||||
switch (opt) {
|
||||
case Kint: { /* signed integers */
|
||||
lua_Integer n = luaL_checkinteger(L, arg);
|
||||
if (size < SZINT) { /* need overflow check? */
|
||||
lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1);
|
||||
luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow");
|
||||
}
|
||||
packint(b, (lua_Unsigned)n, h.islittle, size, (n < 0));
|
||||
break;
|
||||
}
|
||||
case Kuint: { /* unsigned integers */
|
||||
lua_Integer n = luaL_checkinteger(L, arg);
|
||||
if (size < SZINT) /* need overflow check? */
|
||||
luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)),
|
||||
arg, "unsigned overflow");
|
||||
packint(b, (lua_Unsigned)n, h.islittle, size, 0);
|
||||
break;
|
||||
}
|
||||
case Kfloat: { /* floating-point options */
|
||||
volatile Ftypes u;
|
||||
char *buff = luaL_prepbuffsize_53(b, size);
|
||||
lua_Number n = luaL_checknumber(L, arg); /* get argument */
|
||||
if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */
|
||||
else if (size == sizeof(u.d)) u.d = (double)n;
|
||||
else u.n = n;
|
||||
/* move 'u' to final result, correcting endianness if needed */
|
||||
copywithendian(buff, u.buff, size, h.islittle);
|
||||
luaL_addsize_53(b, size);
|
||||
break;
|
||||
}
|
||||
case Kchar: { /* fixed-size string */
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, arg, &len);
|
||||
luaL_argcheck(L, len <= (size_t)size, arg,
|
||||
"string longer than given size");
|
||||
luaL_addlstring_53(b, s, len); /* add string */
|
||||
while (len++ < (size_t)size) /* pad extra space */
|
||||
luaL_addchar_53(b, LUAL_PACKPADBYTE);
|
||||
break;
|
||||
}
|
||||
case Kstring: { /* strings with length count */
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, arg, &len);
|
||||
luaL_argcheck(L, size >= (int)sizeof(size_t) ||
|
||||
len < ((size_t)1 << (size * NB)),
|
||||
arg, "string length does not fit in given size");
|
||||
packint(b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */
|
||||
luaL_addlstring_53(b, s, len);
|
||||
totalsize += len;
|
||||
break;
|
||||
}
|
||||
case Kzstr: { /* zero-terminated string */
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, arg, &len);
|
||||
luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros");
|
||||
luaL_addlstring_53(b, s, len);
|
||||
luaL_addchar_53(b, '\0'); /* add zero at the end */
|
||||
totalsize += len + 1;
|
||||
break;
|
||||
}
|
||||
case Kpadding: luaL_addchar_53(b, LUAL_PACKPADBYTE); /* FALLTHROUGH */
|
||||
case Kpaddalign: case Knop:
|
||||
arg--; /* undo increment */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int lua53_str_packsize (lua_State *L) {
|
||||
Header h;
|
||||
const char *fmt = luaL_checkstring(L, 1); /* format string */
|
||||
size_t totalsize = 0; /* accumulate total size of result */
|
||||
initheader(L, &h);
|
||||
while (*fmt != '\0') {
|
||||
int size, ntoalign;
|
||||
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
|
||||
size += ntoalign; /* total space used by option */
|
||||
luaL_argcheck(L, totalsize <= MAXSIZE - size, 1,
|
||||
"format result too large");
|
||||
totalsize += size;
|
||||
switch (opt) {
|
||||
case Kstring: /* strings with length count */
|
||||
case Kzstr: /* zero-terminated string */
|
||||
luaL_argerror(L, 1, "variable-length format");
|
||||
/* call never return, but to avoid warnings: *//* FALLTHROUGH */
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
lua_pushinteger(L, (lua_Integer)totalsize);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Unpack an integer with 'size' bytes and 'islittle' endianness.
|
||||
** If size is smaller than the size of a Lua integer and integer
|
||||
** is signed, must do sign extension (propagating the sign to the
|
||||
** higher bits); if size is larger than the size of a Lua integer,
|
||||
** it must check the unread bytes to see whether they do not cause an
|
||||
** overflow.
|
||||
*/
|
||||
static lua_Integer unpackint (lua_State *L, const char *str,
|
||||
int islittle, int size, int issigned) {
|
||||
lua_Unsigned res = 0;
|
||||
int i;
|
||||
int limit = (size <= SZINT) ? size : SZINT;
|
||||
for (i = limit - 1; i >= 0; i--) {
|
||||
res <<= NB;
|
||||
res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i];
|
||||
}
|
||||
if (size < SZINT) { /* real size smaller than lua_Integer? */
|
||||
if (issigned) { /* needs sign extension? */
|
||||
lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1);
|
||||
res = ((res ^ mask) - mask); /* do sign extension */
|
||||
}
|
||||
}
|
||||
else if (size > SZINT) { /* must check unread bytes */
|
||||
int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC;
|
||||
for (i = limit; i < size; i++) {
|
||||
if ((unsigned char)str[islittle ? i : size - 1 - i] != mask)
|
||||
luaL_error(L, "%d-byte integer does not fit into Lua Integer", size);
|
||||
}
|
||||
}
|
||||
return (lua_Integer)res;
|
||||
}
|
||||
|
||||
|
||||
int lua53_str_unpack (lua_State *L, const char *fmt, const char *data, size_t ld, int dataidx, int posidx) {
|
||||
Header h;
|
||||
size_t pos = (size_t)posrelat(luaL_optinteger(L, posidx, 1), ld) - 1;
|
||||
int n = 0; /* number of results */
|
||||
luaL_argcheck(L, pos <= ld, posidx, "initial position out of string");
|
||||
initheader(L, &h);
|
||||
while (*fmt != '\0') {
|
||||
int size, ntoalign;
|
||||
KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign);
|
||||
if ((size_t)ntoalign + size > ~pos || pos + ntoalign + size > ld)
|
||||
luaL_argerror(L, dataidx, "data string too short");
|
||||
pos += ntoalign; /* skip alignment */
|
||||
/* stack space for item + next position */
|
||||
luaL_checkstack(L, dataidx, "too many results");
|
||||
n++;
|
||||
switch (opt) {
|
||||
case Kint:
|
||||
case Kuint: {
|
||||
lua_Integer res = unpackint(L, data + pos, h.islittle, size,
|
||||
(opt == Kint));
|
||||
lua_pushinteger(L, res);
|
||||
break;
|
||||
}
|
||||
case Kfloat: {
|
||||
volatile Ftypes u;
|
||||
lua_Number num;
|
||||
copywithendian(u.buff, data + pos, size, h.islittle);
|
||||
if (size == sizeof(u.f)) num = (lua_Number)u.f;
|
||||
else if (size == sizeof(u.d)) num = (lua_Number)u.d;
|
||||
else num = u.n;
|
||||
lua_pushnumber(L, num);
|
||||
break;
|
||||
}
|
||||
case Kchar: {
|
||||
lua_pushlstring(L, data + pos, size);
|
||||
break;
|
||||
}
|
||||
case Kstring: {
|
||||
size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0);
|
||||
luaL_argcheck(L, pos + len + size <= ld, dataidx, "data string too short");
|
||||
lua_pushlstring(L, data + pos + size, len);
|
||||
pos += len; /* skip string */
|
||||
break;
|
||||
}
|
||||
case Kzstr: {
|
||||
size_t len = (int)strlen(data + pos);
|
||||
lua_pushlstring(L, data + pos, len);
|
||||
pos += len + 1; /* skip string plus final '\0' */
|
||||
break;
|
||||
}
|
||||
case Kpaddalign: case Kpadding: case Knop:
|
||||
n--; /* undo increment */
|
||||
break;
|
||||
}
|
||||
pos += size;
|
||||
}
|
||||
lua_pushinteger(L, pos + 1); /* next position */
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
#ifndef LUA53_LSTRLIB_H
|
||||
#define LUA53_LSTRLIB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
typedef struct luaL_Buffer_53 {
|
||||
luaL_Buffer b; /* make incorrect code crash! */
|
||||
char *ptr;
|
||||
size_t nelems;
|
||||
size_t capacity;
|
||||
lua_State *L2;
|
||||
} luaL_Buffer_53;
|
||||
|
||||
void lua53_pushresult (luaL_Buffer_53 *B);
|
||||
void lua53_cleanupbuffer (luaL_Buffer_53 *B);
|
||||
|
||||
void lua53_str_pack (lua_State *L, const char *fmt, int startidx, luaL_Buffer_53 *b);
|
||||
int lua53_str_packsize (lua_State *L);
|
||||
int lua53_str_unpack (lua_State *L, const char *fmt, const char *data, size_t ld, int dataidx, int posidx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LUA53_LSTRLIB_H */
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
** $Id: lutf8lib.c,v 1.13 2014/11/02 19:19:04 roberto Exp $
|
||||
** Standard library for UTF-8 manipulation
|
||||
** Modified by the LOVE Development Team to work with Lua 5.1's API
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2015 Lua.org, PUC-Rio, 2015 LOVE Development Team.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#define lutf8lib_c
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lutf8lib.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#define MAXUNICODE 0x10FFFF
|
||||
|
||||
/* size of buffer for 'utf8esc' function (taken from lobject.h) */
|
||||
#define UTF8BUFFSZ 8
|
||||
|
||||
#define iscont(p) ((*(p) & 0xC0) == 0x80)
|
||||
|
||||
|
||||
/* from strlib */
|
||||
/* translate a relative string position: negative means back from end */
|
||||
static lua_Integer u_posrelat (lua_Integer pos, size_t len) {
|
||||
if (pos >= 0) return pos;
|
||||
else if (0u - (size_t)pos > len) return 0;
|
||||
else return (lua_Integer)len + pos + 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid.
|
||||
*/
|
||||
static const char *utf8_decode (const char *o, int *val) {
|
||||
static unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF};
|
||||
const unsigned char *s = (const unsigned char *)o;
|
||||
unsigned int c = s[0];
|
||||
unsigned int res = 0; /* final result */
|
||||
if (c < 0x80) /* ascii? */
|
||||
res = c;
|
||||
else {
|
||||
int count = 0; /* to count number of continuation bytes */
|
||||
while (c & 0x40) { /* still have continuation bytes? */
|
||||
int cc = s[++count]; /* read next byte */
|
||||
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
|
||||
return NULL; /* invalid byte sequence */
|
||||
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
|
||||
c <<= 1; /* to test next bit */
|
||||
}
|
||||
res |= ((c & 0x7F) << (count * 5)); /* add first byte */
|
||||
if (count > 3 || res > MAXUNICODE || res <= limits[count])
|
||||
return NULL; /* invalid byte sequence */
|
||||
s += count; /* skip continuation bytes read */
|
||||
}
|
||||
if (val) *val = res;
|
||||
return (const char *)s + 1; /* +1 to include first byte */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** utf8len(s [, i [, j]]) --> number of characters that start in the
|
||||
** range [i,j], or nil + current position if 's' is not well formed in
|
||||
** that interval
|
||||
*/
|
||||
static int utflen (lua_State *L) {
|
||||
int n = 0;
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
|
||||
lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len);
|
||||
luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2,
|
||||
"initial position out of string");
|
||||
luaL_argcheck(L, --posj < (lua_Integer)len, 3,
|
||||
"final position out of string");
|
||||
while (posi <= posj) {
|
||||
const char *s1 = utf8_decode(s + posi, NULL);
|
||||
if (s1 == NULL) { /* conversion error? */
|
||||
lua_pushnil(L); /* return nil ... */
|
||||
lua_pushinteger(L, posi + 1); /* ... and current position */
|
||||
return 2;
|
||||
}
|
||||
posi = s1 - s;
|
||||
n++;
|
||||
}
|
||||
lua_pushinteger(L, n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** codepoint(s, [i, [j]]) -> returns codepoints for all characters
|
||||
** that start in the range [i,j]
|
||||
*/
|
||||
static int codepoint (lua_State *L) {
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
|
||||
lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len);
|
||||
int n;
|
||||
const char *se;
|
||||
luaL_argcheck(L, posi >= 1, 2, "out of range");
|
||||
luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of range");
|
||||
if (posi > pose) return 0; /* empty interval; return no values */
|
||||
n = (int)(pose - posi + 1);
|
||||
if (posi + n <= pose) /* (lua_Integer -> int) overflow? */
|
||||
return luaL_error(L, "string slice too long");
|
||||
luaL_checkstack(L, n, "string slice too long");
|
||||
n = 0;
|
||||
se = s + pose;
|
||||
for (s += posi - 1; s < se;) {
|
||||
int code;
|
||||
s = utf8_decode(s, &code);
|
||||
if (s == NULL)
|
||||
return luaL_error(L, "invalid UTF-8 code");
|
||||
lua_pushinteger(L, code);
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/* taken from lobject.c */
|
||||
static int utf8esc (char *buff, unsigned long x) {
|
||||
int n = 1; /* number of bytes put in buffer (backwards) */
|
||||
lua_assert(x <= 0x10FFFF);
|
||||
if (x < 0x80) /* ascii? */
|
||||
buff[UTF8BUFFSZ - 1] = (char) x;
|
||||
else { /* need continuation bytes */
|
||||
unsigned int mfb = 0x3f; /* maximum that fits in first byte */
|
||||
do { /* add continuation bytes */
|
||||
buff[UTF8BUFFSZ - (n++)] = (char) (0x80 | (x & 0x3f));
|
||||
x >>= 6; /* remove added bits */
|
||||
mfb >>= 1; /* now there is one less bit available in first byte */
|
||||
} while (x > mfb); /* still needs continuation byte? */
|
||||
buff[UTF8BUFFSZ - n] = (char) ((~mfb << 1) | x); /* add first byte */
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static void pushutfchar (lua_State *L, int arg) {
|
||||
lua_Integer code = luaL_checkinteger(L, arg);
|
||||
luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, "value out of range");
|
||||
|
||||
/* the %U string format does not exist in lua 5.1 or 5.2, so we emulate it */
|
||||
/* (code from luaO_pushvfstring in lobject.c) */
|
||||
char buff[UTF8BUFFSZ];
|
||||
int l = utf8esc(buff, (long) code);
|
||||
lua_pushlstring(L, buff + UTF8BUFFSZ - l, l);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** utfchar(n1, n2, ...) -> char(n1)..char(n2)...
|
||||
*/
|
||||
static int utfchar (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
if (n == 1) /* optimize common case of single char */
|
||||
pushutfchar(L, 1);
|
||||
else {
|
||||
int i;
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
for (i = 1; i <= n; i++) {
|
||||
pushutfchar(L, i);
|
||||
luaL_addvalue(&b);
|
||||
}
|
||||
luaL_pushresult(&b);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** offset(s, n, [i]) -> index where n-th character counting from
|
||||
** position 'i' starts; 0 means character at 'i'.
|
||||
*/
|
||||
static int byteoffset (lua_State *L) {
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Integer n = luaL_checkinteger(L, 2);
|
||||
lua_Integer posi = (n >= 0) ? 1 : len + 1;
|
||||
posi = u_posrelat(luaL_optinteger(L, 3, posi), len);
|
||||
luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3,
|
||||
"position out of range");
|
||||
if (n == 0) {
|
||||
/* find beginning of current byte sequence */
|
||||
while (posi > 0 && iscont(s + posi)) posi--;
|
||||
}
|
||||
else {
|
||||
if (iscont(s + posi))
|
||||
luaL_error(L, "initial position is a continuation byte");
|
||||
if (n < 0) {
|
||||
while (n < 0 && posi > 0) { /* move back */
|
||||
do { /* find beginning of previous character */
|
||||
posi--;
|
||||
} while (posi > 0 && iscont(s + posi));
|
||||
n++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
n--; /* do not move for 1st character */
|
||||
while (n > 0 && posi < (lua_Integer)len) {
|
||||
do { /* find beginning of next character */
|
||||
posi++;
|
||||
} while (iscont(s + posi)); /* (cannot pass final '\0') */
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n == 0) /* did it find given character? */
|
||||
lua_pushinteger(L, posi + 1);
|
||||
else /* no such character */
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int iter_aux (lua_State *L) {
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Integer n = lua_tointeger(L, 2) - 1;
|
||||
if (n < 0) /* first iteration? */
|
||||
n = 0; /* start from here */
|
||||
else if (n < (lua_Integer)len) {
|
||||
n++; /* skip current byte */
|
||||
while (iscont(s + n)) n++; /* and its continuations */
|
||||
}
|
||||
if (n >= (lua_Integer)len)
|
||||
return 0; /* no more codepoints */
|
||||
else {
|
||||
int code;
|
||||
const char *next = utf8_decode(s + n, &code);
|
||||
if (next == NULL || iscont(next))
|
||||
return luaL_error(L, "invalid UTF-8 code");
|
||||
lua_pushinteger(L, n + 1);
|
||||
lua_pushinteger(L, code);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int iter_codes (lua_State *L) {
|
||||
luaL_checkstring(L, 1);
|
||||
lua_pushcfunction(L, iter_aux);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, 0);
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
/* pattern to match a single UTF-8 character */
|
||||
#if LUA_VERSION_NUM >= 502
|
||||
#define UTF8PATT "[\0-\x7F\xC2-\xF4][\x80-\xBF]*"
|
||||
#else
|
||||
/* lua 5.1 doesn't support literal null bytes in patterns */
|
||||
#define UTF8PATT "[%z\x01-\x7F\xC2-\xF4][\x80-\xBF]*"
|
||||
#endif
|
||||
|
||||
|
||||
static struct luaL_Reg funcs[] = {
|
||||
{"offset", byteoffset},
|
||||
{"codepoint", codepoint},
|
||||
{"char", utfchar},
|
||||
{"len", utflen},
|
||||
{"codes", iter_codes},
|
||||
/* placeholders */
|
||||
{"charpattern", NULL},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
/* modified version of luaopen_utf8, designed to work with lua 5.1-5.3 */
|
||||
int luaopen_luautf8 (lua_State *L) {
|
||||
luaL_Reg *l;
|
||||
lua_createtable(L, 0, (int) (sizeof(funcs) / sizeof(luaL_Reg)) - 1);
|
||||
for (l = funcs; l->name != NULL; l++) {
|
||||
if (l->func != NULL) {
|
||||
lua_pushcfunction(L, l->func);
|
||||
lua_setfield(L, -2, l->name);
|
||||
}
|
||||
}
|
||||
lua_pushliteral(L, UTF8PATT);
|
||||
lua_setfield(L, -2, "charpattern");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
#ifndef LUA53_LUTF8LIB_H
|
||||
#define LUA53_LUTF8LIB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
LUALIB_API int luaopen_luautf8(lua_State *L);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LUA53_LUTF8LIB_H */
|
||||
Reference in New Issue
Block a user