summaryrefslogtreecommitdiff
path: root/source/luametatex/source/luarest/lmtstrlibext.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/luarest/lmtstrlibext.c')
-rw-r--r--source/luametatex/source/luarest/lmtstrlibext.c927
1 files changed, 927 insertions, 0 deletions
diff --git a/source/luametatex/source/luarest/lmtstrlibext.c b/source/luametatex/source/luarest/lmtstrlibext.c
new file mode 100644
index 000000000..78d7f760c
--- /dev/null
+++ b/source/luametatex/source/luarest/lmtstrlibext.c
@@ -0,0 +1,927 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ The relative ordering of the header files is important here, otherwise some of the defines that
+ are needed for lua_sdump come out wrong.
+
+*/
+
+/* todo: byteconcat and utf concat (no separator) */
+
+# include "luametatex.h"
+
+/*tex Helpers */
+
+# define utf_fffd "\xEF\xBF\xBD"
+
+inline static int strlib_aux_tounicode(const char *s, size_t l, size_t *p)
+{
+ unsigned char i = s[*p];
+ *p += 1;
+ if (i < 0x80) {
+ return i;
+ } else if (i >= 0xF0) {
+ if ((*p + 2) < l) {
+ unsigned char j = s[*p];
+ unsigned char k = s[*p + 1];
+ unsigned char l = s[*p + 2];
+ if (j >= 0x80 && k >= 0x80 && l >= 0x80) {
+ *p += 3;
+ return (((((i - 0xF0) * 0x40) + (j - 0x80)) * 0x40) + (k - 0x80)) * 0x40 + (l - 0x80);
+ }
+ }
+ } else if (i >= 0xE0) {
+ if ((*p + 1) < l) {
+ unsigned char j = s[*p];
+ unsigned char k = s[*p + 1];
+ if (j >= 0x80 && k >= 0x80) {
+ *p += 2;
+ return (((i - 0xE0) * 0x40) + (j - 0x80)) * 0x40 + (k - 0x80);
+ }
+ }
+ } else if (i >= 0xC0) {
+ if (*p < l) {
+ unsigned char j = s[*p];
+ if (j >= 0x80) {
+ *p += 1;
+ return ((i - 0xC0) * 0x40) + (j - 0x80);
+ }
+ }
+ }
+ return 0xFFFD;
+}
+
+inline static int strlib_aux_tounichar(const char *s, size_t l, size_t p)
+{
+ unsigned char i = s[p++];
+ if (i < 0x80) {
+ return 1;
+ } else if (i >= 0xF0) {
+ if ((p + 2) < l) {
+ unsigned char j = s[p];
+ unsigned char k = s[p + 1];
+ unsigned char l = s[p + 2];
+ if (j >= 0x80 && k >= 0x80 && l >= 0x80) {
+ return 4;
+ }
+ }
+ } else if (i >= 0xE0) {
+ if ((p + 1) < l) {
+ unsigned char j = s[p];
+ unsigned char k = s[p + 1];
+ if (j >= 0x80 && k >= 0x80) {
+ return 3;
+ }
+ }
+ } else if (i >= 0xC0) {
+ if (p < l) {
+ unsigned char j = s[p];
+ if (j >= 0x80) {
+ return 2;
+ }
+ }
+ }
+ return 0;
+}
+
+inline static size_t strlib_aux_toline(const char *s, size_t l, size_t p, size_t *b)
+{
+ size_t i = p;
+ while (i < l) {
+ if (s[i] == 13) {
+ if ((i + 1) < l) {
+ if (s[i + 1] == 10) {
+ *b = 2; /* cr lf */
+ } else {
+ *b = 1; /* cr */
+ }
+ }
+ return i - p;
+ } else if (s[i] == 10) {
+ *b = 1; /* lf */
+ return i - p;
+ } else {
+ /* other */
+ i += 1;
+ }
+ }
+ return i - p ;
+}
+
+/*tex End of helpers. */
+
+static int strlib_aux_bytepairs(lua_State *L)
+{
+ size_t ls = 0;
+ const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
+ size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
+ if (ind < ls) {
+ unsigned char i;
+ /*tex iterator */
+ if (ind + 1 < ls) {
+ lua_pushinteger(L, ind + 2);
+ } else {
+ lua_pushinteger(L, ind + 1);
+ }
+ lua_replace(L, lua_upvalueindex(2));
+ i = (unsigned char)*(s + ind);
+ /*tex byte one */
+ lua_pushinteger(L, i);
+ if (ind + 1 < ls) {
+ /*tex byte two */
+ i = (unsigned char)*(s + ind + 1);
+ lua_pushinteger(L, i);
+ } else {
+ /*tex odd string length */
+ lua_pushnil(L);
+ }
+ return 2;
+ } else {
+ return 0;
+ }
+}
+
+static int strlib_bytepairs(lua_State *L)
+{
+ luaL_checkstring(L, 1);
+ lua_settop(L, 1);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, strlib_aux_bytepairs, 2);
+ return 1;
+}
+
+static int strlib_aux_bytes(lua_State *L)
+{
+ size_t ls = 0;
+ const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
+ size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
+ if (ind < ls) {
+ /*tex iterator */
+ lua_pushinteger(L, ind + 1);
+ lua_replace(L, lua_upvalueindex(2));
+ /*tex byte */
+ lua_pushinteger(L, (unsigned char)*(s + ind));
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int strlib_bytes(lua_State *L)
+{
+ luaL_checkstring(L, 1);
+ lua_settop(L, 1);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, strlib_aux_bytes, 2);
+ return 1;
+}
+
+static int strlib_aux_utf_failed(lua_State *L, int new_ind)
+{
+ lua_pushinteger(L, new_ind);
+ lua_replace(L, lua_upvalueindex(2));
+ lua_pushliteral(L, utf_fffd);
+ return 1;
+}
+
+/* kind of complex ... these masks */
+
+static int strlib_aux_utfcharacters(lua_State *L)
+{
+ static const unsigned char mask[4] = { 0x80, 0xE0, 0xF0, 0xF8 };
+ static const unsigned char mequ[4] = { 0x00, 0xC0, 0xE0, 0xF0 };
+ size_t ls = 0;
+ const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
+ size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
+ size_t l = ls;
+ if (ind >= l) {
+ return 0;
+ } else {
+ unsigned char c = (unsigned char) s[ind];
+ for (size_t j = 0; j < 4; j++) {
+ if ((c & mask[j]) == mequ[j]) {
+ if (ind + 1 + j > l) {
+ /*tex The result will not fit. */
+ return strlib_aux_utf_failed(L, (int) l);
+ }
+ for (size_t k = 1; k <= j; k++) {
+ c = (unsigned char) s[ind + k];
+ if ((c & 0xC0) != 0x80) {
+ /*tex We have a bad follow byte. */
+ return strlib_aux_utf_failed(L, (int) (ind + k));
+ }
+ }
+ /*tex The iterator. */
+ lua_pushinteger(L, ind + j + 1);
+ lua_replace(L, lua_upvalueindex(2));
+ lua_pushlstring(L, ind + s, j + 1);
+ return 1;
+ }
+ }
+ return strlib_aux_utf_failed(L, (int) (ind + 1)); /* we found a follow byte! */
+ }
+}
+
+static int strlib_utfcharacters(lua_State *L)
+{
+ luaL_checkstring(L, 1);
+ lua_settop(L, 1);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, strlib_aux_utfcharacters, 2);
+ return 1;
+}
+
+static int strlib_aux_utfvalues(lua_State *L)
+{
+ size_t l = 0;
+ const char *s = lua_tolstring(L, lua_upvalueindex(1), &l);
+ size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
+ if (ind < l) {
+ int v = strlib_aux_tounicode(s, l, &ind);
+ lua_pushinteger(L, ind);
+ lua_replace(L, lua_upvalueindex(2));
+ lua_pushinteger(L, v);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int strlib_utfvalues(lua_State *L)
+{
+ luaL_checkstring(L, 1);
+ lua_settop(L, 1);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, strlib_aux_utfvalues, 2);
+ return 1;
+}
+
+static int strlib_aux_characterpairs(lua_State *L)
+{
+ size_t ls = 0;
+ const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
+ size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
+ if (ind < ls) {
+ char b[1];
+ lua_pushinteger(L, ind + 2); /*tex So we can overshoot ls here. */
+ lua_replace(L, lua_upvalueindex(2));
+ b[0] = s[ind];
+ lua_pushlstring(L, b, 1);
+ if ((ind + 1) < ls) {
+ b[0] = s[ind + 1];
+ lua_pushlstring(L, b, 1);
+ } else {
+ lua_pushliteral(L, "");
+ }
+ return 2;
+ } else {
+ return 0; /* string ended */
+ }
+}
+
+static int strlib_characterpairs(lua_State *L)
+{
+ luaL_checkstring(L, 1);
+ lua_settop(L, 1);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, strlib_aux_characterpairs, 2);
+ return 1;
+}
+
+static int strlib_aux_characters(lua_State *L)
+{
+ size_t ls = 0;
+ const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
+ size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
+ if (ind < ls) {
+ char b[1];
+ lua_pushinteger(L, ind + 1); /* iterator */
+ lua_replace(L, lua_upvalueindex(2));
+ b[0] = *(s + ind);
+ lua_pushlstring(L, b, 1);
+ return 1;
+ } else {
+ return 0; /* string ended */
+ }
+}
+
+static int strlib_characters(lua_State *L)
+{
+ luaL_checkstring(L, 1);
+ lua_settop(L, 1);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, strlib_aux_characters, 2);
+ return 1;
+}
+
+static int strlib_bytetable(lua_State *L)
+{
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_createtable(L, (int) l, 0);
+ for (size_t i = 0; i < l; i++) {
+ lua_pushinteger(L, (unsigned char)*(s + i));
+ lua_rawseti(L, -2, i + 1);
+ }
+ return 1;
+}
+
+static int strlib_utfvaluetable(lua_State *L)
+{
+ size_t n = 1;
+ size_t l = 0;
+ size_t p = 0;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_createtable(L, (int) l, 0);
+ while (p < l) {
+ lua_pushinteger(L, strlib_aux_tounicode(s, l, &p));
+ lua_rawseti(L, -2, n++);
+ }
+ return 1;
+}
+
+static int strlib_utfcharactertable(lua_State *L)
+{
+ size_t n = 1;
+ size_t l = 0;
+ size_t p = 0;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_createtable(L, (int) l, 0);
+ while (p < l) {
+ int b = strlib_aux_tounichar(s, l, p);
+ if (b) {
+ lua_pushlstring(L, s + p, b);
+ p += b;
+ } else {
+ lua_pushliteral(L, utf_fffd);
+ p += 1;
+ }
+ lua_rawseti(L, -2, n++);
+ }
+ return 1;
+}
+
+static int strlib_linetable(lua_State *L)
+{
+ size_t n = 1;
+ size_t l = 0;
+ size_t p = 0;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_createtable(L, (int) l, 0);
+ while (p < l) {
+ size_t b = 0;
+ size_t m = strlib_aux_toline(s, l, p, &b);
+ if (m) {
+ lua_pushlstring(L, s + p, m);
+ } else {
+ lua_pushliteral(L, "");
+ }
+ p += m + b;
+ lua_rawseti(L, -2, n++);
+ }
+ return 1;
+}
+
+/*tex
+
+ We provide a few helpers that we derived from the lua utf8 module and slunicode. That way we're
+ sort of covering a decent mix.
+
+*/
+
+# define MAXUNICODE 0x10FFFF
+
+/*tex
+
+ This is a combination of slunicode and utf8 converters but without mode and a bit faster on the
+ average than the utf8 one. The one character branch is a bit more efficient, as is preallocating
+ the buffer size.
+
+*/
+
+static int strlib_utfcharacter(lua_State *L) /* todo: use tounichar here too */
+{
+ int n = lua_gettop(L);
+ if (n == 1) {
+ char u[6];
+ char *c = aux_uni2string(&u[0], (unsigned) lua_tointeger(L, 1));
+ *c = '\0';
+ lua_pushstring(L, u);
+ return 1;
+ } else {
+ luaL_Buffer b;
+ luaL_buffinitsize(L, &b, (size_t) n * 4);
+ for (int i = 1; i <= n; i++) {
+ unsigned u = (unsigned) lua_tointeger(L, i);
+ if (u <= MAXUNICODE) {
+ if (0x80 > u) {
+ luaL_addchar(&b, (unsigned char) u);
+ } else {
+ if (0x800 > u)
+ luaL_addchar(&b, (unsigned char) (0xC0 | (u >> 6)));
+ else {
+ if (0x10000 > u)
+ luaL_addchar(&b, (unsigned char) (0xE0 | (u >> 12)));
+ else {
+ luaL_addchar(&b, (unsigned char) (0xF0 | (u >> 18)));
+ luaL_addchar(&b, (unsigned char) (0x80 | (0x3F & (u >> 12))));
+ }
+ luaL_addchar(&b, 0x80 | (0x3F & (u >> 6)));
+ }
+ luaL_addchar(&b, 0x80 | (0x3F & u));
+ }
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+ }
+}
+
+/*tex
+
+ The \UTF8 codepoint function takes two arguments, being positions in the string, while slunicode
+ byte takes two arguments representing the number of utf characters. The variant below always
+ returns all codepoints.
+
+*/
+
+static int strlib_utfvalue(lua_State *L)
+{
+ size_t l = 0;
+ size_t p = 0;
+ int i = 0;
+ const char *s = luaL_checklstring(L, 1, &l);
+ while (p < l) {
+ lua_pushinteger(L, strlib_aux_tounicode(s, l, &p));
+ i++;
+ }
+ return i;
+}
+
+/*tex This is a simplified version of utf8.len but without range. */
+
+static int strlib_utflength(lua_State *L)
+{
+ size_t ls = 0;
+ size_t ind = 0;
+ size_t n = 0;
+ const char *s = lua_tolstring(L, 1, &ls);
+ while (ind < ls) {
+ unsigned char i = (unsigned char) *(s + ind);
+ if (i < 0x80) {
+ ind += 1;
+ } else if (i >= 0xF0) {
+ ind += 4;
+ } else if (i >= 0xE0) {
+ ind += 3;
+ } else if (i >= 0xC0) {
+ ind += 2;
+ } else {
+ /*tex bad news, stupid recovery */
+ ind += 1;
+ }
+ n++;
+ }
+ lua_pushinteger(L, n);
+ return 1;
+}
+
+/*tex A handy one that formats a float but also strips trailing zeros. */
+
+static int strlib_format_f6(lua_State *L)
+{
+ double n = luaL_optnumber(L, 1, 0.0);
+ if (n == 0.0) {
+ lua_pushliteral(L, "0");
+ } else if (n == 1.0) {
+ lua_pushliteral(L, "1");
+ } else {
+ char s[128];
+ int i, l;
+ /* we should check for max int */
+ if (fmod(n, 1) == 0) {
+ i = snprintf(s, 128, "%i", (int) n);
+ } else {
+ if (lua_type(L, 2) == LUA_TSTRING) {
+ const char *f = lua_tostring(L, 2);
+ i = snprintf(s, 128, f, n);
+ } else {
+ i = snprintf(s, 128, "%0.6f", n) ;
+ }
+ l = i - 1;
+ while (l > 1) {
+ if (s[l - 1] == '.') {
+ break;
+ } else if (s[l] == '0') {
+ s[l] = '\0';
+ --i;
+ } else {
+ break;
+ }
+ l--;
+ }
+ }
+ lua_pushlstring(L, s, i);
+ }
+ return 1;
+}
+
+/*tex
+ The next one is mostly provided as check because doing it in pure \LUA\ is not slower and it's
+ not a bottleneck anyway. There are soms subtle side effects when we don't check for these ranges,
+ especially the trigger bytes (|0xD7FF| etc.) because we can get negative numbers which means
+ wrapping around and such.
+*/
+
+inline static unsigned char strlib_aux_hexdigit(unsigned char n) {
+ return (n < 10 ? '0' : 'A' - 10) + n;
+}
+
+# define invalid_unicode(u) ( \
+ (u >= 0x00E000 && u <= 0x00F8FF) || \
+ (u >= 0x0F0000 && u <= 0x0FFFFF) || \
+ (u >= 0x100000 && u <= 0x10FFFF) || \
+ /* (u >= 0x00D800 && u <= 0x00DFFF)) { */ \
+ (u >= 0x00D7FF && u <= 0x00DFFF) \
+)
+
+static int strlib_format_tounicode16(lua_State *L)
+{
+ lua_Integer u = lua_tointeger(L, 1);
+ if (invalid_unicode(u)) {
+ lua_pushliteral(L, "FFFD");
+ } else if (u < 0xD7FF || (u > 0xDFFF && u <= 0xFFFF)) {
+ char s[4] ;
+ s[3] = strlib_aux_hexdigit((unsigned char) ((u & 0x000F) >> 0));
+ s[2] = strlib_aux_hexdigit((unsigned char) ((u & 0x00F0) >> 4));
+ s[1] = strlib_aux_hexdigit((unsigned char) ((u & 0x0F00) >> 8));
+ s[0] = strlib_aux_hexdigit((unsigned char) ((u & 0xF000) >> 12));
+ lua_pushlstring(L, s, 4);
+ } else {
+ unsigned u1, u2;
+ char s[8] ;
+ u = u - 0x10000; /* negative when invalid range */
+ u1 = (unsigned) (u >> 10) + 0xD800;
+ u2 = (unsigned) (u % 0x400) + 0xDC00;
+ s[3] = strlib_aux_hexdigit((unsigned char) ((u1 & 0x000F) >> 0));
+ s[2] = strlib_aux_hexdigit((unsigned char) ((u1 & 0x00F0) >> 4));
+ s[1] = strlib_aux_hexdigit((unsigned char) ((u1 & 0x0F00) >> 8));
+ s[0] = strlib_aux_hexdigit((unsigned char) ((u1 & 0xF000) >> 12));
+ s[7] = strlib_aux_hexdigit((unsigned char) ((u2 & 0x000F) >> 0));
+ s[6] = strlib_aux_hexdigit((unsigned char) ((u2 & 0x00F0) >> 4));
+ s[5] = strlib_aux_hexdigit((unsigned char) ((u2 & 0x0F00) >> 8));
+ s[4] = strlib_aux_hexdigit((unsigned char) ((u2 & 0xF000) >> 12));
+ lua_pushlstring(L, s, 8);
+ }
+ return 1;
+}
+
+static int strlib_format_toutf8(lua_State *L) /* could be integrated into utfcharacter */
+{
+ if (lua_type(L, 1) == LUA_TTABLE) {
+ lua_Integer n = lua_rawlen(L, 1);
+ if (n > 0) {
+ luaL_Buffer b;
+ luaL_buffinitsize(L, &b, (n + 1) * 4);
+ for (lua_Integer i = 0; i <= n; i++) {
+ /* there should be one operation for getting a number from a table */
+ if (lua_rawgeti(L, 1, i) == LUA_TNUMBER) {
+ unsigned u = (unsigned) lua_tointeger(L, -1);
+ if (0x80 > u) {
+ luaL_addchar(&b, (unsigned char) u);
+ } else if (invalid_unicode(u)) {
+ luaL_addchar(&b, 0xFF);
+ luaL_addchar(&b, 0xFD);
+ } else {
+ if (0x800 > u)
+ luaL_addchar(&b, (unsigned char) (0xC0 | (u >> 6)));
+ else {
+ if (0x10000 > u)
+ luaL_addchar(&b, (unsigned char) (0xE0 | (u >> 12)));
+ else {
+ luaL_addchar(&b, (unsigned char) (0xF0 | (u >>18)));
+ luaL_addchar(&b, (unsigned char) (0x80 | (0x3F & (u >> 12))));
+ }
+ luaL_addchar(&b, 0x80 | (0x3F & (u >> 6)));
+ }
+ luaL_addchar(&b, 0x80 | (0x3F & u));
+ }
+ }
+ lua_pop(L, 1);
+ }
+ luaL_pushresult(&b);
+ } else {
+ lua_pushliteral(L, "");
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/*
+static int strlib_format_toutf16(lua_State* L) {
+ if (lua_type(L, 1) == LUA_TTABLE) {
+ lua_Integer n = lua_rawlen(L, 1);
+ if (n > 0) {
+ luaL_Buffer b;
+ luaL_buffinitsize(L, &b, (n + 2) * 4);
+ for (lua_Integer i = 0; i <= n; i++) {
+ if (lua_rawgeti(L, 1, i) == LUA_TNUMBER) {
+ unsigned u = (unsigned) lua_tointeger(L, -1);
+ if (invalid_unicode(u)) {
+ luaL_addchar(&b, 0xFF);
+ luaL_addchar(&b, 0xFD);
+ } else if (u < 0x10000) {
+ luaL_addchar(&b, (unsigned char) ((u & 0x00FF) ));
+ luaL_addchar(&b, (unsigned char) ((u & 0xFF00) >> 8));
+ } else {
+ u = u - 0x10000;
+ luaL_addchar(&b, (unsigned char) ((((u>>10)+0xD800) & 0x00FF) ));
+ luaL_addchar(&b, (unsigned char) ((((u>>10)+0xD800) & 0xFF00) >> 8));
+ luaL_addchar(&b, (unsigned char) (( (u%1024+0xDC00) & 0x00FF) ));
+ luaL_addchar(&b, (unsigned char) (( (u%1024+0xDC00) & 0xFF00) >> 8));
+ }
+ }
+ lua_pop(L, 1);
+ }
+ luaL_addchar(&b, 0);
+ luaL_addchar(&b, 0);
+ luaL_pushresult(&b);
+ } else {
+ lua_pushliteral(L, "");
+ }
+ return 1;
+ }
+ return 0;
+}
+*/
+
+static int strlib_format_toutf32(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TTABLE) {
+ lua_Integer n = lua_rawlen(L, 1);
+ if (n > 0) {
+ luaL_Buffer b;
+ luaL_buffinitsize(L, &b, (n + 2) * 4);
+ for (lua_Integer i = 0; i <= n; i++) {
+ /* there should be one operation for getting a number from a table */
+ if (lua_rawgeti(L, 1, i) == LUA_TNUMBER) {
+ unsigned u = (unsigned) lua_tointeger(L, -1);
+ if (invalid_unicode(u)) {
+ luaL_addchar(&b, 0x00);
+ luaL_addchar(&b, 0x00);
+ luaL_addchar(&b, 0xFF);
+ luaL_addchar(&b, 0xFD);
+ } else {
+ luaL_addchar(&b, (unsigned char) ((u & 0x000000FF) ));
+ luaL_addchar(&b, (unsigned char) ((u & 0x0000FF00) >> 8));
+ luaL_addchar(&b, (unsigned char) ((u & 0x00FF0000) >> 16));
+ luaL_addchar(&b, (unsigned char) ((u & 0xFF000000) >> 24));
+ }
+ }
+ lua_pop(L, 1);
+ }
+ for (int i = 0; i <= 3; i++) {
+ luaL_addchar(&b, 0);
+ }
+ luaL_pushresult(&b);
+ } else {
+ lua_pushliteral(L, "");
+ }
+ return 1;
+ }
+ return 0;
+}
+
+// static char map[] = {
+// '0', '1', '2', '3',
+// '4', '5', '6', '7',
+// '8', '9', 'A', 'B',
+// 'C', 'D', 'E', 'F',
+// };
+
+static int strlib_pack_rows_columns(lua_State* L)
+{
+ if (lua_type(L, 1) == LUA_TTABLE) {
+ lua_Integer rows = lua_rawlen(L, 1);
+ if (lua_rawgeti(L, 1, 1) == LUA_TTABLE) {
+ lua_Integer columns = lua_rawlen(L, -1);
+ switch (lua_rawgeti(L, -1, 1)) {
+ case LUA_TNUMBER:
+ {
+ lua_Integer size = rows * columns;
+ char *result = lmt_memory_malloc(size);
+ lua_pop(L, 2); /* row and cell */
+ if (result) {
+ char *first = result;
+ for (lua_Integer r = 1; r <= rows; r++) {
+ if (lua_rawgeti(L, -1, r) == LUA_TTABLE) {
+ for (lua_Integer c = 1; c <= columns; c++) {
+ if (lua_rawgeti(L, -1, c) == LUA_TNUMBER) {
+ lua_Integer v = lua_tointeger(L, -1);
+ if (v < 0) {
+ v = 0;
+ } else if (v > 255) {
+ v = 255;
+ }
+ *result++ = (char) v;
+ }
+ lua_pop(L, 1);
+ }
+ }
+ lua_pop(L, 1);
+ }
+ lua_pushlstring(L, first, result - first);
+ return 1;
+ }
+ }
+ case LUA_TTABLE:
+ {
+ lua_Integer mode = lua_rawlen(L, -1);
+ lua_Integer size = rows * columns * mode;
+ char *result = lmt_memory_malloc(size);
+ lua_pop(L, 2); /* row and cell */
+ if (result) {
+ char *first = result;
+ for (lua_Integer r = 1; r <= rows; r++) {
+ if (lua_rawgeti(L, -1, r) == LUA_TTABLE) {
+ for (lua_Integer c = 1; c <= columns; c++) {
+ if (lua_rawgeti(L, -1, c) == LUA_TTABLE) {
+ for (int i = 1; i <= mode; i++) {
+ if (lua_rawgeti(L, -1, i) == LUA_TNUMBER) {
+ lua_Integer v = lua_tointeger(L, -1);
+ if (v < 0) {
+ v = 0;
+ } else if (v > 255) {
+ v = 255;
+ }
+ *result++ = (char) v;
+ }
+ lua_pop(L, 1);
+ }
+ }
+ lua_pop(L, 1);
+ }
+ }
+ lua_pop(L, 1);
+ }
+ lua_pushlstring(L, first, result - first);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static const luaL_Reg strlib_function_list[] = {
+ { "characters", strlib_characters },
+ { "characterpairs", strlib_characterpairs },
+ { "bytes", strlib_bytes },
+ { "bytepairs", strlib_bytepairs },
+ { "bytetable", strlib_bytetable },
+ { "linetable", strlib_linetable },
+ { "utfvalues", strlib_utfvalues },
+ { "utfcharacters", strlib_utfcharacters },
+ { "utfcharacter", strlib_utfcharacter },
+ { "utfvalue", strlib_utfvalue },
+ { "utflength", strlib_utflength },
+ { "utfvaluetable", strlib_utfvaluetable },
+ { "utfcharactertable", strlib_utfcharactertable },
+ { "f6", strlib_format_f6 },
+ { "tounicode16", strlib_format_tounicode16 },
+ { "toutf8", strlib_format_toutf8 },
+ /* { "toutf16", strlib_format_toutf16 }, */ /* untested */
+ { "toutf32", strlib_format_toutf32 },
+ { "packrowscolumns", strlib_pack_rows_columns },
+ { NULL, NULL },
+};
+
+int luaextend_string(lua_State * L)
+{
+ lua_getglobal(L, "string");
+ for (const luaL_Reg *lib = strlib_function_list; lib->name; lib++) {
+ lua_pushcfunction(L, lib->func);
+ lua_setfield(L, -2, lib->name);
+ }
+ lua_pop(L, 1);
+ return 1;
+}
+
+/*
+ The next (old, moved here) experiment was used to check if using some buffer is more efficient
+ than using a table that we concat. It makes no difference. If we ever use this, the initializer
+ |luaextend_string_buffer| will me merged into |luaextend_string|. We could gain a little on a
+ bit more efficient |luaL_checkudata| as we use elsewhere because in practice (surprise) its
+ overhead makes buffers like this {\em 50 percent} slower than the concatinated variant and
+ twice as slow when we reuse a temporary table. It's just better to stay at the \LUA\ end.
+*/
+
+/*
+# define STRING_BUFFER_METATABLE "string.buffer"
+
+typedef struct lmt_string_buffer {
+ char *buffer;
+ size_t length;
+ size_t size;
+ size_t step;
+ size_t padding;
+} lmt_string_buffer;
+
+static int strlib_buffer_gc(lua_State* L)
+{
+ lmt_string_buffer *b = (lmt_string_buffer *) luaL_checkudata(L, 1, STRING_BUFFER_METATABLE);
+ if (b && b->buffer) {
+ lmt_memory_free(b->buffer);
+ }
+ return 0;
+}
+
+static int strlib_buffer_new(lua_State* L)
+{
+ size_t size = lmt_optsizet(L, 1, LUAL_BUFFERSIZE);
+ size_t step = lmt_optsizet(L, 2, size);
+ lmt_string_buffer *b = (lmt_string_buffer *) lua_newuserdatauv(L, sizeof(lmt_string_buffer), 0);
+ b->buffer = lmt_memory_malloc(size);
+ b->size = size;
+ b->step = step;
+ b->length = 0;
+ luaL_setmetatable(L, STRING_BUFFER_METATABLE);
+ return 1;
+
+}
+
+static int strlib_buffer_add(lua_State* L)
+{
+ lmt_string_buffer *b = (lmt_string_buffer *) luaL_checkudata(L, 1, STRING_BUFFER_METATABLE);
+ switch (lua_type(L, 2)) {
+ case LUA_TSTRING:
+ {
+ size_t l;
+ const char *s = lua_tolstring(L, 2, &l);
+ size_t length = b->length + l;
+ if (length >= b->size) {
+ while (length >= b->size) {
+ b->size += b->step;
+ }
+ b->buffer = lmt_memory_realloc(b->buffer, b->size);
+ }
+ memcpy(&b->buffer[b->length], s, l);
+ b->length = length;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int strlib_buffer_get_data(lua_State* L)
+{
+ lmt_string_buffer *b = (lmt_string_buffer *) luaL_checkudata(L, 1, STRING_BUFFER_METATABLE);
+ if (b->buffer) {
+ lua_pushlstring(L, b->buffer, b->length);
+ lua_pushinteger(L, (int) b->length);
+ return 2;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int strlib_buffer_get_size(lua_State* L)
+{
+ lmt_string_buffer *b = (lmt_string_buffer *) luaL_checkudata(L, 1, STRING_BUFFER_METATABLE);
+ lua_pushinteger(L, b->length);
+ return 1;
+}
+
+static const luaL_Reg strlib_function_list_buffer[] = {
+ { "newbuffer", strlib_buffer_new },
+ { "addtobuffer", strlib_buffer_add },
+ { "getbufferdata", strlib_buffer_get_data },
+ { "getbuffersize", strlib_buffer_get_size },
+ { NULL, NULL },
+};
+
+int luaextend_string_buffer(lua_State * L)
+{
+ lua_getglobal(L, "string");
+ for (const luaL_Reg *lib = strlib_function_list_buffer; lib->name; lib++) {
+ lua_pushcfunction(L, lib->func);
+ lua_setfield(L, -2, lib->name);
+ }
+ lua_pop(L, 1);
+ luaL_newmetatable(L, STRING_BUFFER_METATABLE);
+ lua_pushcfunction(L, strlib_buffer_gc);
+ lua_setfield(L, -2, "__gc");
+ lua_pop(L, 1);
+ return 1;
+}
+
+*/