From f726a5f7170b18f25676b35f7d591c135825e735 Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Mon, 22 Jan 2018 03:07:55 -0200 Subject: [PATCH 01/24] Draft of Lua Memory library. (yes, yet another library name) --- LICENSE | 2 +- README.md | 89 +++++++++++-- demo/buffer.lua | 4 +- doc/manual.md | 12 +- src/Makefile | 86 ++++++++----- src/{lstrlib.c => lbytmod.c} | 236 ++++++++++++++++++---------------- src/lmemlib.c | 237 +++++++++++++++++++++++++++++++++++ src/lmemlib.h | 101 +++++++++++++++ src/{lbuflib.c => lmemmod.c} | 142 ++++++++++++--------- src/lstraux.c | 100 --------------- src/lstraux.h | 96 -------------- src/lstrops.c | 50 -------- src/lstrops.h | 36 ------ 13 files changed, 686 insertions(+), 505 deletions(-) rename src/{lstrlib.c => lbytmod.c} (87%) create mode 100644 src/lmemlib.c create mode 100644 src/lmemlib.h rename src/{lbuflib.c => lmemmod.c} (81%) delete mode 100644 src/lstraux.c delete mode 100644 src/lstraux.h delete mode 100644 src/lstrops.c delete mode 100644 src/lstrops.h diff --git a/LICENSE b/LICENSE index 3aed164..525216a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2015 Tecgraf, PUC-Rio. +Copyright (C) 2015-2018 Renato Maia 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 diff --git a/README.md b/README.md index b509c55..75f4de0 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,89 @@ -LuaBuffer -========= +Lua Memory +========== -Seamless Support for Buffers in Lua +The purpose of this project is to support manipulation of fixed-size memory areas in Lua. +These memory areas are much like Lua strings, but their contents can be modified in place and have an identity (selfness) independent from their contents. +The library provides the following functionalities: -The main purpose of this project is to support for buffers that provide a fixed-size memory area (userdata) to be manipulated in Lua. The library introduces the concept of "stream" which is either a buffer or an ordinary Lua string. The library provides the following functionalities: - -- Support for writable memory buffers. -- C API to manipulate Lua streams (string|buffer) in a unified way. -- Alternative implementations of Lua libraries that manipulate generic Lua streams (string|buffer). +- Support for writable memory areas. +- C API to manipulate strings or memory areas in a unified way. +- Alternative implementations of standard Lua libraries that manipulate memory areas just like strings. TODO ---- -- Make buffers provide `buffer.*` functions as methods as before. -- Assure buffers always have a `\0` at the end. +- Make every memory provide `memory.*` functions as methods. +- Assure every memory always have a `\0` after its last byte. - Write the manual +- Add support to create a memory using a pointer (potentially to a memory area external to Lua) using the C API. + +```lua +int freeluamem (lua_State *L) +{ + void *p = luamem_toexternal(L, 1, NULL); + if (p) free(p); + return 0; +} + +size_t sz = 16; +void *p = malloc(sz); +luamem_setexternal(L, p, sz, NULL); -- is persistent, so 'p' is accessible from Lua even if it leaves the stack. +luamem_setexternal(L, p, sz, freeluamem); -- is collectable, but 'p' is accessible from Lua. +luamem_setexternal(L, p, 0, NULL); -- is collectable also + +typedef struct luamem_External { + lua_CFunction callback; + size_t size; + /* private part */ + void *memory. +} luamem_External; + +luamem_External *luamem_newexternal (lua_State *L, void *mem) { + lua_ + luamem_External *lm = + (luamem_External *)lua_newuserdata(L, sizeof(luamem_External)); + lm->callback = NULL; + lm->size = 0; + lm->memory = mem; + return lm; +} + if sz == 0 then mem = "\0" end + #ifndef LUAMEMORY_NOCHECKNULLTERM + assert(mem[sz] == '\0') + #endif + local refs = debug.getregistry().LuaMemoryExternalWeakRefs + --assert(getmetatable(refs).__mode == "v") + local key = lightuserdata(mem) + local memory = refs[key] + if memory == nil then + memory = external{ + memory = mem, + size = sz, + gccallback = gc + } + refs[key] = memory + else + memory.size = sz + memory.gccallback = gc + end + lua_pushvalue(memory) -- only value left pushed to the stack +} +int luamem_getexternal(lua_State *L, void *mem) { + local reg = debug.getregistry().LuaMemoryPointerWeakRegistry + assert(getmetatable(reg).__mode == "v") + local found = reg[lightuserdata(mem)] + if getmetatable(found) == LuaMemoryExternalMetatable then + lua_pushvalue(found) -- only value left pushed to the stack + return true + end + return false +} +void *luamem_toexternal(lua_State *L, int idx, size_t *sz); +int luamem_isexternal(lua_State *L, int idx); + +#define ldata_newref(L,p,s) (luamem_setexternal(L, p, s, NULL), p) +#define ldata_unref(L,p) luamem_setexternal(L, p, 0, NULL) +``` Documentation ------------- diff --git a/demo/buffer.lua b/demo/buffer.lua index 41fe8c9..3e77931 100644 --- a/demo/buffer.lua +++ b/demo/buffer.lua @@ -9,7 +9,7 @@ for i = 1, buffer.size(b) do print(i, buffer.get(b, i)) end --- iteractively fill the buffer +-- iterate to fill the buffer for i = 1, buffer.size(b) do print(i, buffer.set(b, i, 2*i)) end @@ -17,7 +17,7 @@ end -- sets 4th, 5th and 6th bytes in the buffer buffer.set(b, 4, 0xff, 0xff, 0xff) --- move 3 bytes from position 4 to position 1 +-- copy 3 bytes from position 4 to position 1 buffer.fill(b, b, 1, 3, 4) -- clear the positions after the 3 first bytes diff --git a/doc/manual.md b/doc/manual.md index eda8c28..971f9f0 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -60,7 +60,7 @@ Thus, the last byte is at position -1, and so on. In this manual we will refer to byte in position `i` as `b[i]`. This library provides all its functions inside the table `buffer`. -It also sets a metatable for the buffers where the __index field points to the `buffer` table. +It also sets a metatable for the buffers where the `__index` field points to the `buffer` table. Therefore, you can use the library functions in object-oriented style. For instance, `buffer.get(b,i)` can be written as `b:get(i)`, where `b` is a buffer. @@ -80,10 +80,6 @@ Returns the new buffer. Returns the size of buffer `b`. -### `buffer.tostring (b)` - -Returns a string with the contents of buffer `b`. - ### `buffer.get (b [, i [, j]])` Returns the values of bytes in buffer `b` from `i` until `j`; @@ -123,4 +119,8 @@ Returns the index of the first unwritten byte in `b`. ### `buffer.unpack (b, i, fmt)` Returns the values encoded in position `i` of buffer or string `b`, according to the format `fmt`, as in function [buffer.pack](#bufferpack-b-i-fmt-v-). -After the read values, this function also returns the index of the first unread byte in `b`. \ No newline at end of file +After the read values, this function also returns the index of the first unread byte in `b`. + +### `tostring (b)` + +Returns a string with the contents of buffer `b`. diff --git a/src/Makefile b/src/Makefile index 448ee0d..4b75e56 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,62 +8,86 @@ LUA_BIN= $(LUA_HOME)/bin # Your platform. See PLATS for possible values. PLAT= none -CC= gcc -CFLAGS= -O2 -Wall -I$(LUA_INC) $(MYCFLAGS) -LDFLAGS= -L$(LUA_LIB) $(MYLDFLAGS) -LIBS= $(MYLIBS) +CC= gcc -std=gnu99 +CFLAGS= -O2 -Wall -I$(LUA_INC) $(SYSCFLAGS) $(MYCFLAGS) +LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS) +LIBS= $(SYSLIBS) $(MYLIBS) AR= ar rcu RANLIB= ranlib RM= rm -f +SYSCFLAGS= +SYSLDFLAGS= +SYSLIBS= + MYCFLAGS= MYLDFLAGS= MYLIBS= # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= -PLATS= linux solaris macosx +PLATS= linux macosx + +MEM_O= lmemmod.o +BYT_O= lbytmod.o +API_O= lmemlib.o -LIBNAME= luabuffer +MEM_M= memory.so +BYT_M= bytes.so +API_S= libluamemlib.so +LIB_S= libluamemory.so +LIB_A= libluamemory.a -OBJ= lbuflib.o lstrlib.o lstrops.o lstraux.o -LIB= lib$(LIBNAME).a -SO= lib$(LIBNAME).so +ALL_O= $(MEM_O) $(BYT_O) $(API_O) +ALL_A= $(LIB_A) +ALL_S= $(LIB_S) $(API_S) +ALL_M= $(MEM_M) $(BYT_M) +ALL_T= $(ALL_A) $(ALL_S) default: $(PLAT) -all: a so +all: $(ALL_T) -o: $(OBJ) +o: $(ALL_O) -a: $(LIB) +a: $(ALL_A) -so: $(SO) +so: $(ALL_S) -# Static Libraies +m: $(ALL_M) -$(LIB): $(OBJ) - $(AR) $@ $^ - $(RANLIB) $@ +$(API_S): $(API_O) + $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) + +$(MEM_M): $(MEM_O) $(API_S) + $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) -# Dynamic Libraies +$(BYT_M): $(BYT_O) $(API_S) + $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) -$(SO): $(OBJ) - $(LD) $(LDFLAGS) -o $@ $^ +$(LIB_S): $(LIB_O) $(API_S) $(ALL_M) + $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) + +$(LIB_A): $(ALL_O) + $(AR) $@ $^ + $(RANLIB) $@ clean: - $(RM) $(OBJ) $(LIB) $(SO) + $(RM) $(ALL_T) $(ALL_O) + +depend: + @$(CC) $(CFLAGS) -MM l*.c echo: - @echo "CC = $(CC)" - @echo "CFLAGS = $(CFLAGS)" - @echo "AR = $(AR)" - @echo "RANLIB = $(RANLIB)" - @echo "RM = $(RM)" - @echo "MYCFLAGS = $(MYCFLAGS)" - @echo "MYLDFLAGS = $(MYLDFLAGS)" - @echo "MYLIBS = $(MYLIBS)" + @echo "PLAT= $(PLAT)" + @echo "CC= $(CC)" + @echo "CFLAGS= $(CFLAGS)" + @echo "LDFLAGS= $(SYSLDFLAGS)" + @echo "LIBS= $(LIBS)" + @echo "AR= $(AR)" + @echo "RANLIB= $(RANLIB)" + @echo "RM= $(RM)" # Convenience targets for usual platforms ALL= all @@ -76,10 +100,6 @@ linux: $(MAKE) $(ALL) LD="gcc" MYCFLAGS="-fpic $(MYCFLAGS)" \ MYLDFLAGS="-Wl,-E -O -shared $(MYLDFLAGS)" -solaris: - $(MAKE) $(ALL) LD="gcc" MYCFLAGS="-fpic $(MYCFLAGS)" \ - MYLDFLAGS="-O -shared $(MYLDFLAGS)" - macosx: $(MAKE) $(ALL) MYCFLAGS="-fno-common $(MYCFLAGS)" \ MYLDFLAGS="-bundle -undefined dynamic_lookup $(MYLDFLAGS)" \ diff --git a/src/lstrlib.c b/src/lbytmod.c similarity index 87% rename from src/lstrlib.c rename to src/lbytmod.c index 66ed6ba..9bdd662 100644 --- a/src/lstrlib.c +++ b/src/lbytmod.c @@ -1,16 +1,32 @@ /* -** $Id$ -** NOTE: most of the code in here is copied from the source of Lua 5.3.1 by -** R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. -** -** Stream support for the Lua language -** See Copyright Notice in lstraux.h +* NOTE: most of the code below is copied from the source of Lua 5.3.1 by +* R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. +* +* Copyright (C) 1994-2015 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. */ -#define lstrlib_c +#define lbytmod_c -#include "lstraux.h" -#include "lstrops.h" +#include "lmemlib.h" #include #include @@ -38,7 +54,7 @@ static int str_len (lua_State *L) { size_t l; - luabuf_checkstream(L, 1, &l); + luamem_checkstring(L, 1, &l); lua_pushinteger(L, (lua_Integer)l); return 1; } @@ -46,15 +62,15 @@ static int str_len (lua_State *L) { static int str_isbuffer (lua_State *L) { - lua_pushboolean(L, luabuf_isbuffer(L, 1)); + lua_pushboolean(L, luamem_isalloc(L, 1)); return 1; } static int str_diff (lua_State *L) { size_t l1, l2; - const char *s1 = luabuf_checkstream(L, 1, &l1); - const char *s2 = luabuf_checkstream(L, 2, &l2); + const char *s1 = luamem_checkstring(L, 1, &l1); + const char *s2 = luamem_checkstring(L, 2, &l2); size_t i, n=(l1 (lua_Integer)l) end = l; if (start == 1 && end == l && lua_type(L, 1) == LUA_TSTRING) @@ -102,7 +118,7 @@ static int str_reverse (lua_State *L) { size_t l; int op = luaL_checkoption(L, 1, NULL, inoutops); if (op == 2) { /* inplace */ - char *p = luabuf_checkbuffer(L, 2, &l); + char *p = luamem_checkmemory(L, 2, &l); size_t i; for (i = 0; i <= l/2; ++i) { size_t j = l - i - 1; @@ -111,14 +127,14 @@ static int str_reverse (lua_State *L) { p[j] = t; } } else { - const char *s = luabuf_checkstream(L, 2, &l); + const char *s = luamem_checkstring(L, 2, &l); if (op == 0) { /* string */ - luabuf_Buffer b; - char *p = luabuf_buffinitsize(L, &b, l); + luamem_Buffer b; + char *p = luamem_buffinitsize(L, &b, l); copyreverse(p, s, l); - luabuf_pushresultsize(&b, l); + luamem_pushresultsize(&b, l); } else { /* buffer */ - char *p = luabuf_newbuffer(L, l); + char *p = luamem_newalloc(L, l); copyreverse(p, s, l); } } @@ -136,15 +152,15 @@ static void copylower (char *p, const char *s, size_t l) { static int str_lower (lua_State *L) { size_t l; int op = luaL_checkoption(L, 1, NULL, inoutops); - const char *s = luabuf_checkstream(L, 2, &l); + const char *s = luamem_checkstring(L, 2, &l); if (op == 0) { /* string */ - luabuf_Buffer b; - char *p = luabuf_buffinitsize(L, &b, l); + luamem_Buffer b; + char *p = luamem_buffinitsize(L, &b, l); copylower(p, s, l); - luabuf_pushresultsize(&b, l); + luamem_pushresultsize(&b, l); } else { - char *p = (op == 1) ? luabuf_newbuffer(L, l) - : luabuf_checkbuffer(L, 2, NULL); + char *p = (op == 1) ? luamem_newalloc(L, l) + : luamem_checkmemory(L, 2, NULL); copylower(p, s, l); } return 1; @@ -161,15 +177,15 @@ static void copyupper (char *p, const char *s, size_t l) { static int str_upper (lua_State *L) { size_t l; int op = luaL_checkoption(L, 1, NULL, inoutops); - const char *s = luabuf_checkstream(L, 2, &l); + const char *s = luamem_checkstring(L, 2, &l); if (op == 0) { /* string */ - luabuf_Buffer b; - char *p = luabuf_buffinitsize(L, &b, l); + luamem_Buffer b; + char *p = luamem_buffinitsize(L, &b, l); copyupper(p, s, l); - luabuf_pushresultsize(&b, l); + luamem_pushresultsize(&b, l); } else { - char *p = (op == 1) ? luabuf_newbuffer(L, l) - : luabuf_checkbuffer(L, 2, NULL); + char *p = (op == 1) ? luamem_newalloc(L, l) + : luamem_checkmemory(L, 2, NULL); copyupper(p, s, l); } return 1; @@ -196,23 +212,23 @@ static void copyrepeat (char *p, static int str_rep (lua_State *L) { size_t l, lsep; int op = luaL_checkoption(L, 1, NULL, outops); - const char *s = luabuf_checkstream(L, 2, &l); + const char *s = luamem_checkstring(L, 2, &l); lua_Integer n = luaL_checkinteger(L, 3); const char *sep = luaL_optlstring(L, 4, "", &lsep); if (n <= 0) { if (op == 0) lua_pushliteral(L, ""); - else luabuf_newbuffer(L, 0); + else luamem_newalloc(L, 0); } else if (l + lsep < l || l + lsep > LUABUF_MAXSIZE / n) /* may overflow? */ return luaL_error(L, "resulting string too large"); else { size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; if (op == 0) { /* string */ - luabuf_Buffer b; - char *p = luabuf_buffinitsize(L, &b, totallen); + luamem_Buffer b; + char *p = luamem_buffinitsize(L, &b, totallen); copyrepeat(p, s, l, sep, lsep, n); - luabuf_pushresultsize(&b, totallen); + luamem_pushresultsize(&b, totallen); } else { - char *p = luabuf_newbuffer(L, totallen); + char *p = luamem_newalloc(L, totallen); copyrepeat(p, s, l, sep, lsep, n); } } @@ -222,8 +238,8 @@ static int str_rep (lua_State *L) { static int str_byte (lua_State *L) { size_t l; - const char *s = luabuf_checkstream(L, 1, &l); - return luastreamI_str2byte(L, s, l); + const char *s = luamem_checkstring(L, 1, &l); + return luamem_str2byte(L, s, l); } @@ -231,13 +247,13 @@ static int str_char (lua_State *L) { int n = lua_gettop(L)-1; /* number of bytes */ int op = luaL_checkoption(L, 1, NULL, outops); if (op == 0) { - luabuf_Buffer b; - char *p = luabuf_buffinitsize(L, &b, n); - luastreamI_code2char(L, 2, p, n); - luabuf_pushresultsize(&b, n); + luamem_Buffer b; + char *p = luamem_buffinitsize(L, &b, n); + luamem_code2char(L, 2, p, n); + luamem_pushresultsize(&b, n); } else { - char *p = luabuf_newbuffer(L, n); - luastreamI_code2char(L, 2, p, n); + char *p = luamem_newalloc(L, n); + luamem_code2char(L, 2, p, n); } return 1; } @@ -245,22 +261,22 @@ static int str_char (lua_State *L) { static int writer (lua_State *L, const void *b, size_t size, void *B) { (void)L; - luabuf_addstream((luabuf_Buffer *) B, (const char *)b, size); + luamem_addlstring((luamem_Buffer *) B, (const char *)b, size); return 0; } static int str_dump (lua_State *L) { - luabuf_Buffer b; + luamem_Buffer b; int strip = lua_toboolean(L, 3); int op = luaL_checkoption(L, 1, NULL, outops); luaL_checktype(L, 2, LUA_TFUNCTION); lua_settop(L, 1); - luabuf_buffinit(L,&b); + luamem_buffinit(L,&b); if (lua_dump(L, writer, &b, strip) != 0) return luaL_error(L, "unable to dump given function"); - if (op == 0) luabuf_pushresult(&b); - else luabuf_pushresbuf(&b); + if (op == 0) luamem_pushresult(&b); + else luamem_pushresbuf(&b); return 1; } @@ -656,9 +672,9 @@ static int nospecials (const char *p, size_t l) { static int str_find_aux (lua_State *L, int find) { size_t ls, lp; - const char *s = luabuf_checkstream(L, 1, &ls); - const char *p = luabuf_checkstream(L, 2, &lp); - lua_Integer init = luastreamI_posrelat(luaL_optinteger(L, 3, 1), ls); + const char *s = luamem_checkstring(L, 1, &ls); + const char *p = luamem_checkstring(L, 2, &lp); + lua_Integer init = luamem_posrelat(luaL_optinteger(L, 3, 1), ls); if (init < 1) init = 1; else if (init > (lua_Integer)ls + 1) { /* start after string's end? */ lua_pushnil(L); /* cannot find anything */ @@ -719,8 +735,8 @@ static int str_match (lua_State *L) { static int gmatch_aux (lua_State *L) { MatchState ms; size_t ls, lp; - const char *s = luabuf_tostream(L, lua_upvalueindex(1), &ls); - const char *p = luabuf_tostream(L, lua_upvalueindex(2), &lp); + const char *s = luamem_tostring(L, lua_upvalueindex(1), &ls); + const char *p = luamem_tostring(L, lua_upvalueindex(2), &lp); const char *src; ms.L = L; ms.matchdepth = MAXCCALLS; @@ -746,8 +762,8 @@ static int gmatch_aux (lua_State *L) { static int str_gmatch (lua_State *L) { - luabuf_checkstream(L, 1, NULL); - luabuf_checkstream(L, 2, NULL); + luamem_checkstring(L, 1, NULL); + luamem_checkstring(L, 2, NULL); lua_settop(L, 2); lua_pushinteger(L, 0); lua_pushcclosure(L, gmatch_aux, 3); @@ -755,23 +771,23 @@ static int str_gmatch (lua_State *L) { } -static void add_s (MatchState *ms, luabuf_Buffer *b, const char *s, +static void add_s (MatchState *ms, luamem_Buffer *b, const char *s, const char *e) { size_t l, i; lua_State *L = ms->L; - const char *news = luabuf_tostream(L, 3, &l); + const char *news = luamem_tostring(L, 3, &l); for (i = 0; i < l; i++) { if (news[i] != L_ESC) - luabuf_addchar(b, news[i]); + luamem_addchar(b, news[i]); else { i++; /* skip ESC */ if (!isdigit(uchar(news[i]))) { if (news[i] != L_ESC) luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); - luabuf_addchar(b, news[i]); + luamem_addchar(b, news[i]); } else if (news[i] == '0') - luabuf_addstream(b, s, e - s); + luamem_addlstring(b, s, e - s); else { push_onecapture(ms, news[i] - '1', s, e); luaL_tolstring(L, -1, NULL); /* if number, convert it to string */ @@ -784,7 +800,7 @@ static void add_s (MatchState *ms, luabuf_Buffer *b, const char *s, static void add_value (MatchState *ms, - luabuf_Buffer *b, + luamem_Buffer *b, const char *s, const char *e, int tr) { @@ -820,18 +836,18 @@ static void add_value (MatchState *ms, static int str_gsub (lua_State *L) { size_t srcl, lp; int op = luaL_checkoption(L, 1, NULL, outops); - const char *src = luabuf_checkstream(L, 2, &srcl); - const char *p = luabuf_checkstream(L, 3, &lp); + const char *src = luamem_checkstring(L, 2, &srcl); + const char *p = luamem_checkstring(L, 3, &lp); int tr = lua_type(L, 4); lua_Integer max_s = luaL_optinteger(L, 5, srcl + 1); int anchor = (*p == '^'); lua_Integer n = 0; MatchState ms; - luabuf_Buffer b; + luamem_Buffer b; luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, "string/function/table expected"); - luabuf_buffinit(L, &b); + luamem_buffinit(L, &b); if (anchor) { p++; lp--; /* skip anchor character */ } @@ -852,13 +868,13 @@ static int str_gsub (lua_State *L) { if (e && e>src) /* non empty match? */ src = e; /* skip it */ else if (src < ms.src_end) - luabuf_addchar(&b, *src++); + luamem_addchar(&b, *src++); else break; if (anchor) break; } - luabuf_addstream(&b, src, ms.src_end-src); - if (op == 0) luabuf_pushresult(&b); - else luabuf_pushresbuf(&b); + luamem_addlstring(&b, src, ms.src_end-src); + if (op == 0) luamem_pushresult(&b); + else luamem_pushresbuf(&b); lua_pushinteger(L, n); /* number of substitutions */ return 2; } @@ -886,14 +902,14 @@ static int str_gsub (lua_State *L) { #define MAX_FORMAT (sizeof(FLAGS) + 2 + 10) -static void addquoted (lua_State *L, luabuf_Buffer *b, int arg) { +static void addquoted (lua_State *L, luamem_Buffer *b, int arg) { size_t l; - const char *s = luabuf_checkstream(L, arg, &l); - luabuf_addchar(b, '"'); + const char *s = luamem_checkstring(L, arg, &l); + luamem_addchar(b, '"'); while (l--) { if (*s == '"' || *s == '\\' || *s == '\n') { - luabuf_addchar(b, '\\'); - luabuf_addchar(b, *s); + luamem_addchar(b, '\\'); + luamem_addchar(b, *s); } else if (*s == '\0' || iscntrl(uchar(*s))) { char buff[10]; @@ -901,13 +917,13 @@ static void addquoted (lua_State *L, luabuf_Buffer *b, int arg) { sprintf(buff, "\\%d", (int)uchar(*s)); else sprintf(buff, "\\%03d", (int)uchar(*s)); - luabuf_addstring(b, buff); + luamem_addstring(b, buff); } else - luabuf_addchar(b, *s); + luamem_addchar(b, *s); s++; } - luabuf_addchar(b, '"'); + luamem_addchar(b, '"'); } static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { @@ -952,16 +968,16 @@ static int str_format (lua_State *L) { int op = luaL_checkoption(L, arg++, NULL, outops); const char *strfrmt = luaL_checklstring(L, arg, &sfl); const char *strfrmt_end = strfrmt+sfl; - luabuf_Buffer b; - luabuf_buffinit(L, &b); + luamem_Buffer b; + luamem_buffinit(L, &b); while (strfrmt < strfrmt_end) { if (*strfrmt != L_ESC) - luabuf_addchar(&b, *strfrmt++); + luamem_addchar(&b, *strfrmt++); else if (*++strfrmt == L_ESC) - luabuf_addchar(&b, *strfrmt++); /* %% */ + luamem_addchar(&b, *strfrmt++); /* %% */ else { /* format item */ char form[MAX_FORMAT]; /* to store the format ('%...') */ - char *buff = luabuf_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ + char *buff = luamem_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ int nb = 0; /* number of bytes in added item */ if (++arg > top) luaL_argerror(L, arg, "no value"); @@ -1014,8 +1030,8 @@ static int str_format (lua_State *L) { luaL_addsize(&b, nb); } } - if (op == 0) luabuf_pushresult(&b); - else luabuf_pushresbuf(&b); + if (op == 0) luamem_pushresult(&b); + else luamem_pushresbuf(&b); return 1; } @@ -1219,9 +1235,9 @@ static KOption getdetails (Header *h, size_t totalsize, ** the size of a Lua integer, correcting the extra sign-extension ** bytes if necessary (by default they would be zeros). */ -static void packint (luabuf_Buffer *b, lua_Unsigned n, +static void packint (luamem_Buffer *b, lua_Unsigned n, int islittle, int size, int neg) { - char *buff = luabuf_prepbuffsize(b, size); + char *buff = luamem_prepbuffsize(b, size); int i; buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ for (i = 1; i < size; i++) { @@ -1255,7 +1271,7 @@ static void copywithendian (volatile char *dest, volatile const char *src, static int str_pack (lua_State *L) { - luabuf_Buffer b; + luamem_Buffer b; Header h; int arg = 1; /* current argument to pack */ int op = luaL_checkoption(L, arg++, NULL, outops); @@ -1263,13 +1279,13 @@ static int str_pack (lua_State *L) { size_t totalsize = 0; /* accumulate total size of result */ initheader(L, &h); lua_pushnil(L); /* mark to separate arguments from string buffer */ - luabuf_buffinit(L, &b); + luamem_buffinit(L, &b); while (*fmt != '\0') { int size, ntoalign; KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); totalsize += ntoalign + size; while (ntoalign-- > 0) - luabuf_addchar(&b, LUA_PACKPADBYTE); /* fill alignment */ + luamem_addchar(&b, LUA_PACKPADBYTE); /* fill alignment */ arg++; switch (opt) { case Kint: { /* signed integers */ @@ -1291,7 +1307,7 @@ static int str_pack (lua_State *L) { } case Kfloat: { /* floating-point options */ volatile Ftypes u; - char *buff = luabuf_prepbuffsize(&b, size); + char *buff = luamem_prepbuffsize(&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; @@ -1303,39 +1319,39 @@ static int str_pack (lua_State *L) { } case Kchar: { /* fixed-size string */ size_t len; - const char *s = luabuf_checkstream(L, arg, &len); + const char *s = luamem_checkstring(L, arg, &len); luaL_argcheck(L, len == (size_t)size, arg, "wrong length"); - luabuf_addstream(&b, s, size); + luamem_addlstring(&b, s, size); break; } case Kstring: { /* strings with length count */ size_t len; - const char *s = luabuf_checkstream(L, arg, &len); + const char *s = luamem_checkstring(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 */ - luabuf_addstream(&b, s, len); + luamem_addlstring(&b, s, len); totalsize += len; break; } case Kzstr: { /* zero-terminated string */ size_t len; - const char *s = luabuf_checkstream(L, arg, &len); + const char *s = luamem_checkstring(L, arg, &len); luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); - luabuf_addstream(&b, s, len); - luabuf_addchar(&b, '\0'); /* add zero at the end */ + luamem_addlstring(&b, s, len); + luamem_addchar(&b, '\0'); /* add zero at the end */ totalsize += len + 1; break; } - case Kpadding: luabuf_addchar(&b, LUA_PACKPADBYTE); /* go through */ + case Kpadding: luamem_addchar(&b, LUA_PACKPADBYTE); /* go through */ case Kpaddalign: case Knop: arg--; /* undo increment */ break; } } - if (op == 0) luabuf_pushresult(&b); - else luabuf_pushresbuf(&b); + if (op == 0) luamem_pushresult(&b); + else luamem_pushresbuf(&b); return 1; } @@ -1357,7 +1373,7 @@ static int str_packsize (lua_State *L) { switch (opt) { case Kstring: { /* strings with length count */ size_t len; - luabuf_checkstream(L, arg, &len); + luamem_checkstring(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"); @@ -1366,7 +1382,7 @@ static int str_packsize (lua_State *L) { } case Kzstr: { /* zero-terminated string */ size_t len; - const char *s = luabuf_checkstream(L, arg, &len); + const char *s = luamem_checkstring(L, arg, &len); luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); totalsize += len + 1; break; @@ -1417,8 +1433,8 @@ static int str_unpack (lua_State *L) { Header h; const char *fmt = luaL_checkstring(L, 1); size_t ld; - const char *data = luabuf_checkstream(L, 2, &ld); - size_t pos = (size_t)luastreamI_posrelat(luaL_optinteger(L, 3, 1), ld) - 1; + const char *data = luamem_checkstring(L, 2, &ld); + size_t pos = (size_t)luamem_posrelat(luaL_optinteger(L, 3, 1), ld) - 1; int n = 0; /* number of results */ luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); initheader(L, &h); @@ -1503,7 +1519,7 @@ static const luaL_Reg strlib[] = { }; -LUABUFMOD_API int luaopen_buffer_stream (lua_State *L) { +LUAMEMMOD_API int luaopen_bytes (lua_State *L) { luaL_newlib(L, strlib); return 1; } diff --git a/src/lmemlib.c b/src/lmemlib.c new file mode 100644 index 0000000..878d9c7 --- /dev/null +++ b/src/lmemlib.c @@ -0,0 +1,237 @@ +/* +** $Id$ +** Auxiliary functions for handling generic streams in Lua. +** See Copyright Notice in lstraux.h +*/ + +#define lmemlib_c + +#define LUAMEMLIB_API + +#include "lmemlib.h" + +#include + + + +LUAMEMLIB_API char *luamem_newalloc (lua_State *L, size_t l) { + char *mem = lua_newuserdata(L, l * sizeof(char)); + luaL_newmetatable(L, LUAMEM_ALLOC); + lua_setmetatable(L, -2); + return mem; +} + + +#define LUAMEM_REFREGISTRY "luamem_ReferenceRegistry" + +static int pushref (lua_State *L, char *mem) { + if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, LUAMEM_REFREGISTRY)) { + lua_pushliteral(L, "v"); + lua_setfield(L, -2, "__mode"); + lua_pushvalue(L, -1); + lua_setmetatable(L, -2); + } + lua_pushlightuserdata(L, mem); + return lua_gettable(L, -2) == LUA_TUSERDATA; +} + +static int luaunref (lua_State *L) { + luamem_Ref *ref = luamem_toref(L, 1); + if (ref && ref->unref) ref->unref(ref->memory, ref->len); + return 0; +} + +LUAMEMLIB_API void luamem_pushrefmt (lua_State *L) { + if (luaL_newmetatable(L, LUAMEM_REF)) { + lua_pushcfunction(L, luaunref); + lua_setfield(L, -2, "__gc"); + } +} + +LUAMEMLIB_API luamem_Ref *luamem_pushref (lua_State *L, char *mem) { + luamem_Ref *ref; + if (pushref(L, mem)) { + ref = (luamem_Ref *)lua_touserdata(L, -1); + } else { + lua_pop(L, 1); + ref = (luamem_Ref *)lua_newuserdata(L, sizeof(luamen_Ref)); + ref->len = 0; + ref->unref = NULL; + ref->memory = mem; + luamem_pushrefmt(L); + lua_setmetatable(L, -2); + lua_pushlightuserdata(L, mem); + lua_pushvalue(L, -2); + lua_settable(L, -4) + } + lua_remove(L, -2); + return ref; +} + +LUAMEMLIB_API luamem_Ref *luamem_getref (lua_State *L, char *mem) { + luamem_Ref *ref; + pushref(L, mem); + ref = (luamem_Ref *)lua_touserdata(L, -1); + lua_pop(L, 2); + return ref; +} + + +LUAMEMLIB_API char *luamem_tomemory (lua_State *L, int idx, size_t *len) { + void *p = lua_touserdata(L, idx); + if (p) { /* value is a userdata? */ + if (lua_getmetatable(L, idx)) { /* does it have a metatable? */ + char *mem; + luaL_getmetatable(L, LUAMEM_ALLOC); /* get allocated memory metatable */ + if (lua_rawequal(L, -1, -2)) { /* is the same? */ + if (len) *len = lua_rawlen(L, idx); + mem = (char *)p; + } else { + lua_pop(L, 1); /* remove allocated memory metatable */ + luaL_getmetatable(L, LUAMEM_REF); /* get referenced memory metatable */ + if (lua_rawequal(L, -1, -2)) { + luamem_Ref *ref = (luamem_Ref *)p; + if (len) *len = ref->len; + mem = ref->memory; + } else { + if (len) *len = 0; + mem = NULL; + } + } + lua_pop(L, 2); /* remove both metatables */ + return mem; + } + } + if (len) *len = 0; + return NULL; +} + +LUAMEMLIB_API char *luamem_checkmemory (lua_State *L, int idx, size_t *len) { + char *mem = luamem_tomemory(L, idx, len); + if (!mem) luaL_argerror(L, idx, "memory expected"); + return mem; +} + + +LUAMEMLIB_API int luamem_isstring (lua_State *L, int idx) { + return (lua_isstring(L, idx) || luamem_ismemory(L, idx)); +} + +LUAMEMLIB_API const char *luamem_tostring (lua_State *L, int idx, size_t *len) { + if (lua_isstring(L, idx)) return lua_tolstring(L, idx, len); + return luamem_tomemory(L, idx, len); +} + +LUAMEMLIB_API const char *luamem_checkstring (lua_State *L, int idx, size_t *len) { + const char *s = luamem_tostring(L, idx, len); + if (!s) luaL_argerror(L, idx, "string expected"); + return s; +} + + +/* +* NOTE: most of the code below is copied from the source of Lua 5.3.1 by +* R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. +* +* Copyright (C) 1994-2015 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. +*/ + + +/* macro to 'unsign' a character */ +#define uchar(c) ((unsigned char)(c)) + + +/* translate a relative string position: negative means back from end */ +LUAMEMLIB_API lua_Integer luamem_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; +} + +LUAMEMLIB_API int luamem_str2byte (lua_State *L, const char *s, size_t l) { + lua_Integer posi = luamem_posrelat(luaL_optinteger(L, 2, 1), l); + lua_Integer pose = luamem_posrelat(luaL_optinteger(L, 3, posi), l); + int n, i; + if (posi < 1) posi = 1; + if (pose > (lua_Integer)l) pose = l; + if (posi > pose) return 0; /* empty interval; return no values */ + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + luaL_checkstack(L, n, "string slice too long"); + for (i=0; ib != (B)->initb) + + +LUAMEMLIB_API void luamem_pushresbuf (luamem_Buffer *B) { + lua_State *L = B->L; + if (!buffonstack(B) || B->n < B->size) { + char *p = (char *)luamem_newalloc(L, B->n * sizeof(char)); + /* move content to new buffer */ + memcpy(p, B->b, B->n * sizeof(char)); + if (buffonstack(B)) + lua_remove(L, -2); /* remove old buffer */ + } +} + + +LUAMEMLIB_API void luamem_pushresbufsize (luamem_Buffer *B, size_t sz) { + luaL_addsize(B, sz); + luamem_pushresbuf(B); +} + + +LUAMEMLIB_API void luamem_addvalue (luamem_Buffer *B) { + lua_State *L = B->L; + size_t l; + const char *s = luamem_tostring(L, -1, &l); + if (buffonstack(B)) + lua_insert(L, -2); /* put value below buffer */ + luamem_addlstring(B, s, l); + lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */ +} + +/* }====================================================== */ diff --git a/src/lmemlib.h b/src/lmemlib.h new file mode 100644 index 0000000..1cd163a --- /dev/null +++ b/src/lmemlib.h @@ -0,0 +1,101 @@ +/* +** $Id$ +** Lua Stream - Generic stream support for the Lua language +** Renato Maia, Tecgraf/PUC-Rio (http://www.tecgraf.puc-rio.br/~maia) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lstraux_h +#define lstraux_h + + +#include +#include + + + +#ifndef LUAMEMLIB_API +#define LUAMEMLIB_API LUALIB_API +#endif + +#ifndef LUAMEMMOD_API +#define LUAMEMMOD_API LUAMOD_API +#endif + + + +#define LUAMEM_ALLOC "char[]" +#define LUAMEM_REF "luamem_Ref" + + +typedef void (*luamem_Unref) (char *mem, size_t len); + +typedef struct luamem_Ref { + size_t len; + luamem_Unref unref; + /* private part */ + char *memory; +} luamem_Ref; + + +#define luamem_toref(L,I) (luaL_testudata(L,I,LUAMEM_REF)) +#define luamem_isref(L,I) (luamem_toref(L,I) != NULL) +#define luamem_isalloc(L,I) (luaL_testudata(L,I,LUAMEM_ALLOC) != NULL) +#define luamem_ismemory(L,I) (luaL_tomemory(L,I,NULL) != NULL) + + +LUAMEMLIB_API char * (luamem_newalloc) (lua_State *L, size_t len); + +LUAMEMLIB_API void (luamem_pushrefmt) (lua_State *L); +LUAMEMLIB_API luamem_Ref *(luamem_pushref) (lua_State *L, char *mem); +LUAMEMLIB_API luamem_Ref *(luamem_getref) (lua_State *L, char *mem); +LUAMEMLIB_API luamem_Ref *(luamem_toref) (lua_State *L, int idx); + +LUAMEMLIB_API char * (luamem_tomemory) (lua_State *L, int idx, size_t *len); +LUAMEMLIB_API char * (luamem_checkmemory) (lua_State *L, int idx, size_t *len); + +LUAMEMLIB_API int (luamem_isstring) (lua_State *L, int idx); +LUAMEMLIB_API const char *(luamem_tostring) (lua_State *L, int idx, size_t *len); +LUAMEMLIB_API const char *(luamem_checkstring) (lua_State *L, int idx, size_t *len); + + +/* +** 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 LUABUF_MAXSIZE \ + (sizeof(size_t) < sizeof(int) ? (~(size_t)0) : (size_t)(INT_MAX)) + +LUAMEMLIB_API lua_Integer luamem_posrelat (lua_Integer pos, size_t len); +LUAMEMLIB_API int luamem_str2byte (lua_State *L, const char *s, size_t l); +LUAMEMLIB_API void luamem_code2char (lua_State *L, int idx, char *p, int n); + + +/* +** {====================================================== +** Lua stack's buffer support +** ======================================================= +*/ + +#define luamem_Buffer luaL_Buffer +#define luamem_addchar luaL_addchar +#define luamem_addsize luaL_addsize +#define luamem_prepbuffsize luaL_prepbuffsize +#define luamem_prepbuffer luaL_prepbuffer +#define luamem_addlstring luaL_addlstring +#define luamem_addstring luaL_addstring +#define luamem_pushresult luaL_pushresult +#define luamem_pushresultsize luaL_pushresultsize +#define luamem_buffinit luaL_buffinit +#define luamem_buffinitsize luaL_buffinitsize + + +LUAMEMLIB_API void (luamem_addvalue) (luamem_Buffer *B); +LUAMEMLIB_API void (luamem_pushresbuf) (luamem_Buffer *B); +LUAMEMLIB_API void (luamem_pushresbufsize) (luamem_Buffer *B, size_t sz); + +/* }====================================================== */ + + +#endif diff --git a/src/lbuflib.c b/src/lmemmod.c similarity index 81% rename from src/lbuflib.c rename to src/lmemmod.c index b78f1a0..9603205 100644 --- a/src/lbuflib.c +++ b/src/lmemmod.c @@ -1,19 +1,12 @@ -/* -** $Id$ -** Stream support for the Lua language -** See Copyright Notice in lstraux.h -*/ +#define lmemmod_c -#define lbuflib_c - -#include "lstraux.h" -#include "lstrops.h" +#include "lmemlib.h" #include -static int buf_create (lua_State *L) { +static int mem_create (lua_State *L) { char *p; size_t lb; const char *s = NULL; @@ -24,9 +17,9 @@ static int buf_create (lua_State *L) { lb = (size_t)sz; } else { lua_Integer posi, pose; - s = luabuf_checkstream(L, 1, &lb); - posi = luastreamI_posrelat(luaL_optinteger(L, 2, 1), lb); - pose = luastreamI_posrelat(luaL_optinteger(L, 3, -1), lb); + s = luamem_checkstring(L, 1, &lb); + posi = luamem_posrelat(luaL_optinteger(L, 2, 1), lb); + pose = luamem_posrelat(luaL_optinteger(L, 3, -1), lb); if (posi < 1) posi = 1; if (pose > (lua_Integer)lb) pose = lb; if (posi > pose) { @@ -39,48 +32,48 @@ static int buf_create (lua_State *L) { s += posi-1; } } - p = luabuf_newbuffer(L, lb); + p = luamem_newalloc(L, lb); if (s) memcpy(p, s, lb * sizeof(char)); return 1; } -static int buf_len (lua_State *L) { +static int mem_len (lua_State *L) { size_t lb; - luabuf_checkbuffer(L, 1, &lb); + luamem_checkmemory(L, 1, &lb); lua_pushinteger(L, (lua_Integer)lb); return 1; } -static int buf_tostring (lua_State *L) { +static int mem_tostring (lua_State *L) { size_t lb; - const char *s = luabuf_checkbuffer(L, 1, &lb); + const char *s = luamem_checkmemory(L, 1, &lb); if (lb>0) lua_pushlstring(L, s, lb); else lua_pushliteral(L, ""); return 1; } -static int buf_get (lua_State *L) { +static int mem_get (lua_State *L) { size_t lb; - const char *s = luabuf_checkbuffer(L, 1, &lb); - return luastreamI_str2byte(L, s, lb); + const char *s = luamem_checkmemory(L, 1, &lb); + return luamem_str2byte(L, s, lb); } -static int buf_set (lua_State *L) { +static int mem_set (lua_State *L) { size_t lb; int n = lua_gettop(L)-2; /* number of bytes */ - char *p = luabuf_checkbuffer(L, 1, &lb); - lua_Integer i = luastreamI_posrelat(luaL_checkinteger(L, 2), lb); + char *p = luamem_checkmemory(L, 1, &lb); + lua_Integer i = luamem_posrelat(luaL_checkinteger(L, 2), lb); luaL_argcheck(L, 1 <= i && i <= (lua_Integer)lb, 2, "index out of bounds"); lb = 1+lb-i; - luastreamI_code2char(L, 3, p+i-1, n= (int)sizeof(size_t) || len < ((size_t)1 << (size * NB)), arg, "string length does not fit in given size"); @@ -455,7 +474,7 @@ static int buf_pack (lua_State *L) { } case Kzstr: { /* zero-terminated string */ size_t len; - const char *s = luabuf_checkstream(L, arg, &len); + const char *s = luamem_checkstring(L, arg, &len); luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); if (!packstream(&buff, &i, lb, s, len) || !packchar(&buff, &i, lb, '\0')) return packfailed(L, i, arg); @@ -512,11 +531,11 @@ static lua_Integer unpackint (lua_State *L, const char *str, } -static int buf_unpack (lua_State *L) { +static int mem_unpack (lua_State *L) { Header h; size_t ld; - const char *data = luabuf_checkbuffer(L, 1, &ld); - size_t pos = (size_t)luastreamI_posrelat(luaL_checkinteger(L, 2), ld) - 1; + const char *data = luamem_checkmemory(L, 1, &ld); + size_t pos = (size_t)luamem_posrelat(luaL_checkinteger(L, 2), ld) - 1; const char *fmt = luaL_checkstring(L, 3); int n = 0; /* number of results */ luaL_argcheck(L, pos <= ld, 2, "initial position out of bounds"); @@ -579,36 +598,37 @@ static int buf_unpack (lua_State *L) { -static const luaL_Reg buflib[] = { - {"create", buf_create}, - {"fill", buf_fill}, - {"get", buf_get}, - {"len", buf_len}, - {"set", buf_set}, - {"pack", buf_pack}, - {"unpack", buf_unpack}, +static const luaL_Reg lib[] = { + {"create", mem_create}, + {"fill", mem_fill}, + {"get", mem_get}, + {"len", mem_len}, + {"set", mem_set}, + {"pack", mem_pack}, + {"unpack", mem_unpack}, {NULL, NULL} }; -static const luaL_Reg bufmeta[] = { - {"__len", buf_len}, - {"__tostring", buf_tostring}, +static const luaL_Reg meta[] = { + {"__len", mem_len}, + {"__tostring", mem_tostring}, {NULL, NULL} }; -static void createmetatable (lua_State *L) { - if (!luaL_getmetatable(L, LUABUF_BUFFER)) { - lua_pop(L, 1); /* pop 'nil' */ - luaL_newmetatable(L, LUABUF_BUFFER); - } - luaL_setfuncs(L, bufmeta, 0); /* add buffer methods to new metatable */ +static void setupmeta (lua_State *L) { + luaL_setfuncs(L, meta, 0); /* add metamethods to new metatable */ + lua_pushvalue(L, -2); /* push library */ + lua_setfield(L, -2, "__index"); /* metatable.__index = library */ lua_pop(L, 1); /* pop new metatable */ } -LUABUFMOD_API int luaopen_buffer (lua_State *L) { - luaL_newlib(L, buflib); - createmetatable(L); +LUAMEMMOD_API int luaopen_memory (lua_State *L) { + luaL_newlib(L, lib); + luaL_newmetatable(L, LUAMEM_ALLOC); + setupmeta(L); + luamem_pushrefmt(L); + setupmeta(L); return 1; } diff --git a/src/lstraux.c b/src/lstraux.c deleted file mode 100644 index b4282e1..0000000 --- a/src/lstraux.c +++ /dev/null @@ -1,100 +0,0 @@ -/* -** $Id$ -** Auxiliary functions for handling generic streams in Lua. -** See Copyright Notice in lstraux.h -*/ - -#define lstraux_c -#define LUABUFLIB_API - -#include "lstraux.h" - -#include - - - -LUABUFLIB_API char *luabuf_newbuffer (lua_State *L, size_t l) { - char *p = lua_newuserdata(L, l * sizeof(char)); - if (!luaL_getmetatable(L, LUABUF_BUFFER)) { - lua_pop(L, 1); /* pop 'nil' */ - luaL_newmetatable(L, LUABUF_BUFFER); - } - lua_setmetatable(L, -2); - return p; -} - - -LUABUFLIB_API char *luabuf_tobuffer (lua_State *L, int idx, size_t *len) { - char *b = luaL_testudata(L, idx, LUABUF_BUFFER); - if (b && len) *len = lua_rawlen(L, idx); - return b; -} - - -LUABUFLIB_API int luabuf_isstream (lua_State *L, int idx) { - return (lua_isstring(L, idx) || luabuf_isbuffer(L, idx)); -} - - -LUABUFLIB_API const char *luabuf_tostream (lua_State *L, int idx, size_t *len) { - if (lua_isstring(L, idx)) return lua_tolstring(L, idx, len); - return luabuf_tobuffer(L, idx, len); -} - - -LUABUFLIB_API char *luabuf_checkbuffer (lua_State *L, int idx, size_t *len) { - char *b = luaL_checkudata(L, idx, LUABUF_BUFFER); - if (len) *len = lua_rawlen(L, idx); - return b; -} - - -LUABUFLIB_API const char *luabuf_checkstream (lua_State *L, int arg, size_t *len) { - const char *s = luabuf_tostream(L, arg, len); - if (!s) luaL_argerror(L, arg, "stream expected"); - return s; -} - - -/* -** {====================================================== -** Generic Buffer manipulation -** ======================================================= -*/ - -/* -** check whether buffer is using a userdata on the stack as a temporary -** buffer -*/ -#define buffonstack(B) ((B)->b != (B)->initb) - - -LUABUFLIB_API void luabuf_pushresbuf (luabuf_Buffer *B) { - lua_State *L = B->L; - if (!buffonstack(B) || B->n < B->size) { - char *p = (char *)luabuf_newbuffer(L, B->n * sizeof(char)); - /* move content to new buffer */ - memcpy(p, B->b, B->n * sizeof(char)); - if (buffonstack(B)) - lua_remove(L, -2); /* remove old buffer */ - } -} - - -LUABUFLIB_API void luabuf_pushresbufsize (luabuf_Buffer *B, size_t sz) { - luaL_addsize(B, sz); - luabuf_pushresbuf(B); -} - - -LUABUFLIB_API void luabuf_addvalue (luabuf_Buffer *B) { - lua_State *L = B->L; - size_t l; - const char *s = luabuf_tostream(L, -1, &l); - if (buffonstack(B)) - lua_insert(L, -2); /* put value below buffer */ - luabuf_addstream(B, s, l); - lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */ -} - -/* }====================================================== */ diff --git a/src/lstraux.h b/src/lstraux.h deleted file mode 100644 index 2a8c83c..0000000 --- a/src/lstraux.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -** $Id$ -** Lua Stream - Generic stream support for the Lua language -** Renato Maia, Tecgraf/PUC-Rio (http://www.tecgraf.puc-rio.br/~maia) -** See Copyright Notice at the end of this file -*/ - - -#ifndef lstraux_h -#define lstraux_h - - -#include -#include - - - -#ifndef LUABUFLIB_API -#define LUABUFLIB_API LUALIB_API -#endif - -#ifndef LUABUFMOD_API -#define LUABUFMOD_API LUAMOD_API -#endif - - - -/* -** A buffer is a userdata with metatable 'LUABUF_BUFFER'. -*/ - -#define LUABUF_BUFFER "char*" - - -#define luabuf_isbuffer(L,I) (luaL_testudata (L,I,LUABUF_BUFFER) != NULL) - - -LUABUFLIB_API char * (luabuf_newbuffer) (lua_State *L, size_t len); -LUABUFLIB_API int (luabuf_isstream) (lua_State *L, int idx); -LUABUFLIB_API char * (luabuf_tobuffer) (lua_State *L, int idx, size_t *len); -LUABUFLIB_API const char *(luabuf_tostream) (lua_State *L, int idx, size_t *len); -LUABUFLIB_API char * (luabuf_checkbuffer) (lua_State *L, int arg, size_t *len); -LUABUFLIB_API const char *(luabuf_checkstream) (lua_State *L, int arg, size_t *len); - -/* -** {====================================================== -** Lua stack's buffer support -** ======================================================= -*/ - -#define luabuf_Buffer luaL_Buffer -#define luabuf_addchar luaL_addchar -#define luabuf_addsize luaL_addsize -#define luabuf_prepbuffsize luaL_prepbuffsize -#define luabuf_prepbuffer luaL_prepbuffer -#define luabuf_addstream luaL_addlstring -#define luabuf_addlstring luaL_addlstring -#define luabuf_addstring luaL_addstring -#define luabuf_pushresult luaL_pushresult -#define luabuf_pushresultsize luaL_pushresultsize -#define luabuf_buffinit luaL_buffinit -#define luabuf_buffinitsize luaL_buffinitsize - - -LUABUFLIB_API void (luabuf_addvalue) (luabuf_Buffer *B); -LUABUFLIB_API void (luabuf_pushresbuf) (luabuf_Buffer *B); -LUABUFLIB_API void (luabuf_pushresbufsize) (luabuf_Buffer *B, size_t sz); - -/* }====================================================== */ - - -/****************************************************************************** -* Copyright (C) 1994-2015 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. -******************************************************************************/ - - -#endif diff --git a/src/lstrops.c b/src/lstrops.c deleted file mode 100644 index b4cf53d..0000000 --- a/src/lstrops.c +++ /dev/null @@ -1,50 +0,0 @@ -/* -** $Id$ -** NOTE: most of the code in here is copied from the source of Lua 5.3.1 by -** R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. -** -** Stream support for the Lua language -** See Copyright Notice in lstraux.h -*/ - -#define lstrops_c - -#include "lstraux.h" -#include "lstrops.h" - - -/* macro to 'unsign' a character */ -#define uchar(c) ((unsigned char)(c)) - - -/* translate a relative string position: negative means back from end */ -LUABUF_FUNC lua_Integer luastreamI_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; -} - -LUABUF_FUNC int luastreamI_str2byte (lua_State *L, const char *s, size_t l) { - lua_Integer posi = luastreamI_posrelat(luaL_optinteger(L, 2, 1), l); - lua_Integer pose = luastreamI_posrelat(luaL_optinteger(L, 3, posi), l); - int n, i; - if (posi < 1) posi = 1; - if (pose > (lua_Integer)l) pose = l; - if (posi > pose) return 0; /* empty interval; return no values */ - n = (int)(pose - posi + 1); - if (posi + n <= pose) /* arithmetic overflow? */ - return luaL_error(L, "string slice too long"); - luaL_checkstack(L, n, "string slice too long"); - for (i=0; i - - - -#ifndef LUABUF_FUNC -#define LUABUF_FUNC LUAI_FUNC -#endif - - - -/* -** 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 LUABUF_MAXSIZE \ - (sizeof(size_t) < sizeof(int) ? (~(size_t)0) : (size_t)(INT_MAX)) - - - - -LUABUF_FUNC lua_Integer luastreamI_posrelat (lua_Integer pos, size_t len); -LUABUF_FUNC int luastreamI_str2byte (lua_State *L, const char *s, size_t l); -LUABUF_FUNC void luastreamI_code2char (lua_State *L, int idx, char *p, int n); - - -#endif From 2f3376f17d3ec99d2beae3779554585fe50740e8 Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Wed, 24 Jan 2018 00:46:00 -0200 Subject: [PATCH 02/24] Functional version of Lua Memory library. Module with string-like operations was droped. --- README.md | 79 +-- demo/buffer.lua | 24 - demo/fill.lua | 24 + doc/manual.md | 150 ++--- src/Makefile | 11 +- src/lbytmod.c | 1525 ---------------------------------------------- src/lmemlib.c | 97 +-- src/lmemlib.h | 52 +- src/lmemmod.c | 177 ++++-- test/testall.lua | 505 +++------------ test/tpack.lua | 121 ++-- 11 files changed, 448 insertions(+), 2317 deletions(-) delete mode 100644 demo/buffer.lua create mode 100644 demo/fill.lua delete mode 100644 src/lbytmod.c diff --git a/README.md b/README.md index 75f4de0..374fcc5 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,20 @@ Lua Memory ========== -The purpose of this project is to support manipulation of fixed-size memory areas in Lua. +The purpose of this project is to support manipulation of memory areas in Lua. These memory areas are much like Lua strings, but their contents can be modified in place and have an identity (selfness) independent from their contents. The library provides the following functionalities: - Support for writable memory areas. - C API to manipulate strings or memory areas in a unified way. -- Alternative implementations of standard Lua libraries that manipulate memory areas just like strings. TODO ---- -- Make every memory provide `memory.*` functions as methods. -- Assure every memory always have a `\0` after its last byte. -- Write the manual -- Add support to create a memory using a pointer (potentially to a memory area external to Lua) using the C API. - -```lua -int freeluamem (lua_State *L) -{ - void *p = luamem_toexternal(L, 1, NULL); - if (p) free(p); - return 0; -} - -size_t sz = 16; -void *p = malloc(sz); -luamem_setexternal(L, p, sz, NULL); -- is persistent, so 'p' is accessible from Lua even if it leaves the stack. -luamem_setexternal(L, p, sz, freeluamem); -- is collectable, but 'p' is accessible from Lua. -luamem_setexternal(L, p, 0, NULL); -- is collectable also - -typedef struct luamem_External { - lua_CFunction callback; - size_t size; - /* private part */ - void *memory. -} luamem_External; - -luamem_External *luamem_newexternal (lua_State *L, void *mem) { - lua_ - luamem_External *lm = - (luamem_External *)lua_newuserdata(L, sizeof(luamem_External)); - lm->callback = NULL; - lm->size = 0; - lm->memory = mem; - return lm; -} - if sz == 0 then mem = "\0" end - #ifndef LUAMEMORY_NOCHECKNULLTERM - assert(mem[sz] == '\0') - #endif - local refs = debug.getregistry().LuaMemoryExternalWeakRefs - --assert(getmetatable(refs).__mode == "v") - local key = lightuserdata(mem) - local memory = refs[key] - if memory == nil then - memory = external{ - memory = mem, - size = sz, - gccallback = gc - } - refs[key] = memory - else - memory.size = sz - memory.gccallback = gc - end - lua_pushvalue(memory) -- only value left pushed to the stack -} -int luamem_getexternal(lua_State *L, void *mem) { - local reg = debug.getregistry().LuaMemoryPointerWeakRegistry - assert(getmetatable(reg).__mode == "v") - local found = reg[lightuserdata(mem)] - if getmetatable(found) == LuaMemoryExternalMetatable then - lua_pushvalue(found) -- only value left pushed to the stack - return true - end - return false -} -void *luamem_toexternal(lua_State *L, int idx, size_t *sz); -int luamem_isexternal(lua_State *L, int idx); - -#define ldata_newref(L,p,s) (luamem_setexternal(L, p, s, NULL), p) -#define ldata_unref(L,p) luamem_setexternal(L, p, 0, NULL) -``` +- Add the C API to the manual. +- Finish adaptation of `string.pack` tests to test `memory.pack`. +- Add the tests for resizable memory. +- Add support for bitwise operations on the memory contents. Documentation ------------- diff --git a/demo/buffer.lua b/demo/buffer.lua deleted file mode 100644 index 3e77931..0000000 --- a/demo/buffer.lua +++ /dev/null @@ -1,24 +0,0 @@ --- create a buffer -local b = buffer.create(10) - --- fill buffer with zeros -buffer.fill(b, 0) - --- iterate over buffer -for i = 1, buffer.size(b) do - print(i, buffer.get(b, i)) -end - --- iterate to fill the buffer -for i = 1, buffer.size(b) do - print(i, buffer.set(b, i, 2*i)) -end - --- sets 4th, 5th and 6th bytes in the buffer -buffer.set(b, 4, 0xff, 0xff, 0xff) - --- copy 3 bytes from position 4 to position 1 -buffer.fill(b, b, 1, 3, 4) - --- clear the positions after the 3 first bytes -buffer.fill(b, 0, 4) diff --git a/demo/fill.lua b/demo/fill.lua new file mode 100644 index 0000000..7c5d7bf --- /dev/null +++ b/demo/fill.lua @@ -0,0 +1,24 @@ +-- create a memory +local b = memory.create(10) + +-- fill memory with zeros +memory.fill(b, 0) + +-- iterate over memory +for i = 1, memory.len(b) do + print(i, memory.get(b, i)) +end + +-- iterate to fill the memory +for i = 1, memory.len(b) do + memory.set(b, i, 2*i) +end + +-- sets 4th, 5th and 6th bytes in the memory +memory.set(b, 4, 0xff, 0xff, 0xff) + +-- copy 3 bytes from position 4 to position 1 +memory.fill(b, b, 1, 3, 4) + +-- clear the positions after the 3 first bytes +memory.fill(b, 0, 4) diff --git a/doc/manual.md b/doc/manual.md index 971f9f0..44e4ce5 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -1,88 +1,72 @@ -Module `buffer` -=============== - --- buffer support -[ok] b = buffer.create (b|s|sz [, i [, j]]) -[ok] b:set (pos, ...) -[ok] b:fill (b|s [, i [, j [, o]]]) -[ok] sz = #b -- ~ stream.len -[ok] str = tostring (b) -- ~ stream.tostring -[ok] ... = b:get ([i [, j]]) -- ~ stream.byte -[??] fmt_i, arg_i = b:pack (i, fmt, ...) -- padding shall not change buffer -[??] ... = buffer:unpack (i, fmt [, pos]) - -Module `buffer.stream` -====================== - --- inspect streams -[ ] "string"|"buffer" = stream.type (b|s) -[ok] index, lesser = stream.diff (b|s, b|s) -[ok] sz = stream.len (b|s) -[ok] str = stream.tostring (b|s [, i [, j]]) -- ~ string.sub -[ok] ... = stream.byte (b|s [, i [, j]]) --- pattern matching -[??] i, j = stream.find (b|s, pattern [, init [, plain]]) -[??] for ... in stream.gmatch (b|s, pattern) do -[??] ... = stream.match (b|s, pattern [, init]) --- structure packing -[??] ... = stream.unpack (fmt, b|s [, pos]) -[??] size = stream.packsize (fmt, ...) --- stream factories (out="string"|"buffer") -[ok] b|s = stream.char (out, ...) -[??] b|s = stream.dump (out, f [, strip]) -[??] b|s = stream.format (out, fmt, ...) -[??] b|s = stream.gsub (out, b|s, pattern, repl [, n]) -[??] b|s = stream.pack (out, fmt, ...) -[??] b|s = stream.rep (out, b|s, n [, sep]) -[??] b|s = stream.lower (out, b|s) -- out="string"|"buffer"|"inplace" -[??] b|s = stream.upper (out, b|s) -- out="string"|"buffer"|"inplace" -[??] b|s = stream.reverse (out, b|s) -- out="string"|"buffer"|"inplace" -[ ] b|s = stream.concat (out, list [, sep [, i [, j]]]) - - -Legend ------- -[ok] - implementation and tests -[??] - implementation only -[ ] - no implementation yet - +Index +===== + +- [`memory.create`](#memorycreate-s-i-j-) +- [`memory.resize`](#memoryresize-m-l) +- [`memory.type`](#memorytype-m) +- [`memory.len`](#memorylen-m) +- [`memory.diff`](#memorydiff-m1-m2) +- [`memory.get`](#memoryget-m-i-j-) +- [`memory.set`](#memoryset-m-i) +- [`memory.fill`](#memoryfill-m-s-i-j-o-) +- [`memory.pack`](#memorypack-m-i-fmt-v-) +- [`memory.unpack`](#memoryunpack-m-i-fmt) +- [`tostring`](#tostring-m) Contents ======== -Writable Fixed-Size Buffers ---------------------------- +Writable Byte Sequences +----------------------- -This library provides generic functions for manipulation of fixed-size memory buffers. -When indexing a buffer, the first byte is at position 1 (not at 0, as in C). -Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the buffer. +This library provides generic functions for manipulation of writable memory areas. +A memory can have a fixed size or be resizable. +When indexing a memory, the first byte is at position 1 (not at 0, as in C). +Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the memory. Thus, the last byte is at position -1, and so on. -In this manual we will refer to byte in position `i` as `b[i]`. +In this manual we will refer to byte in position `i` as `m[i]`. -This library provides all its functions inside the table `buffer`. -It also sets a metatable for the buffers where the `__index` field points to the `buffer` table. +This library provides all its functions inside the table `memory`. +It also sets a metatable for the memory where the `__index` field points to the `memory` table. Therefore, you can use the library functions in object-oriented style. -For instance, `buffer.get(b,i)` can be written as `b:get(i)`, where `b` is a buffer. +For instance, `memory.get(m,i)` can be written as `m:get(i)`, where `m` is a memory. -### `buffer.create (s [, i [, j]])` +### `memory.create ([s [, i [, j]]])` -Creates a new buffer of `s` bytes when `s` is a number. +Creates a new fixed-size memory of `s` bytes with value zero when `s` is a number. -If `s` is a string or a buffer, then the new buffer will have the same size and contents of `s` from position `i` until position `j`; +If `s` is a string or a memory, then the new memory will have the same size and contents of `s` from position `i` until position `j`; `i` and `j` can be negative. The default value for `i` is 1; the default value for `j` is -1 (which is the same as the size of `s`). -These indices are corrected following the same rules of function [`buffer.get`](#bufferget-b-i-j). +These indices are corrected following the same rules of function [`memory.get`](#memoryget-m-i-j). + +If `s` is not provided, a resizable memory of zero bytes is created. + +Returns the new memory. + +### `memory.resize (m, l)` + +Changes resizable memory `m` to contain `l` bytes. +All the initial bytes that fit in the new size are preserved. +Any extra bytes have value zero. + +### `memory.type (m)` + +Returns `"fixed"` if `m` is a fixed-size memory, or `"resizable"` if it is a resizable memory, or `nil` otherwise. + +### `memory.len (m)` -Returns the new buffer. +Returns the size of memory `m`. -### `buffer.len (b)` +### `memory.diff (m1, m2)` -Returns the size of buffer `b`. +Returns the index of the first byte which values differ in `m1` and `m2`, or `nil` if both contain the same bytes. +It also returns the result of a `m1 < m2` as if they were strings. -### `buffer.get (b [, i [, j]])` +### `memory.get (m [, i [, j]])` -Returns the values of bytes in buffer `b` from `i` until `j`; +Returns the values of bytes in memory `m` from `i` until `j`; `i` and `j` can be negative. The default value for `i` is 1; the default value for `j` is `i`. @@ -91,36 +75,36 @@ If, after the translation of negative indices, `i` is less than 1, it is correct If `j` is greater than the size of `s`, it is corrected to that size. If, after these corrections, `i` is greater than `j`, the range is empty and no values are returned. -### `buffer.set (b, i, ...)` +### `memory.set (m, i, ...)` -Sets the values of bytes in buffer `b` from position `i` with values indicated by numbers received as arguments `...`; +Sets the values of bytes in memory `m` from position `i` with values indicated by numbers received as arguments `...`; `i` can be negative. -If there are more arguments than bytes in the range from `i` to the end of buffer `b`, the extra arguments are ignored. +If there are more arguments than bytes in the range from `i` to the end of memory `m`, the extra arguments are ignored. -### `buffer.fill (b, s [, i [, j [, o]]])` +### `memory.fill (m, s [, i [, j [, o]]])` -Sets the values of all bytes in buffer `b` in the range from position `i` until `j` with the contents from position `o` of the string or buffer `s`; +Sets the values of all bytes in memory `m` in the range from position `i` until `j` with the contents from position `o` of the string or memory `s`; `i`, `j` and `o` can be negative. If, after the translation of negative indices, `o` is less than 1, it is corrected to 1. -After the translation of negative indices, `i` and `j` must refer to valid positions of `b`. +After the translation of negative indices, `i` and `j` must refer to valid positions of `m`. -If `i` is greater and `j` (empty range), or `o` refers to a position beyond the size of `b` (no contents) this function has no effect. -Otherwise, the specified contents from `s` (from `o`) are copied repeatedly until they fill all bytes in the specified range of `b` (from `i` to `j`). +If `i` is greater and `j` (empty range), or `o` refers to a position beyond the size of `m` (no contents) this function has no effect. +Otherwise, the specified contents from `s` (from `o`) are copied repeatedly until they fill all bytes in the specified range of `m` (from `i` to `j`). -If `s` is a number then all bytes in the specified range of `b` are set with the value of `s`. +If `s` is a number then all bytes in the specified range of `m` are set with the value of `s`. The value of `o` is ignored in this case. -### `buffer.pack (b, i, fmt, v...)` +### `memory.pack (m, i, fmt, v...)` -Serializes in buffer `b`, from position `i`, the values `v...` in binary form according to the format `fmt` (see the [Lua manual](http://www.lua.org/manual/5.3/manual.html#6.4.2)). -Returns the index of the first unwritten byte in `b`. +Serializes in memory `m`, from position `i`, the values `v...` in binary form according to the format `fmt` (see the [Lua manual](http://www.lua.org/manual/5.3/manual.html#6.4.2)). +Returns a boolean indicating whether all values were packed in memory `m`, followed by the index of the first unwritten byte in `m` and all the values `v...` that were not packed. -### `buffer.unpack (b, i, fmt)` +### `memory.unpack (m, i, fmt)` -Returns the values encoded in position `i` of buffer or string `b`, according to the format `fmt`, as in function [buffer.pack](#bufferpack-b-i-fmt-v-). -After the read values, this function also returns the index of the first unread byte in `b`. +Returns the values encoded in position `i` of memory or string `m`, according to the format `fmt`, as in function [memory.pack](#memorypack-m-i-fmt-v-). +After the read values, this function also returns the index of the first unread byte in `m`. -### `tostring (b)` +### `tostring (m)` -Returns a string with the contents of buffer `b`. +Returns a string with the contents of memory `m`. diff --git a/src/Makefile b/src/Makefile index 4b75e56..bc26785 100644 --- a/src/Makefile +++ b/src/Makefile @@ -30,20 +30,18 @@ MYLIBS= PLATS= linux macosx MEM_O= lmemmod.o -BYT_O= lbytmod.o API_O= lmemlib.o MEM_M= memory.so -BYT_M= bytes.so API_S= libluamemlib.so LIB_S= libluamemory.so LIB_A= libluamemory.a -ALL_O= $(MEM_O) $(BYT_O) $(API_O) +ALL_O= $(MEM_O) $(API_O) ALL_A= $(LIB_A) ALL_S= $(LIB_S) $(API_S) -ALL_M= $(MEM_M) $(BYT_M) -ALL_T= $(ALL_A) $(ALL_S) +ALL_M= $(MEM_M) +ALL_T= $(ALL_A) $(ALL_S) $(ALL_M) default: $(PLAT) @@ -63,9 +61,6 @@ $(API_S): $(API_O) $(MEM_M): $(MEM_O) $(API_S) $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) -$(BYT_M): $(BYT_O) $(API_S) - $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) - $(LIB_S): $(LIB_O) $(API_S) $(ALL_M) $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) diff --git a/src/lbytmod.c b/src/lbytmod.c deleted file mode 100644 index 9bdd662..0000000 --- a/src/lbytmod.c +++ /dev/null @@ -1,1525 +0,0 @@ -/* -* NOTE: most of the code below is copied from the source of Lua 5.3.1 by -* R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. -* -* Copyright (C) 1994-2015 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. -*/ - -#define lbytmod_c - -#include "lmemlib.h" - -#include -#include - - - -#if !defined(lua_assert) -#define lua_assert(x) ((void)0) -#endif - - - -/* -** maximum number of captures that a pattern can do during -** pattern-matching. This limit is arbitrary. -*/ -#if !defined(LUA_MAXCAPTURES) -#define LUA_MAXCAPTURES 32 -#endif - - -/* macro to 'unsign' a character */ -#define uchar(c) ((unsigned char)(c)) - - -static int str_len (lua_State *L) { - size_t l; - luamem_checkstring(L, 1, &l); - lua_pushinteger(L, (lua_Integer)l); - return 1; -} - - - -static int str_isbuffer (lua_State *L) { - lua_pushboolean(L, luamem_isalloc(L, 1)); - return 1; -} - - -static int str_diff (lua_State *L) { - size_t l1, l2; - const char *s1 = luamem_checkstring(L, 1, &l1); - const char *s2 = luamem_checkstring(L, 2, &l2); - size_t i, n=(l1 (lua_Integer)l) end = l; - if (start == 1 && end == l && lua_type(L, 1) == LUA_TSTRING) - lua_settop(L, 1); - else if (start <= end) - lua_pushlstring(L, s + start - 1, (size_t)(end - start + 1)); - else lua_pushliteral(L, ""); - return 1; -} - - -static const char *const outops[] = {"string", "buffer", NULL}; -static const char *const inoutops[] = {"string", "buffer", "inplace", NULL}; - - -static void copyreverse (char *p, const char *s, size_t l) { - size_t i; - for (i=0; i 1) { /* first n-1 copies (followed by separator) */ - memcpy(p, s, l * sizeof(char)); p += l; - if (lsep > 0) { /* empty 'memcpy' is not that cheap */ - memcpy(p, sep, lsep * sizeof(char)); - p += lsep; - } - } - memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ -} - - -static int str_rep (lua_State *L) { - size_t l, lsep; - int op = luaL_checkoption(L, 1, NULL, outops); - const char *s = luamem_checkstring(L, 2, &l); - lua_Integer n = luaL_checkinteger(L, 3); - const char *sep = luaL_optlstring(L, 4, "", &lsep); - if (n <= 0) { - if (op == 0) lua_pushliteral(L, ""); - else luamem_newalloc(L, 0); - } else if (l + lsep < l || l + lsep > LUABUF_MAXSIZE / n) /* may overflow? */ - return luaL_error(L, "resulting string too large"); - else { - size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; - if (op == 0) { /* string */ - luamem_Buffer b; - char *p = luamem_buffinitsize(L, &b, totallen); - copyrepeat(p, s, l, sep, lsep, n); - luamem_pushresultsize(&b, totallen); - } else { - char *p = luamem_newalloc(L, totallen); - copyrepeat(p, s, l, sep, lsep, n); - } - } - return 1; -} - - -static int str_byte (lua_State *L) { - size_t l; - const char *s = luamem_checkstring(L, 1, &l); - return luamem_str2byte(L, s, l); -} - - -static int str_char (lua_State *L) { - int n = lua_gettop(L)-1; /* number of bytes */ - int op = luaL_checkoption(L, 1, NULL, outops); - if (op == 0) { - luamem_Buffer b; - char *p = luamem_buffinitsize(L, &b, n); - luamem_code2char(L, 2, p, n); - luamem_pushresultsize(&b, n); - } else { - char *p = luamem_newalloc(L, n); - luamem_code2char(L, 2, p, n); - } - return 1; -} - - -static int writer (lua_State *L, const void *b, size_t size, void *B) { - (void)L; - luamem_addlstring((luamem_Buffer *) B, (const char *)b, size); - return 0; -} - - -static int str_dump (lua_State *L) { - luamem_Buffer b; - int strip = lua_toboolean(L, 3); - int op = luaL_checkoption(L, 1, NULL, outops); - luaL_checktype(L, 2, LUA_TFUNCTION); - lua_settop(L, 1); - luamem_buffinit(L,&b); - if (lua_dump(L, writer, &b, strip) != 0) - return luaL_error(L, "unable to dump given function"); - if (op == 0) luamem_pushresult(&b); - else luamem_pushresbuf(&b); - return 1; -} - - - -/* -** {====================================================== -** PATTERN MATCHING -** ======================================================= -*/ - - -#define CAP_UNFINISHED (-1) -#define CAP_POSITION (-2) - - -typedef struct MatchState { - int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ - const char *src_init; /* init of source string */ - const char *src_end; /* end ('\0') of source string */ - const char *p_end; /* end ('\0') of pattern */ - lua_State *L; - int level; /* total number of captures (finished or unfinished) */ - struct { - const char *init; - ptrdiff_t len; - } capture[LUA_MAXCAPTURES]; -} MatchState; - - -/* recursive function */ -static const char *match (MatchState *ms, const char *s, const char *p); - - -/* maximum recursion depth for 'match' */ -#if !defined(MAXCCALLS) -#define MAXCCALLS 200 -#endif - - -#define L_ESC '%' -#define SPECIALS "^$*+?.([%-" - - -static int check_capture (MatchState *ms, int l) { - l -= '1'; - if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) - return luaL_error(ms->L, "invalid capture index %%%d", l + 1); - return l; -} - - -static int capture_to_close (MatchState *ms) { - int level = ms->level; - for (level--; level>=0; level--) - if (ms->capture[level].len == CAP_UNFINISHED) return level; - return luaL_error(ms->L, "invalid pattern capture"); -} - - -static const char *classend (MatchState *ms, const char *p) { - switch (*p++) { - case L_ESC: { - if (p == ms->p_end) - luaL_error(ms->L, "malformed pattern (ends with '%%')"); - return p+1; - } - case '[': { - if (*p == '^') p++; - do { /* look for a ']' */ - if (p == ms->p_end) - luaL_error(ms->L, "malformed pattern (missing ']')"); - if (*(p++) == L_ESC && p < ms->p_end) - p++; /* skip escapes (e.g. '%]') */ - } while (*p != ']'); - return p+1; - } - default: { - return p; - } - } -} - - -static int match_class (int c, int cl) { - int res; - switch (tolower(cl)) { - case 'a' : res = isalpha(c); break; - case 'c' : res = iscntrl(c); break; - case 'd' : res = isdigit(c); break; - case 'g' : res = isgraph(c); break; - case 'l' : res = islower(c); break; - case 'p' : res = ispunct(c); break; - case 's' : res = isspace(c); break; - case 'u' : res = isupper(c); break; - case 'w' : res = isalnum(c); break; - case 'x' : res = isxdigit(c); break; - case 'z' : res = (c == 0); break; /* deprecated option */ - default: return (cl == c); - } - return (islower(cl) ? res : !res); -} - - -static int matchbracketclass (int c, const char *p, const char *ec) { - int sig = 1; - if (*(p+1) == '^') { - sig = 0; - p++; /* skip the '^' */ - } - while (++p < ec) { - if (*p == L_ESC) { - p++; - if (match_class(c, uchar(*p))) - return sig; - } - else if ((*(p+1) == '-') && (p+2 < ec)) { - p+=2; - if (uchar(*(p-2)) <= c && c <= uchar(*p)) - return sig; - } - else if (uchar(*p) == c) return sig; - } - return !sig; -} - - -static int singlematch (MatchState *ms, const char *s, const char *p, - const char *ep) { - if (s >= ms->src_end) - return 0; - else { - int c = uchar(*s); - switch (*p) { - case '.': return 1; /* matches any char */ - case L_ESC: return match_class(c, uchar(*(p+1))); - case '[': return matchbracketclass(c, p, ep-1); - default: return (uchar(*p) == c); - } - } -} - - -static const char *matchbalance (MatchState *ms, const char *s, - const char *p) { - if (p >= ms->p_end - 1) - luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); - if (*s != *p) return NULL; - else { - int b = *p; - int e = *(p+1); - int cont = 1; - while (++s < ms->src_end) { - if (*s == e) { - if (--cont == 0) return s+1; - } - else if (*s == b) cont++; - } - } - return NULL; /* string ends out of balance */ -} - - -static const char *max_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - ptrdiff_t i = 0; /* counts maximum expand for item */ - while (singlematch(ms, s + i, p, ep)) - i++; - /* keeps trying to match with the maximum repetitions */ - while (i>=0) { - const char *res = match(ms, (s+i), ep+1); - if (res) return res; - i--; /* else didn't match; reduce 1 repetition to try again */ - } - return NULL; -} - - -static const char *min_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - for (;;) { - const char *res = match(ms, s, ep+1); - if (res != NULL) - return res; - else if (singlematch(ms, s, p, ep)) - s++; /* try with one more repetition */ - else return NULL; - } -} - - -static const char *start_capture (MatchState *ms, const char *s, - const char *p, int what) { - const char *res; - int level = ms->level; - if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); - ms->capture[level].init = s; - ms->capture[level].len = what; - ms->level = level+1; - if ((res=match(ms, s, p)) == NULL) /* match failed? */ - ms->level--; /* undo capture */ - return res; -} - - -static const char *end_capture (MatchState *ms, const char *s, - const char *p) { - int l = capture_to_close(ms); - const char *res; - ms->capture[l].len = s - ms->capture[l].init; /* close capture */ - if ((res = match(ms, s, p)) == NULL) /* match failed? */ - ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ - return res; -} - - -static const char *match_capture (MatchState *ms, const char *s, int l) { - size_t len; - l = check_capture(ms, l); - len = ms->capture[l].len; - if ((size_t)(ms->src_end-s) >= len && - memcmp(ms->capture[l].init, s, len) == 0) - return s+len; - else return NULL; -} - - -static const char *match (MatchState *ms, const char *s, const char *p) { - if (ms->matchdepth-- == 0) - luaL_error(ms->L, "pattern too complex"); - init: /* using goto's to optimize tail recursion */ - if (p != ms->p_end) { /* end of pattern? */ - switch (*p) { - case '(': { /* start capture */ - if (*(p + 1) == ')') /* position capture? */ - s = start_capture(ms, s, p + 2, CAP_POSITION); - else - s = start_capture(ms, s, p + 1, CAP_UNFINISHED); - break; - } - case ')': { /* end capture */ - s = end_capture(ms, s, p + 1); - break; - } - case '$': { - if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */ - goto dflt; /* no; go to default */ - s = (s == ms->src_end) ? s : NULL; /* check end of string */ - break; - } - case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ - switch (*(p + 1)) { - case 'b': { /* balanced string? */ - s = matchbalance(ms, s, p + 2); - if (s != NULL) { - p += 4; goto init; /* return match(ms, s, p + 4); */ - } /* else fail (s == NULL) */ - break; - } - case 'f': { /* frontier? */ - const char *ep; char previous; - p += 2; - if (*p != '[') - luaL_error(ms->L, "missing '[' after '%%f' in pattern"); - ep = classend(ms, p); /* points to what is next */ - previous = (s == ms->src_init) ? '\0' : *(s - 1); - if (!matchbracketclass(uchar(previous), p, ep - 1) && - matchbracketclass(uchar(*s), p, ep - 1)) { - p = ep; goto init; /* return match(ms, s, ep); */ - } - s = NULL; /* match failed */ - break; - } - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - case '8': case '9': { /* capture results (%0-%9)? */ - s = match_capture(ms, s, uchar(*(p + 1))); - if (s != NULL) { - p += 2; goto init; /* return match(ms, s, p + 2) */ - } - break; - } - default: goto dflt; - } - break; - } - default: dflt: { /* pattern class plus optional suffix */ - const char *ep = classend(ms, p); /* points to optional suffix */ - /* does not match at least once? */ - if (!singlematch(ms, s, p, ep)) { - if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ - p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ - } - else /* '+' or no suffix */ - s = NULL; /* fail */ - } - else { /* matched once */ - switch (*ep) { /* handle optional suffix */ - case '?': { /* optional */ - const char *res; - if ((res = match(ms, s + 1, ep + 1)) != NULL) - s = res; - else { - p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ - } - break; - } - case '+': /* 1 or more repetitions */ - s++; /* 1 match already done */ - /* go through */ - case '*': /* 0 or more repetitions */ - s = max_expand(ms, s, p, ep); - break; - case '-': /* 0 or more repetitions (minimum) */ - s = min_expand(ms, s, p, ep); - break; - default: /* no suffix */ - s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ - } - } - break; - } - } - } - ms->matchdepth++; - return s; -} - - - -static const char *lmemfind (const char *s1, size_t l1, - const char *s2, size_t l2) { - if (l2 == 0) return s1; /* empty strings are everywhere */ - else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ - else { - const char *init; /* to search for a '*s2' inside 's1' */ - l2--; /* 1st char will be checked by 'memchr' */ - l1 = l1-l2; /* 's2' cannot be found after that */ - while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { - init++; /* 1st char is already checked */ - if (memcmp(init, s2+1, l2) == 0) - return init-1; - else { /* correct 'l1' and 's1' to try again */ - l1 -= init-s1; - s1 = init; - } - } - return NULL; /* not found */ - } -} - - -static void push_onecapture (MatchState *ms, int i, const char *s, - const char *e) { - if (i >= ms->level) { - if (i == 0) /* ms->level == 0, too */ - lua_pushlstring(ms->L, s, e - s); /* add whole match */ - else - luaL_error(ms->L, "invalid capture index %%%d", i + 1); - } - else { - ptrdiff_t l = ms->capture[i].len; - if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); - if (l == CAP_POSITION) - lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); - else - lua_pushlstring(ms->L, ms->capture[i].init, l); - } -} - - -static int push_captures (MatchState *ms, const char *s, const char *e) { - int i; - int nlevels = (ms->level == 0 && s) ? 1 : ms->level; - luaL_checkstack(ms->L, nlevels, "too many captures"); - for (i = 0; i < nlevels; i++) - push_onecapture(ms, i, s, e); - return nlevels; /* number of strings pushed */ -} - - -/* check whether pattern has no special characters */ -static int nospecials (const char *p, size_t l) { - size_t upto = 0; - do { - if (strpbrk(p + upto, SPECIALS)) - return 0; /* pattern has a special character */ - upto += strlen(p + upto) + 1; /* may have more after \0 */ - } while (upto <= l); - return 1; /* no special chars found */ -} - - -static int str_find_aux (lua_State *L, int find) { - size_t ls, lp; - const char *s = luamem_checkstring(L, 1, &ls); - const char *p = luamem_checkstring(L, 2, &lp); - lua_Integer init = luamem_posrelat(luaL_optinteger(L, 3, 1), ls); - if (init < 1) init = 1; - else if (init > (lua_Integer)ls + 1) { /* start after string's end? */ - lua_pushnil(L); /* cannot find anything */ - return 1; - } - /* explicit request or no special characters? */ - if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { - /* do a plain search */ - const char *s2 = lmemfind(s + init - 1, ls - (size_t)init + 1, p, lp); - if (s2) { - lua_pushinteger(L, s2 - s + 1); - lua_pushinteger(L, s2 - s + lp); - return 2; - } - } - else { - MatchState ms; - const char *s1 = s + init - 1; - int anchor = (*p == '^'); - if (anchor) { - p++; lp--; /* skip anchor character */ - } - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s + ls; - ms.p_end = p + lp; - do { - const char *res; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - if ((res=match(&ms, s1, p)) != NULL) { - if (find) { - lua_pushinteger(L, s1 - s + 1); /* start */ - lua_pushinteger(L, res - s); /* end */ - return push_captures(&ms, NULL, 0) + 2; - } - else - return push_captures(&ms, s1, res); - } - } while (s1++ < ms.src_end && !anchor); - } - lua_pushnil(L); /* not found */ - return 1; -} - - -static int str_find (lua_State *L) { - return str_find_aux(L, 1); -} - - -static int str_match (lua_State *L) { - return str_find_aux(L, 0); -} - - -static int gmatch_aux (lua_State *L) { - MatchState ms; - size_t ls, lp; - const char *s = luamem_tostring(L, lua_upvalueindex(1), &ls); - const char *p = luamem_tostring(L, lua_upvalueindex(2), &lp); - const char *src; - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s+ls; - ms.p_end = p + lp; - for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); - src <= ms.src_end; - src++) { - const char *e; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - if ((e = match(&ms, src, p)) != NULL) { - lua_Integer newstart = e-s; - if (e == src) newstart++; /* empty match? go at least one position */ - lua_pushinteger(L, newstart); - lua_replace(L, lua_upvalueindex(3)); - return push_captures(&ms, src, e); - } - } - return 0; /* not found */ -} - - -static int str_gmatch (lua_State *L) { - luamem_checkstring(L, 1, NULL); - luamem_checkstring(L, 2, NULL); - lua_settop(L, 2); - lua_pushinteger(L, 0); - lua_pushcclosure(L, gmatch_aux, 3); - return 1; -} - - -static void add_s (MatchState *ms, luamem_Buffer *b, const char *s, - const char *e) { - size_t l, i; - lua_State *L = ms->L; - const char *news = luamem_tostring(L, 3, &l); - for (i = 0; i < l; i++) { - if (news[i] != L_ESC) - luamem_addchar(b, news[i]); - else { - i++; /* skip ESC */ - if (!isdigit(uchar(news[i]))) { - if (news[i] != L_ESC) - luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); - luamem_addchar(b, news[i]); - } - else if (news[i] == '0') - luamem_addlstring(b, s, e - s); - else { - push_onecapture(ms, news[i] - '1', s, e); - luaL_tolstring(L, -1, NULL); /* if number, convert it to string */ - lua_remove(L, -2); /* remove original value */ - luaL_addvalue(b); /* add capture to accumulated result */ - } - } - } -} - - -static void add_value (MatchState *ms, - luamem_Buffer *b, - const char *s, - const char *e, - int tr) { - lua_State *L = ms->L; - switch (tr) { - case LUA_TFUNCTION: { - int n; - lua_pushvalue(L, 3); - n = push_captures(ms, s, e); - lua_call(L, n, 1); - break; - } - case LUA_TTABLE: { - push_onecapture(ms, 0, s, e); - lua_gettable(L, 3); - break; - } - default: { /* LUA_TNUMBER or LUA_TSTRING */ - add_s(ms, b, s, e); - return; - } - } - if (!lua_toboolean(L, -1)) { /* nil or false? */ - lua_pop(L, 1); - lua_pushlstring(L, s, e - s); /* keep original text */ - } - else if (!lua_isstring(L, -1)) - luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); - luaL_addvalue(b); /* add result to accumulator */ -} - - -static int str_gsub (lua_State *L) { - size_t srcl, lp; - int op = luaL_checkoption(L, 1, NULL, outops); - const char *src = luamem_checkstring(L, 2, &srcl); - const char *p = luamem_checkstring(L, 3, &lp); - int tr = lua_type(L, 4); - lua_Integer max_s = luaL_optinteger(L, 5, srcl + 1); - int anchor = (*p == '^'); - lua_Integer n = 0; - MatchState ms; - luamem_Buffer b; - luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || - tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, - "string/function/table expected"); - luamem_buffinit(L, &b); - if (anchor) { - p++; lp--; /* skip anchor character */ - } - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = src; - ms.src_end = src+srcl; - ms.p_end = p + lp; - while (n < max_s) { - const char *e; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - e = match(&ms, src, p); - if (e) { - n++; - add_value(&ms, &b, src, e, tr); - } - if (e && e>src) /* non empty match? */ - src = e; /* skip it */ - else if (src < ms.src_end) - luamem_addchar(&b, *src++); - else break; - if (anchor) break; - } - luamem_addlstring(&b, src, ms.src_end-src); - if (op == 0) luamem_pushresult(&b); - else luamem_pushresbuf(&b); - lua_pushinteger(L, n); /* number of substitutions */ - return 2; -} - -/* }====================================================== */ - - - -/* -** {====================================================== -** STRING FORMAT -** ======================================================= -*/ - -/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ -#define MAX_ITEM 512 - -/* valid flags in a format specification */ -#define FLAGS "-+ #0" - -/* -** maximum size of each format specification (such as "%-099.99d") -** (+2 for length modifiers; +10 accounts for %99.99x plus margin of error) -*/ -#define MAX_FORMAT (sizeof(FLAGS) + 2 + 10) - - -static void addquoted (lua_State *L, luamem_Buffer *b, int arg) { - size_t l; - const char *s = luamem_checkstring(L, arg, &l); - luamem_addchar(b, '"'); - while (l--) { - if (*s == '"' || *s == '\\' || *s == '\n') { - luamem_addchar(b, '\\'); - luamem_addchar(b, *s); - } - else if (*s == '\0' || iscntrl(uchar(*s))) { - char buff[10]; - if (!isdigit(uchar(*(s+1)))) - sprintf(buff, "\\%d", (int)uchar(*s)); - else - sprintf(buff, "\\%03d", (int)uchar(*s)); - luamem_addstring(b, buff); - } - else - luamem_addchar(b, *s); - s++; - } - luamem_addchar(b, '"'); -} - -static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { - const char *p = strfrmt; - while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ - if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) - luaL_error(L, "invalid format (repeated flags)"); - if (isdigit(uchar(*p))) p++; /* skip width */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - if (*p == '.') { - p++; - if (isdigit(uchar(*p))) p++; /* skip precision */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - } - if (isdigit(uchar(*p))) - luaL_error(L, "invalid format (width or precision too long)"); - *(form++) = '%'; - memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char)); - form += p - strfrmt + 1; - *form = '\0'; - return p; -} - - -/* -** add length modifier into formats -*/ -static void addlenmod (char *form, const char *lenmod) { - size_t l = strlen(form); - size_t lm = strlen(lenmod); - char spec = form[l - 1]; - strcpy(form + l - 1, lenmod); - form[l + lm - 1] = spec; - form[l + lm] = '\0'; -} - - -static int str_format (lua_State *L) { - int top = lua_gettop(L); - int arg = 1; - size_t sfl; - int op = luaL_checkoption(L, arg++, NULL, outops); - const char *strfrmt = luaL_checklstring(L, arg, &sfl); - const char *strfrmt_end = strfrmt+sfl; - luamem_Buffer b; - luamem_buffinit(L, &b); - while (strfrmt < strfrmt_end) { - if (*strfrmt != L_ESC) - luamem_addchar(&b, *strfrmt++); - else if (*++strfrmt == L_ESC) - luamem_addchar(&b, *strfrmt++); /* %% */ - else { /* format item */ - char form[MAX_FORMAT]; /* to store the format ('%...') */ - char *buff = luamem_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ - int nb = 0; /* number of bytes in added item */ - if (++arg > top) - luaL_argerror(L, arg, "no value"); - strfrmt = scanformat(L, strfrmt, form); - switch (*strfrmt++) { - case 'c': { - nb = sprintf(buff, form, (int)luaL_checkinteger(L, arg)); - break; - } - case 'd': case 'i': - case 'o': case 'u': case 'x': case 'X': { - lua_Integer n = luaL_checkinteger(L, arg); - addlenmod(form, LUA_INTEGER_FRMLEN); - nb = sprintf(buff, form, n); - break; - } -#if defined(LUA_USE_AFORMAT) - case 'a': case 'A': -#endif - case 'e': case 'E': case 'f': - case 'g': case 'G': { - addlenmod(form, LUA_NUMBER_FRMLEN); - nb = sprintf(buff, form, luaL_checknumber(L, arg)); - break; - } - case 'q': { - addquoted(L, &b, arg); - break; - } - case 's': { - size_t l; - const char *s = luaL_tolstring(L, arg, &l); - if (!strchr(form, '.') && l >= 100) { - /* no precision and string is too long to be formatted; - keep original string */ - luaL_addvalue(&b); - break; - } - else { - nb = sprintf(buff, form, s); - lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ - break; - } - } - default: { /* also treat cases 'pnLlh' */ - return luaL_error(L, "invalid option '%%%c' to 'format'", - *(strfrmt - 1)); - } - } - luaL_addsize(&b, nb); - } - } - if (op == 0) luamem_pushresult(&b); - else luamem_pushresbuf(&b); - return 1; -} - -/* }====================================================== */ - - -/* -** {====================================================== -** PACK/UNPACK -** ======================================================= -*/ - - -/* value used for padding */ -#if !defined(LUA_PACKPADBYTE) -#define LUA_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)LUABUF_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 (luamem_Buffer *b, lua_Unsigned n, - int islittle, int size, int neg) { - char *buff = luamem_prepbuffsize(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(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++); - } -} - - -static int str_pack (lua_State *L) { - luamem_Buffer b; - Header h; - int arg = 1; /* current argument to pack */ - int op = luaL_checkoption(L, arg++, NULL, outops); - const char *fmt = luaL_checkstring(L, arg); /* format string */ - size_t totalsize = 0; /* accumulate total size of result */ - initheader(L, &h); - lua_pushnil(L); /* mark to separate arguments from string buffer */ - luamem_buffinit(L, &b); - while (*fmt != '\0') { - int size, ntoalign; - KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); - totalsize += ntoalign + size; - while (ntoalign-- > 0) - luamem_addchar(&b, LUA_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 = luamem_prepbuffsize(&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(&b, size); - break; - } - case Kchar: { /* fixed-size string */ - size_t len; - const char *s = luamem_checkstring(L, arg, &len); - luaL_argcheck(L, len == (size_t)size, arg, "wrong length"); - luamem_addlstring(&b, s, size); - break; - } - case Kstring: { /* strings with length count */ - size_t len; - const char *s = luamem_checkstring(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 */ - luamem_addlstring(&b, s, len); - totalsize += len; - break; - } - case Kzstr: { /* zero-terminated string */ - size_t len; - const char *s = luamem_checkstring(L, arg, &len); - luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); - luamem_addlstring(&b, s, len); - luamem_addchar(&b, '\0'); /* add zero at the end */ - totalsize += len + 1; - break; - } - case Kpadding: luamem_addchar(&b, LUA_PACKPADBYTE); /* go through */ - case Kpaddalign: case Knop: - arg--; /* undo increment */ - break; - } - } - if (op == 0) luamem_pushresult(&b); - else luamem_pushresbuf(&b); - return 1; -} - - -static int str_packsize (lua_State *L) { - Header h; - int arg = 1; /* current argument to pack */ - const char *fmt = luaL_checkstring(L, arg); /* 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 <= LUABUF_MAXSIZE - size, 1, - "format result too large"); - totalsize += size; - arg++; - switch (opt) { - case Kstring: { /* strings with length count */ - size_t len; - luamem_checkstring(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"); - totalsize += len; - break; - } - case Kzstr: { /* zero-terminated string */ - size_t len; - const char *s = luamem_checkstring(L, arg, &len); - luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); - totalsize += len + 1; - break; - } - 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; -} - - -static int str_unpack (lua_State *L) { - Header h; - const char *fmt = luaL_checkstring(L, 1); - size_t ld; - const char *data = luamem_checkstring(L, 2, &ld); - size_t pos = (size_t)luamem_posrelat(luaL_optinteger(L, 3, 1), ld) - 1; - int n = 0; /* number of results */ - luaL_argcheck(L, pos <= ld, 3, "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, 2, "data string too short"); - pos += ntoalign; /* skip alignment */ - /* stack space for item + next position */ - luaL_checkstack(L, 2, "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, 2, "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; -} - -/* }====================================================== */ - - -static const luaL_Reg strlib[] = { - {"byte", str_byte}, - {"char", str_char}, - {"diff", str_diff}, - {"dump", str_dump}, - {"find", str_find}, - {"format", str_format}, - {"gmatch", str_gmatch}, - {"gsub", str_gsub}, - {"isbuffer", str_isbuffer}, - {"len", str_len}, - {"lower", str_lower}, - {"match", str_match}, - {"pack", str_pack}, - {"packsize", str_packsize}, - {"rep", str_rep}, - {"reverse", str_reverse}, - {"tostring", str_tostring}, - {"upper", str_upper}, - {"unpack", str_unpack}, - {NULL, NULL} -}; - - -LUAMEMMOD_API int luaopen_bytes (lua_State *L) { - luaL_newlib(L, strlib); - return 1; -} diff --git a/src/lmemlib.c b/src/lmemlib.c index 878d9c7..35ba16f 100644 --- a/src/lmemlib.c +++ b/src/lmemlib.c @@ -1,9 +1,3 @@ -/* -** $Id$ -** Auxiliary functions for handling generic streams in Lua. -** See Copyright Notice in lstraux.h -*/ - #define lmemlib_c #define LUAMEMLIB_API @@ -24,61 +18,48 @@ LUAMEMLIB_API char *luamem_newalloc (lua_State *L, size_t l) { #define LUAMEM_REFREGISTRY "luamem_ReferenceRegistry" -static int pushref (lua_State *L, char *mem) { - if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, LUAMEM_REFREGISTRY)) { - lua_pushliteral(L, "v"); - lua_setfield(L, -2, "__mode"); - lua_pushvalue(L, -1); - lua_setmetatable(L, -2); - } - lua_pushlightuserdata(L, mem); - return lua_gettable(L, -2) == LUA_TUSERDATA; -} +typedef struct luamem_Ref { + char *mem; + size_t len; + luamem_Unref unref; +} luamem_Ref; + +#define unref(L,r) if (r->unref) ref->unref(L, r->mem, r->len) static int luaunref (lua_State *L) { - luamem_Ref *ref = luamem_toref(L, 1); - if (ref && ref->unref) ref->unref(ref->memory, ref->len); + luamem_Ref *ref = luaL_testudata(L, 1, LUAMEM_REF); + if (ref) unref(L, ref); return 0; } -LUAMEMLIB_API void luamem_pushrefmt (lua_State *L) { +LUAMEMLIB_API void luamem_newref (lua_State *L) { + luamem_Ref *ref = (luamem_Ref *)lua_newuserdata(L, sizeof(luamem_Ref)); + ref->mem = NULL; + ref->len = 0; + ref->unref = NULL; if (luaL_newmetatable(L, LUAMEM_REF)) { lua_pushcfunction(L, luaunref); lua_setfield(L, -2, "__gc"); } + lua_setmetatable(L, -2); } -LUAMEMLIB_API luamem_Ref *luamem_pushref (lua_State *L, char *mem) { - luamem_Ref *ref; - if (pushref(L, mem)) { - ref = (luamem_Ref *)lua_touserdata(L, -1); - } else { - lua_pop(L, 1); - ref = (luamem_Ref *)lua_newuserdata(L, sizeof(luamen_Ref)); - ref->len = 0; - ref->unref = NULL; - ref->memory = mem; - luamem_pushrefmt(L); - lua_setmetatable(L, -2); - lua_pushlightuserdata(L, mem); - lua_pushvalue(L, -2); - lua_settable(L, -4) +LUAMEMLIB_API int luamem_setref (lua_State *L, int idx, char *mem, size_t len, luamem_Unref unref) { + luamem_Ref *ref = luaL_testudata(L, 1, LUAMEM_REF); + if (ref) { + if (mem != ref->mem) unref(L, ref); + ref->mem = mem; + ref->len = len; + ref->unref = unref; + return 1; } - lua_remove(L, -2); - return ref; -} - -LUAMEMLIB_API luamem_Ref *luamem_getref (lua_State *L, char *mem) { - luamem_Ref *ref; - pushref(L, mem); - ref = (luamem_Ref *)lua_touserdata(L, -1); - lua_pop(L, 2); - return ref; + return 0; } -LUAMEMLIB_API char *luamem_tomemory (lua_State *L, int idx, size_t *len) { +LUAMEMLIB_API char *luamem_tomemoryx (lua_State *L, int idx, size_t *len, int *isref) { void *p = lua_touserdata(L, idx); + if (isref) *isref = 0; if (p) { /* value is a userdata? */ if (lua_getmetatable(L, idx)) { /* does it have a metatable? */ char *mem; @@ -91,8 +72,9 @@ LUAMEMLIB_API char *luamem_tomemory (lua_State *L, int idx, size_t *len) { luaL_getmetatable(L, LUAMEM_REF); /* get referenced memory metatable */ if (lua_rawequal(L, -1, -2)) { luamem_Ref *ref = (luamem_Ref *)p; + if (isref) *isref = 1; if (len) *len = ref->len; - mem = ref->memory; + mem = ref->mem; } else { if (len) *len = 0; mem = NULL; @@ -129,6 +111,29 @@ LUAMEMLIB_API const char *luamem_checkstring (lua_State *L, int idx, size_t *len } +LUAMEMLIB_API void *luamem_realloc(lua_State *L, void *mem, size_t old, + size_t new) +{ + void *userdata; + lua_Alloc alloc = lua_getallocf(L, &userdata); + return alloc(userdata, mem, old, new); +} + +LUAMEMLIB_API void luamem_free(lua_State *L, void *memo, size_t size) +{ + void *userdata; + lua_Alloc alloc = lua_getallocf(L, &userdata); + alloc(userdata, memo, size, 0); +} + +LUAMEMLIB_API size_t luamem_checklenarg (lua_State *L, int idx) +{ + lua_Integer sz = luaL_checkinteger(L, idx); + luaL_argcheck(L, 0 <= sz && sz < (lua_Integer)LUAMEM_MAXALLOC, + idx, "invalid size"); + return (size_t)sz; +} + /* * NOTE: most of the code below is copied from the source of Lua 5.3.1 by * R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. diff --git a/src/lmemlib.h b/src/lmemlib.h index 1cd163a..07b8a91 100644 --- a/src/lmemlib.h +++ b/src/lmemlib.h @@ -1,11 +1,3 @@ -/* -** $Id$ -** Lua Stream - Generic stream support for the Lua language -** Renato Maia, Tecgraf/PUC-Rio (http://www.tecgraf.puc-rio.br/~maia) -** See Copyright Notice at the end of this file -*/ - - #ifndef lstraux_h #define lstraux_h @@ -29,47 +21,45 @@ #define LUAMEM_REF "luamem_Ref" -typedef void (*luamem_Unref) (char *mem, size_t len); +LUAMEMLIB_API char *(luamem_newalloc) (lua_State *L, size_t len); -typedef struct luamem_Ref { - size_t len; - luamem_Unref unref; - /* private part */ - char *memory; -} luamem_Ref; +typedef void (*luamem_Unref) (lua_State *L, void *mem, size_t len); -#define luamem_toref(L,I) (luaL_testudata(L,I,LUAMEM_REF)) -#define luamem_isref(L,I) (luamem_toref(L,I) != NULL) -#define luamem_isalloc(L,I) (luaL_testudata(L,I,LUAMEM_ALLOC) != NULL) -#define luamem_ismemory(L,I) (luaL_tomemory(L,I,NULL) != NULL) +#define luamem_isref(L,I) (luaL_testudata(L,I,LUAMEM_REF) != NULL) +LUAMEMLIB_API void (luamem_newref) (lua_State *L); +LUAMEMLIB_API int (luamem_setref) (lua_State *L, int idx, char *mem, size_t len, luamem_Unref unref); -LUAMEMLIB_API char * (luamem_newalloc) (lua_State *L, size_t len); -LUAMEMLIB_API void (luamem_pushrefmt) (lua_State *L); -LUAMEMLIB_API luamem_Ref *(luamem_pushref) (lua_State *L, char *mem); -LUAMEMLIB_API luamem_Ref *(luamem_getref) (lua_State *L, char *mem); -LUAMEMLIB_API luamem_Ref *(luamem_toref) (lua_State *L, int idx); +#define luamem_ismemory(L,I) (luamem_tomemory(L,I,NULL) != NULL) +#define luamem_tomemory(L,I,S) (luamem_tomemoryx(L,I,S,NULL)) -LUAMEMLIB_API char * (luamem_tomemory) (lua_State *L, int idx, size_t *len); -LUAMEMLIB_API char * (luamem_checkmemory) (lua_State *L, int idx, size_t *len); +LUAMEMLIB_API char *(luamem_tomemoryx) (lua_State *L, int idx, size_t *len, int *isref); +LUAMEMLIB_API char *(luamem_checkmemory) (lua_State *L, int idx, size_t *len); -LUAMEMLIB_API int (luamem_isstring) (lua_State *L, int idx); + +LUAMEMLIB_API int (luamem_isstring) (lua_State *L, int idx); LUAMEMLIB_API const char *(luamem_tostring) (lua_State *L, int idx, size_t *len); LUAMEMLIB_API const char *(luamem_checkstring) (lua_State *L, int idx, size_t *len); +LUAMEMLIB_API void *(luamem_realloc) (lua_State *L, void *mem, size_t old, + size_t new); +LUAMEMLIB_API void (luamem_free) (lua_State *L, void *memo, size_t size); +LUAMEMLIB_API size_t (luamem_checklenarg) (lua_State *L, int idx); + + /* ** 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 LUABUF_MAXSIZE \ +#define LUAMEM_MAXALLOC \ (sizeof(size_t) < sizeof(int) ? (~(size_t)0) : (size_t)(INT_MAX)) -LUAMEMLIB_API lua_Integer luamem_posrelat (lua_Integer pos, size_t len); -LUAMEMLIB_API int luamem_str2byte (lua_State *L, const char *s, size_t l); -LUAMEMLIB_API void luamem_code2char (lua_State *L, int idx, char *p, int n); +LUAMEMLIB_API lua_Integer (luamem_posrelat) (lua_Integer pos, size_t len); +LUAMEMLIB_API int (luamem_str2byte) (lua_State *L, const char *s, size_t l); +LUAMEMLIB_API void (luamem_code2char) (lua_State *L, int idx, char *p, int n); /* diff --git a/src/lmemmod.c b/src/lmemmod.c index 9603205..434fcf0 100644 --- a/src/lmemmod.c +++ b/src/lmemmod.c @@ -7,73 +7,118 @@ static int mem_create (lua_State *L) { - char *p; - size_t lb; - const char *s = NULL; - if (lua_type(L, 1) == LUA_TNUMBER) { - lua_Integer sz = luaL_checkinteger(L, 1); - luaL_argcheck(L, 0 <= sz && sz < (lua_Integer)LUABUF_MAXSIZE, 1, - "invalid size"); - lb = (size_t)sz; + if (lua_gettop(L) == 0) { + luamem_newref(L); } else { - lua_Integer posi, pose; - s = luamem_checkstring(L, 1, &lb); - posi = luamem_posrelat(luaL_optinteger(L, 2, 1), lb); - pose = luamem_posrelat(luaL_optinteger(L, 3, -1), lb); - if (posi < 1) posi = 1; - if (pose > (lua_Integer)lb) pose = lb; - if (posi > pose) { - lb = 0; - s = NULL; + char *p; + size_t len; + const char *s = NULL; + if (lua_type(L, 1) == LUA_TNUMBER) { + len = luamem_checklenarg(L, 1); } else { - lb = (int)(pose - posi + 1); - if (posi + lb <= pose) /* arithmetic overflow? */ - return luaL_error(L, "string slice too long"); - s += posi-1; + lua_Integer posi, pose; + s = luamem_checkstring(L, 1, &len); + posi = luamem_posrelat(luaL_optinteger(L, 2, 1), len); + pose = luamem_posrelat(luaL_optinteger(L, 3, -1), len); + if (posi < 1) posi = 1; + if (pose > (lua_Integer)len) pose = len; + if (posi > pose) { + len = 0; + s = NULL; + } else { + len = (int)(pose - posi + 1); + if (posi + len <= pose) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + s += posi-1; + } } + p = luamem_newalloc(L, len); + if (s) memcpy(p, s, len * sizeof(char)); + else memset(p, 0, len * sizeof(char)); + } + return 1; +} + +static int mem_resize (lua_State *L) { + char *mem; + size_t len; + size_t size = luamem_checklenarg(L, 2); + luaL_argcheck(L, luamem_isref(L, 1), 1, "resizable memory expected"); + mem = luamem_tomemory(L, 1, &len); + if (len != size) { + mem = luamem_realloc(L, mem, len, size); + if (!mem) luaL_error(L, "out of memory"); + if (len < size) memset(mem+len, 0, (size-len)*sizeof(char)); + luamem_setref(L, 1, mem, size, luamem_free); } - p = luamem_newalloc(L, lb); - if (s) memcpy(p, s, lb * sizeof(char)); + return 0; +} + +static int mem_type (lua_State *L) { + int isref; + char *mem = luamem_tomemoryx(L, 1, NULL, &isref); + if (!mem) lua_pushnil(L); + else if (isref) lua_pushliteral(L, "fixed"); + else lua_pushliteral(L, "resizable"); return 1; } static int mem_len (lua_State *L) { - size_t lb; - luamem_checkmemory(L, 1, &lb); - lua_pushinteger(L, (lua_Integer)lb); + size_t len; + luamem_checkmemory(L, 1, &len); + lua_pushinteger(L, (lua_Integer)len); return 1; } static int mem_tostring (lua_State *L) { - size_t lb; - const char *s = luamem_checkmemory(L, 1, &lb); - if (lb>0) lua_pushlstring(L, s, lb); + size_t len; + const char *s = luamem_checkmemory(L, 1, &len); + if (len>0) lua_pushlstring(L, s, len); else lua_pushliteral(L, ""); return 1; } +static int mem_diff (lua_State *L) { + size_t l1, l2; + const char *s1 = luamem_checkstring(L, 1, &l1); + const char *s2 = luamem_checkstring(L, 2, &l2); + size_t i, n=(l1= (int)sizeof(size_t) || len < ((size_t)1 << (size * NB)), arg, "string length does not fit in given size"); - if (!packint(&buff, &i, lb, (lua_Unsigned)len, h.islittle, size, 0) || /* pack length */ - !packstream(&buff, &i, lb, s, len)) + if (!packint(&mem, &i, lb, (lua_Unsigned)len, h.islittle, size, 0) || /* pack length */ + !packstream(&mem, &i, lb, s, len)) return packfailed(L, i, arg); break; } @@ -476,12 +522,12 @@ static int mem_pack (lua_State *L) { size_t len; const char *s = luamem_checkstring(L, arg, &len); luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); - if (!packstream(&buff, &i, lb, s, len) || !packchar(&buff, &i, lb, '\0')) + if (!packstream(&mem, &i, lb, s, len) || !packchar(&mem, &i, lb, '\0')) return packfailed(L, i, arg); break; } case Kpadding: { - if (!getbytes(&buff, &i, lb, 1)) + if (!getbytes(&mem, &i, lb, 1)) return packfailed(L, i, arg); /* go through */ } @@ -492,8 +538,7 @@ static int mem_pack (lua_State *L) { } lua_pushboolean(L, 1); lua_pushinteger(L, i+1); - lua_pushinteger(L, arg-2); - return 3; + return 2; } @@ -600,9 +645,12 @@ static int mem_unpack (lua_State *L) { static const luaL_Reg lib[] = { {"create", mem_create}, + {"type", mem_type}, + {"resize", mem_resize}, + {"len", mem_len}, + {"diff", mem_diff}, {"fill", mem_fill}, {"get", mem_get}, - {"len", mem_len}, {"set", mem_set}, {"pack", mem_pack}, {"unpack", mem_unpack}, @@ -616,7 +664,8 @@ static const luaL_Reg meta[] = { }; -static void setupmeta (lua_State *L) { +static void setupmetatable (lua_State *L, const char *name) { + luaL_newmetatable(L, name); luaL_setfuncs(L, meta, 0); /* add metamethods to new metatable */ lua_pushvalue(L, -2); /* push library */ lua_setfield(L, -2, "__index"); /* metatable.__index = library */ @@ -626,9 +675,7 @@ static void setupmeta (lua_State *L) { LUAMEMMOD_API int luaopen_memory (lua_State *L) { luaL_newlib(L, lib); - luaL_newmetatable(L, LUAMEM_ALLOC); - setupmeta(L); - luamem_pushrefmt(L); - setupmeta(L); + setupmetatable(L, LUAMEM_ALLOC); + setupmetatable(L, LUAMEM_REF); return 1; } diff --git a/test/testall.lua b/test/testall.lua index 28a233c..f2b785f 100644 --- a/test/testall.lua +++ b/test/testall.lua @@ -1,8 +1,4 @@ --- $Id$ -local buffer = require "buffer" -local stream = require "buffer.stream" - -print('testing buffers and buffer functions') +local memory = require "memory" local maxi, mini = math.maxinteger, math.mininteger @@ -12,58 +8,61 @@ local function checkerror(msg, f, ...) assert(not s and string.find(err, msg)) end --- testing stream.isbuffer(string) -assert(stream.isbuffer(nil) == false) -assert(stream.isbuffer(true) == false) -assert(stream.isbuffer(false) == false) -assert(stream.isbuffer(0) == false) -assert(stream.isbuffer(2^70) == false) -assert(stream.isbuffer('123') == false) -assert(stream.isbuffer({}) == false) -assert(stream.isbuffer(function () end) == false) -assert(stream.isbuffer(print) == false) -assert(stream.isbuffer(coroutine.running()) == false) -assert(stream.isbuffer(io.stdout) == false) - --- testing stream.isbuffer(string), buffer:set(i, d), buffer:get(i) +print "memory.type(value)" +assert(memory.type(nil) == nil) +assert(memory.type(true) == nil) +assert(memory.type(false) == nil) +assert(memory.type(0) == nil) +assert(memory.type(2^70) == nil) +assert(memory.type('123') == nil) +assert(memory.type({}) == nil) +assert(memory.type(function () end) == nil) +assert(memory.type(print) == nil) +assert(memory.type(coroutine.running()) == nil) +assert(memory.type(io.stdout) == nil) + +print "memory.type(string), memory:set(i, d), memory:get(i)" local checkmodifiable do local allchars = {} for i = 255, 0, -1 do allchars[#allchars+1] = string.char(i) end allchars = table.concat(allchars) + local types = { + fixed = true, + resizable = true, + } function checkmodifiable(b, size) - assert(stream.isbuffer(b) == true) - assert(stream.len(b) == size) + assert(types[memory.type(b)] ~= nil) + assert(memory.len(b) == size) assert(#b == size) for i = 1, size do - buffer.set(b, i, math.max(0, 256-i)) + memory.set(b, i, math.max(0, 256-i)) end for i = 1, size do - assert(buffer.get(b, i) == math.max(0, 256-i)) + assert(memory.get(b, i) == math.max(0, 256-i)) end local expected = (size > #allchars) and allchars..string.rep("\0", size - #allchars) or allchars:sub(1, size) - assert(stream.diff(b, expected) == nil) + assert(memory.diff(b, expected) == nil) end end --- testing buffer:set(i, d) -do - local b = buffer.create(10) - checkerror("value out of range", buffer.set, b, 1, 256) - checkerror("value out of range", buffer.set, b, 1, 511) - checkerror("value out of range", buffer.set, b, 1, -1) +do print "memory:set(i, d)" + local b = memory.create(10) + checkerror("value out of range", memory.set, b, 1, 256) + checkerror("value out of range", memory.set, b, 1, 511) + checkerror("value out of range", memory.set, b, 1, -1) end -do -- testing buffer.create(string), stream.diff, stream.len, #buffer +do print "memory.create(string), memory.diff, memory.len, #memory" local function check(data, str, expi, explt) - local b = buffer.create(data) - local i, lt = stream.diff(b, str) + local b = memory.create(data) + local i, lt = memory.diff(b, str) assert(i == expi) assert(lt == explt) - local i, lt = stream.diff(b, buffer.create(str)) + local i, lt = memory.diff(b, memory.create(str)) assert(i == expi) assert(lt == explt) checkmodifiable(b, #data) @@ -85,9 +84,9 @@ do -- testing buffer.create(string), stream.diff, stream.len, #buffer check('\0\0b', '\0\0a\0', 3, false) end -do -- testing buffer.create(size) +do print "memory.create(size)" local function check(size) - checkmodifiable(buffer.create(size), size) + checkmodifiable(memory.create(size), size) end check(0) check(1) @@ -96,13 +95,13 @@ do -- testing buffer.create(size) check(8192) end -do -- testing buffer.create(buffer|string [, i [, j]]) +do print "memory.create(memory|string [, i [, j]])" local function check(expected, data, ...) - local b = buffer.create(data, ...) - assert(stream.diff(b, expected) == nil) + local b = memory.create(data, ...) + assert(memory.diff(b, expected) == nil) checkmodifiable(b, #expected) - local b = buffer.create(buffer.create(data), ...) - assert(stream.diff(b, expected) == nil) + local b = memory.create(memory.create(data), ...) + assert(memory.diff(b, expected) == nil) checkmodifiable(b, #expected) end check("" , "") @@ -126,29 +125,29 @@ do -- testing buffer.create(buffer|string [, i [, j]]) check("234" , "\000123456789",3,5) end -do -- testing buffer:fill(stream [, i [, j]]) +do print "memory:fill(string [, i [, j]])" local data = string.rep(" ", 10) local full = "1234567890ABCDEF" local function fillup(space) return full:sub(1, #space) end local function check(expected, i, j) - for _, S in ipairs({tostring, buffer.create}) do - local b = buffer.create(data) - buffer.fill(b, S"", i, j) - assert(stream.diff(b, data) == nil) - buffer.fill(b, S"xuxu", i, j, 5) - assert(stream.diff(b, data) == nil) - buffer.fill(b, S"abc", i, j) - assert(stream.diff(b, expected) == nil) - buffer.fill(b, 0x55, i, j) - assert(stream.diff(b, expected:gsub("%S", "\x55")) == nil) - buffer.fill(b, S"XYZ", i, j, 3) - assert(stream.diff(b, expected:gsub("%S", "Z")) == nil) - buffer.fill(b, S"XYZ", i, j, -1) - assert(stream.diff(b, expected:gsub("%S", "Z")) == nil) - buffer.fill(b, S(full), i, j) - assert(stream.diff(b, expected:gsub("%S+", fillup)) == nil) + for _, S in ipairs({tostring, memory.create}) do + local b = memory.create(data) + memory.fill(b, S"", i, j) + assert(memory.diff(b, data) == nil) + memory.fill(b, S"xuxu", i, j, 5) + assert(memory.diff(b, data) == nil) + memory.fill(b, S"abc", i, j) + assert(memory.diff(b, expected) == nil) + memory.fill(b, 0x55, i, j) + assert(memory.diff(b, expected:gsub("%S", "\x55")) == nil) + memory.fill(b, S"XYZ", i, j, 3) + assert(memory.diff(b, expected:gsub("%S", "Z")) == nil) + memory.fill(b, S"XYZ", i, j, -1) + assert(memory.diff(b, expected:gsub("%S", "Z")) == nil) + memory.fill(b, S(full), i, j) + assert(memory.diff(b, expected:gsub("%S+", fillup)) == nil) end end check("abcabcabca") @@ -164,8 +163,8 @@ do -- testing buffer:fill(stream [, i [, j]]) check(" abca",-4) check(" abc ",-6, -4) local function check(...) - local b = buffer.create(data) - checkerror("index out of bounds", buffer.fill, b, "xuxu", ...) + local b = memory.create(data) + checkerror("index out of bounds", memory.fill, b, "xuxu", ...) end check( mini, maxi) check( mini, mini) @@ -175,383 +174,35 @@ do -- testing buffer:fill(stream [, i [, j]]) check( mini, -4) check( 3, maxi) do - local b = buffer.create(full) - buffer.fill(b, b) - assert(stream.diff(b, full) == nil) + local b = memory.create(full) + memory.fill(b, b) + assert(memory.diff(b, full) == nil) end do - local b = buffer.create(full) - buffer.fill(b, 0) - assert(stream.diff(b, string.rep("\0", #full)) == nil) + local b = memory.create(full) + memory.fill(b, 0) + assert(memory.diff(b, string.rep("\0", #full)) == nil) end do - local b = buffer.create(full) - buffer.fill(b, b, 11, -1) - assert(stream.diff(b, "1234567890123456") == nil) + local b = memory.create(full) + memory.fill(b, b, 11, -1) + assert(memory.diff(b, "1234567890123456") == nil) end do - local b = buffer.create(full) - buffer.fill(b, b, 1, 6, 11) - assert(stream.diff(b, "ABCDEF7890ABCDEF") == nil) + local b = memory.create(full) + memory.fill(b, b, 1, 6, 11) + assert(memory.diff(b, "ABCDEF7890ABCDEF") == nil) end do - local b = buffer.create(full) - buffer.fill(b, b, 1, 10, 7) - assert(stream.diff(b, "7890ABCDEFABCDEF") == nil) + local b = memory.create(full) + memory.fill(b, b, 1, 10, 7) + assert(memory.diff(b, "7890ABCDEFABCDEF") == nil) end do - local b = buffer.create(full) - buffer.fill(b, b, 7, -1) - assert(stream.diff(b, "1234561234567890") == nil) - end -end - -do -- testing stream.tostring(b|s [, i [, j]]), stream.byte(b|s [, i [, j]]) - local function check(data, ...) - local i,j = ... - local expected = string.sub(data, i or 1, j) - local s = data - assert(stream.tostring(s, ...) == expected) - if select("#", ...) >= 2 then - assert(string.char(stream.byte(s, ...)) == expected) - end - local s = buffer.create(data) - assert(stream.tostring(s, ...) == expected) - if select("#", ...) >= 2 then - assert(string.char(stream.byte(s, ...)) == expected) - end - end - check("a") - check("\xe4") - check("\255") - check("\0") - check("\0\0alo\0x", -1) - check("ba", 2) - check("\n\n", 2, -1) - check("\n\n", 2, 2) - check("") - check("hi", -3) - check("hi", 3) - check("hi", 9, 10) - check("hi", 2, 1) - check("\0\255\0") - check("\0\xe4\0") - check("\xe4l\0óu", 1, -1) - check("\xe4l\0óu", 1, 0) - check("\xe4l\0óu", -10, 100) -end - - - - - ---[======[ -do -- testing stream.concat(out, list [, sep [, i [, j]]]) - local function makelists(list) - local buffers = {} - local mixed = {} - for index, str in pairs(list) do - local b = buffer.create(str) - buffer[index] = b - mixed[index] = index%2==0 and str or b - end - return { - strings = list, - buffers = buffers, - mixed = mixed, - } - end - local function check(expected, lists, ...) - for _, list in pairs(lists) do - local str = stream.concat("string", list, ...) - assert(str == expected) - local buf = stream.concat("buffer", list, ...) - assert(stream.diff(buf, expected) == expected) - checkmodifiable(buf) - end + local b = memory.create(full) + memory.fill(b, b, 7, -1) + assert(memory.diff(b, "1234561234567890") == nil) end - local empty = makelists{} - check("", empty) - check("", empty, 'x') - check("", empty, "x", maxi, maxi - 1) - check("", empty, "x", mini + 1, mini) - check("", empty, "x", maxi, mini) - check("\0.\0.\0\1.\0.\0\1\2", makelists{'\0', '\0\1', '\0\1\2'}, '.\0.') - local a = {}; for i=1,300 do a[i] = "xuxu" end - a = makelists(a) - check(string.rep("xuxu", 300, "123"), a, "123") - check("xuxu" , a, "b" , 20, 20) - check("xuxuxuxu" , a, "" , 20, 21) - check("" , a, "x" , 22, 21) - check("xuxu3xuxu" , a, "3" , 299) - check("alo" , makelists{[maxi]="alo" }, "x", maxi, maxi) - check("y-alo", makelists{[maxi]="alo",[maxi-1]="y"}, "-", maxi-1, maxi) - - assert(not pcall(stream.concat, {buffer.create"a", "b", {}})) - - a = makelists{"a","b","c"} - check("", a, ",", 1, 0) - check("a", a, ",", 1, 1) - check("a,b", a, ",", 1, 2) - check("b,c", a, ",", 2) - check("c", a, ",", 3) - check("", a, ",", 4) end --- testing string.find -assert(string.find("123456789", "345") == 3) -a,b = string.find("123456789", "345") -assert(string.sub("123456789", a, b) == "345") -assert(string.find("1234567890123456789", "345", 3) == 3) -assert(string.find("1234567890123456789", "345", 4) == 13) -assert(string.find("1234567890123456789", "346", 4) == nil) -assert(string.find("1234567890123456789", ".45", -9) == 13) -assert(string.find("abcdefg", "\0", 5, 1) == nil) -assert(string.find("", "") == 1) -assert(string.find("", "", 1) == 1) -assert(not string.find("", "", 2)) -assert(string.find('', 'aaa', 1) == nil) -assert(('alo(.)alo'):find('(.)', 1, 1) == 4) - --- testing string.byte/string.char -assert(string.byte("a") == 97) -assert(string.byte("\xe4") > 127) -assert(string.byte(string.char(255)) == 255) -assert(string.byte(string.char(0)) == 0) -assert(string.byte("\0") == 0) -assert(string.byte("\0\0alo\0x", -1) == string.byte('x')) -assert(string.byte("ba", 2) == 97) -assert(string.byte("\n\n", 2, -1) == 10) -assert(string.byte("\n\n", 2, 2) == 10) -assert(string.byte("") == nil) -assert(string.byte("hi", -3) == nil) -assert(string.byte("hi", 3) == nil) -assert(string.byte("hi", 9, 10) == nil) -assert(string.byte("hi", 2, 1) == nil) -assert(string.char() == "") -assert(string.char(0, 255, 0) == "\0\255\0") -assert(string.char(0, string.byte("\xe4"), 0) == "\0\xe4\0") -assert(string.char(string.byte("\xe4l\0óu", 1, -1)) == "\xe4l\0óu") -assert(string.char(string.byte("\xe4l\0óu", 1, 0)) == "") -assert(string.char(string.byte("\xe4l\0óu", -10, 100)) == "\xe4l\0óu") - -assert(string.upper("ab\0c") == "AB\0C") -assert(string.lower("\0ABCc%$") == "\0abcc%$") -assert(string.rep('teste', 0) == '') -assert(string.rep('tés\00tê', 2) == 'tés\0têtés\000tê') -assert(string.rep('', 10) == '') - -if string.packsize("i") == 4 then - -- result length would be 2^31 (int overflow) - checkerror("too large", string.rep, 'aa', (1 << 30)) - checkerror("too large", string.rep, 'a', (1 << 30), ',') -end - --- repetitions with separator -assert(string.rep('teste', 0, 'xuxu') == '') -assert(string.rep('teste', 1, 'xuxu') == 'teste') -assert(string.rep('\1\0\1', 2, '\0\0') == '\1\0\1\0\0\1\0\1') -assert(string.rep('', 10, '.') == string.rep('.', 9)) -assert(not pcall(string.rep, "aa", maxi // 2)) -assert(not pcall(string.rep, "", maxi // 2, "aa")) - -assert(string.reverse"" == "") -assert(string.reverse"\0\1\2\3" == "\3\2\1\0") -assert(string.reverse"\0001234" == "4321\0") - -for i=0,30 do assert(string.len(string.rep('a', i)) == i) end - -assert(type(tostring(nil)) == 'string') -assert(type(tostring(12)) == 'string') -assert(string.find(tostring{}, 'table:')) -assert(string.find(tostring(print), 'function:')) -assert(#tostring('\0') == 1) -assert(tostring(true) == "true") -assert(tostring(false) == "false") -assert(tostring(-1203) == "-1203") -assert(tostring(1203.125) == "1203.125") -assert(tostring(-0.5) == "-0.5") -assert(tostring(-32767) == "-32767") -if 2147483647 > 0 then -- no overflow? (32 bits) - assert(tostring(-2147483647) == "-2147483647") -end -if 4611686018427387904 > 0 then -- no overflow? (64 bits) - assert(tostring(4611686018427387904) == "4611686018427387904") - assert(tostring(-4611686018427387904) == "-4611686018427387904") -end - -if tostring(0.0) == "0.0" then -- "standard" coercion float->string - assert('' .. 12 == '12' and 12.0 .. '' == '12.0') - assert(tostring(-1203 + 0.0) == "-1203.0") -else -- compatible coercion - assert(tostring(0.0) == "0") - assert('' .. 12 == '12' and 12.0 .. '' == '12') - assert(tostring(-1203 + 0.0) == "-1203") -end - - -x = '"ílo"\n\\' -assert(string.format('%q%s', x, x) == '"\\"ílo\\"\\\n\\\\""ílo"\n\\') -assert(string.format('%q', "\0") == [["\0"]]) -assert(load(string.format('return %q', x))() == x) -x = "\0\1\0023\5\0009" -assert(load(string.format('return %q', x))() == x) -assert(string.format("\0%c\0%c%x\0", string.byte("\xe4"), string.byte("b"), 140) == - "\0\xe4\0b8c\0") -assert(string.format('') == "") -assert(string.format("%c",34)..string.format("%c",48)..string.format("%c",90)..string.format("%c",100) == - string.format("%c%c%c%c", 34, 48, 90, 100)) -assert(string.format("%s\0 is not \0%s", 'not be', 'be') == 'not be\0 is not \0be') -assert(string.format("%%%d %010d", 10, 23) == "%10 0000000023") -assert(tonumber(string.format("%f", 10.3)) == 10.3) -x = string.format('"%-50s"', 'a') -assert(#x == 52) -assert(string.sub(x, 1, 4) == '"a ') - -assert(string.format("-%.20s.20s", string.rep("%", 2000)) == - "-"..string.rep("%", 20)..".20s") -assert(string.format('"-%20s.20s"', string.rep("%", 2000)) == - string.format("%q", "-"..string.rep("%", 2000)..".20s")) - --- format x tostring -assert(string.format("%s %s", nil, true) == "nil true") -assert(string.format("%s %.4s", false, true) == "false true") -assert(string.format("%.3s %.3s", false, true) == "fal tru") -local m = setmetatable({}, {__tostring = function () return "hello" end}) -assert(string.format("%s %.10s", m, m) == "hello hello") - - -assert(string.format("%x", 0.0) == "0") -assert(string.format("%02x", 0.0) == "00") -assert(string.format("%08X", 4294967295) == "FFFFFFFF") -assert(string.format("%+08d", 31501) == "+0031501") -assert(string.format("%+08d", -30927) == "-0030927") - - -do -- longest number that can be formatted - local i = 1 - local j = 10000 - while i + 1 < j do -- binary search for maximum finite float - local m = (i + j) // 2 - if 10^m < math.huge then i = m else j = m end - end - assert(10^i < math.huge and 10^j == math.huge) - assert(string.len(string.format('%.99f', -(10^i))) > i) -end - - --- testing large numbers for format -do -- assume at least 32 bits - local max, min = 0x7fffffff, -0x80000000 -- "large" for 32 bits - assert(string.sub(string.format("%8x", -1), -8) == "ffffffff") - assert(string.format("%x", max) == "7fffffff") - assert(string.sub(string.format("%x", min), -8) == "80000000") - assert(string.format("%d", max) == "2147483647") - assert(string.format("%d", min) == "-2147483648") - assert(string.format("%u", 0xffffffff) == "4294967295") - assert(string.format("%o", 0xABCD) == "125715") - - max, min = 0x7fffffffffffffff, -0x8000000000000000 - if max > 2.0^53 then -- only for 64 bits - assert(string.format("%x", (2^52 | 0) - 1) == "fffffffffffff") - assert(string.format("0x%8X", 0x8f000003) == "0x8F000003") - assert(string.format("%d", 2^53) == "9007199254740992") - assert(string.format("%i", -2^53) == "-9007199254740992") - assert(string.format("%x", max) == "7fffffffffffffff") - assert(string.format("%x", min) == "8000000000000000") - assert(string.format("%d", max) == "9223372036854775807") - assert(string.format("%d", min) == "-9223372036854775808") - assert(string.format("%u", ~(-1 << 64)) == "18446744073709551615") - assert(tostring(1234567890123) == '1234567890123') - end -end - - -do print("testing 'format %a %A'") - local function matchhexa (n) - local s = string.format("%a", n) - -- result matches ISO C requirements - assert(string.find(s, "^%-?0x[1-9a-f]%.?[0-9a-f]*p[-+]?%d+$")) - assert(tonumber(s) == n) -- and has full precision - s = string.format("%A", n) - assert(string.find(s, "^%-?0X[1-9A-F]%.?[0-9A-F]*P[-+]?%d+$")) - assert(tonumber(s) == n) - end - for _, n in ipairs{0.1, -0.1, 1/3, -1/3, 1e30, -1e30, - -45/247, 1, -1, 2, -2, 3e-20, -3e-20} do - matchhexa(n) - end - - assert(string.find(string.format("%A", 0.0), "^0X0%.?0?P%+?0$")) - assert(string.find(string.format("%a", -0.0), "^%-0x0%.?0?p%+?0$")) - - if not _port then -- test inf, -inf, NaN, and -0.0 - assert(string.find(string.format("%a", 1/0), "^inf")) - assert(string.find(string.format("%A", -1/0), "^%-INF")) - assert(string.find(string.format("%a", 0/0), "^%-?nan")) - assert(string.find(string.format("%a", -0.0), "^%-0x0")) - end - - if not pcall(string.format, "%.3a", 0) then - (Message or print)("\n >>> modifiers for format '%a' not available <<<\n") - else - assert(string.find(string.format("%+.2A", 12), "^%+0X%x%.%x0P%+?%d$")) - assert(string.find(string.format("%.4A", -12), "^%-0X%x%.%x000P%+?%d$")) - end -end - - --- errors in format - -local function check (fmt, msg) - checkerror(msg, string.format, fmt, 10) -end - -local aux = string.rep('0', 600) -check("%100.3d", "too long") -check("%1"..aux..".3d", "too long") -check("%1.100d", "too long") -check("%10.1"..aux.."004d", "too long") -check("%t", "invalid option") -check("%"..aux.."d", "repeated flags") -check("%d %d", "no value") - - -assert(load("return 1\n--comment without ending EOL")() == 1) - - - -if not _port then - - local locales = { "ptb", "ISO-8859-1", "pt_BR" } - local function trylocale (w) - for i = 1, #locales do - if os.setlocale(locales[i], w) then return true end - end - return false - end - - if not trylocale("collate") then - print("locale not supported") - else - assert("alo" < "álo" and "álo" < "amo") - end - - if not trylocale("ctype") then - print("locale not supported") - else - assert(string.gsub("áéíóú", "%a", "x") == "xxxxx") - assert(string.gsub("áÁéÉ", "%l", "x") == "xÁxÉ") - assert(string.gsub("áÁéÉ", "%u", "x") == "áxéx") - assert(string.upper"áÁé{xuxu}ção" == "ÁÁÉ{XUXU}ÇÃO") - end - - os.setlocale("C") - assert(os.setlocale() == 'C') - assert(os.setlocale(nil, "numeric") == 'C') - -end - ---]======] - -print('OK') +print "OK" diff --git a/test/tpack.lua b/test/tpack.lua index 2cc8739..94e136a 100644 --- a/test/tpack.lua +++ b/test/tpack.lua @@ -1,8 +1,61 @@ --- $Id: tpack.lua,v 1.10 2014/12/26 17:20:53 roberto Exp $ -local buffer = require "buffer" -local pack = buffer.pack +local memory = require "memory" +local pack = memory.pack local packsize = string.packsize -local unpack = buffer.unpack +local unpack = memory.unpack + +do + local function failpack(expectpos, expectvals, ok, pos, ...) + assert(ok == false) + assert(pos == expectpos) + for i, value in ipairs(expectvals) do + assert(value == select(i, ...)) + end + assert(select("#", ...) == #expectvals) + end + + local m = memory.create(0) + failpack(1, {123,456,789}, pack(m, 1, "b", 123,456,789)) + + local m = memory.create(10) + memory.fill(m, 0x55) + local values = { + 0x11111111, + 0x22222222, + 0x33333333, + 0x44444444, + } + local expectvals = { + 0x33333333, + 0x44444444, + } + failpack(9, expectvals, pack(m, 1, "i4i4i4i4", table.unpack(values))) +end + +--[[ +NOTE: most of the code below is copied from the tests of Lua 5.3.1 by + R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. + +Copyright (C) 1994-2015 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. +--]] print "testing pack/unpack" @@ -40,10 +93,10 @@ function checkerror (msg, f, ...) assert(not status and string.find(err, msg)) end -local function assertpack(sz, n, ok, pos, validx) - assert(ok) +local function assertpack(sz, n, ok, pos, ...) + assert(ok == true) assert(pos == sz+1) - assert(validx == n+1) + assert(select("#", ...) == 0) end local function assertunpack(sz, vals, ...) @@ -58,7 +111,7 @@ end do local function testpack(fmt, val) local sz = packsize(fmt) - local b = buffer.create(sz) + local b = memory.create(sz) assertpack(sz, 1, pack(b, 1, fmt, val)) assertunpack(sz, {val}, unpack(b, 1, fmt)) end @@ -76,7 +129,7 @@ end for i = 1, NB do -- small numbers with signal extension ("\xFF...") local s = string.rep("\xff", i) - local b = buffer.create(i) + local b = memory.create(i) assertpack(i, 1, pack(b, 1, "i" .. i, -1)) assert(tostring(b) == s) assertunpack(i, {-1}, unpack(b, 1, "i" .. i)) @@ -92,36 +145,36 @@ for i = 1, NB do end do - local b = buffer.create(sizeLI+1) + local b = memory.create(sizeLI+1) local lnum = 0x13121110090807060504030201 assertpack(sizeLI, 1, pack(b, 1, "i" .. i)) -- overflows - buffer.fill(b, 0, 1, i-1) - buffer.set(b, i, 1) + memory.fill(b, 0, 1, i-1) + memory.set(b, i, 1) checkerror("does not fit", unpack, b, 1, "i" .. i) end end @@ -131,7 +184,7 @@ for i = 1, sizeLI do local lnum = 0x13121110090807060504030201 local n = lnum & (~(-1 << (i * 8))) local s = string.sub(lstr, 1, i) - local b = buffer.create(i) + local b = memory.create(i) assertpack(i, 1, pack(b, 1, "i" .. i, n)) @@ -143,10 +196,10 @@ end do local u = 0xf0 for i = 1, sizeLI - 1 do - local b = buffer.create(i) - buffer.set(b, 1, 0xf0) + local b = memory.create(i) + memory.set(b, 1, 0xf0) if i>=2 then - buffer.fill(b, 0xff, 2, i) + memory.fill(b, 0xff, 2, i) end assertunpack(i, {-16}, unpack(b, 1, "I"..i)) @@ -156,10 +209,10 @@ end -- mixed endianness do - local b = buffer.create(4) + local b = memory.create(4) assertpack(4, 2, pack(b, 1, ">i2 i2")) assertpack(4, 1, pack(b, 1, "=i4", 2001)) local s = tostring(b) @@ -170,13 +223,13 @@ end print("testing invalid formats") do - local b = buffer.create(math.max(16, NB+1)) + local b = memory.create(math.max(16, NB+1)) checkerror("out of limits", pack, b, 1, "i0", 0) checkerror("out of limits", pack, b, 1, "i" .. NB + 1, 0) checkerror("out of limits", pack, b, 1, "!" .. NB + 1, 0) checkerror("%(17%) out of limits %[1,16%]", pack, b, 1, "Xi" .. NB + 1) checkerror("invalid format option 'r'", pack, b, 1, "i3r", 0) - buffer.fill(b, 16, 1, 16) + memory.fill(b, 16, 1, 16) checkerror("16%-byte integer", unpack, b, 1, "i16") checkerror("not power of 2", pack, b, 1, "!4i3", 0); checkerror("missing size", pack, b, 1, "c", "") @@ -184,7 +237,7 @@ end -- overflow in packing for i = 1, sizeLI - 1 do - local b = buffer.create(i) + local b = memory.create(i) local umax = (1 << (i * 8)) - 1 local max = umax >> 1 local min = ~max @@ -206,7 +259,7 @@ end -- Lua integer size do - local b = buffer.create(sizeLI) + local b = memory.create(sizeLI) assertpack(sizeLI, 1, pack(b, 1, ">j", math.maxinteger)) assertunpack(sizeLI, {math.maxinteger}, unpack(b, 1, ">j")) assertpack(sizeLI, 1, pack(b, 1, " Date: Mon, 29 Jan 2018 00:41:20 -0200 Subject: [PATCH 03/24] Add the tests for resizable memory. --- README.md | 1 - demo/fill.lua | 18 +-- doc/manual.md | 2 +- src/lmemlib.c | 28 ++-- src/lmemlib.h | 12 +- src/lmemmod.c | 25 ++-- test/testall.lua | 346 ++++++++++++++++++++++++++--------------------- 7 files changed, 241 insertions(+), 191 deletions(-) diff --git a/README.md b/README.md index 374fcc5..1791e94 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ TODO - Add the C API to the manual. - Finish adaptation of `string.pack` tests to test `memory.pack`. -- Add the tests for resizable memory. - Add support for bitwise operations on the memory contents. Documentation diff --git a/demo/fill.lua b/demo/fill.lua index 7c5d7bf..911acb4 100644 --- a/demo/fill.lua +++ b/demo/fill.lua @@ -1,24 +1,24 @@ -- create a memory -local b = memory.create(10) +local m = memory.create(10) -- fill memory with zeros -memory.fill(b, 0) +memory.fill(m, 0) -- iterate over memory -for i = 1, memory.len(b) do - print(i, memory.get(b, i)) +for i = 1, memory.len(m) do + print(i, memory.get(m, i)) end -- iterate to fill the memory -for i = 1, memory.len(b) do - memory.set(b, i, 2*i) +for i = 1, memory.len(m) do + memory.set(m, i, 2*i) end -- sets 4th, 5th and 6th bytes in the memory -memory.set(b, 4, 0xff, 0xff, 0xff) +memory.set(m, 4, 0xff, 0xff, 0xff) -- copy 3 bytes from position 4 to position 1 -memory.fill(b, b, 1, 3, 4) +memory.fill(m, m, 1, 3, 4) -- clear the positions after the 3 first bytes -memory.fill(b, 0, 4) +memory.fill(m, 0, 4) diff --git a/doc/manual.md b/doc/manual.md index 44e4ce5..7025736 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -89,7 +89,7 @@ Sets the values of all bytes in memory `m` in the range from position `i` until If, after the translation of negative indices, `o` is less than 1, it is corrected to 1. After the translation of negative indices, `i` and `j` must refer to valid positions of `m`. -If `i` is greater and `j` (empty range), or `o` refers to a position beyond the size of `m` (no contents) this function has no effect. +If `i` is greater and `j` (empty range), or `o` refers to a position beyond the size of `s` (no contents) this function has no effect. Otherwise, the specified contents from `s` (from `o`) are copied repeatedly until they fill all bytes in the specified range of `m` (from `i` to `j`). If `s` is a number then all bytes in the specified range of `m` are set with the value of `s`. diff --git a/src/lmemlib.c b/src/lmemlib.c index 35ba16f..14c0a3a 100644 --- a/src/lmemlib.c +++ b/src/lmemlib.c @@ -44,7 +44,8 @@ LUAMEMLIB_API void luamem_newref (lua_State *L) { lua_setmetatable(L, -2); } -LUAMEMLIB_API int luamem_setref (lua_State *L, int idx, char *mem, size_t len, luamem_Unref unref) { +LUAMEMLIB_API int luamem_setref (lua_State *L, int idx, + char *mem, size_t len, luamem_Unref unref) { luamem_Ref *ref = luaL_testudata(L, 1, LUAMEM_REF); if (ref) { if (mem != ref->mem) unref(L, ref); @@ -57,35 +58,36 @@ LUAMEMLIB_API int luamem_setref (lua_State *L, int idx, char *mem, size_t len, l } -LUAMEMLIB_API char *luamem_tomemoryx (lua_State *L, int idx, size_t *len, int *isref) { +LUAMEMLIB_API char *luamem_tomemoryx (lua_State *L, int idx, + size_t *len, luamem_Unref *unref, + int *type) { + char *mem = NULL; void *p = lua_touserdata(L, idx); - if (isref) *isref = 0; + if (len) *len = 0; + if (unref) *unref = NULL; + if (type) *type = LUAMEM_TNONE; if (p) { /* value is a userdata? */ if (lua_getmetatable(L, idx)) { /* does it have a metatable? */ - char *mem; luaL_getmetatable(L, LUAMEM_ALLOC); /* get allocated memory metatable */ if (lua_rawequal(L, -1, -2)) { /* is the same? */ - if (len) *len = lua_rawlen(L, idx); mem = (char *)p; + if (len) *len = lua_rawlen(L, idx); + if (type) *type = LUAMEM_TALLOC; } else { lua_pop(L, 1); /* remove allocated memory metatable */ luaL_getmetatable(L, LUAMEM_REF); /* get referenced memory metatable */ if (lua_rawequal(L, -1, -2)) { luamem_Ref *ref = (luamem_Ref *)p; - if (isref) *isref = 1; - if (len) *len = ref->len; mem = ref->mem; - } else { - if (len) *len = 0; - mem = NULL; + if (len) *len = ref->len; + if (unref) *unref = ref->unref; + if (type) *type = LUAMEM_TREF; } } lua_pop(L, 2); /* remove both metatables */ - return mem; } } - if (len) *len = 0; - return NULL; + return mem; } LUAMEMLIB_API char *luamem_checkmemory (lua_State *L, int idx, size_t *len) { diff --git a/src/lmemlib.h b/src/lmemlib.h index 07b8a91..e18c3a1 100644 --- a/src/lmemlib.h +++ b/src/lmemlib.h @@ -16,6 +16,9 @@ #endif +#define LUAMEM_TNONE 0 +#define LUAMEM_TALLOC 1 +#define LUAMEM_TREF 2 #define LUAMEM_ALLOC "char[]" #define LUAMEM_REF "luamem_Ref" @@ -29,13 +32,16 @@ typedef void (*luamem_Unref) (lua_State *L, void *mem, size_t len); #define luamem_isref(L,I) (luaL_testudata(L,I,LUAMEM_REF) != NULL) LUAMEMLIB_API void (luamem_newref) (lua_State *L); -LUAMEMLIB_API int (luamem_setref) (lua_State *L, int idx, char *mem, size_t len, luamem_Unref unref); +LUAMEMLIB_API int (luamem_setref) (lua_State *L, int idx, + char *mem, size_t len, luamem_Unref unref); #define luamem_ismemory(L,I) (luamem_tomemory(L,I,NULL) != NULL) -#define luamem_tomemory(L,I,S) (luamem_tomemoryx(L,I,S,NULL)) +#define luamem_tomemory(L,I,S) (luamem_tomemoryx(L,I,S,NULL,NULL)) -LUAMEMLIB_API char *(luamem_tomemoryx) (lua_State *L, int idx, size_t *len, int *isref); +LUAMEMLIB_API char *(luamem_tomemoryx) (lua_State *L, int idx, + size_t *len, luamem_Unref *unref, + int *type); LUAMEMLIB_API char *(luamem_checkmemory) (lua_State *L, int idx, size_t *len); diff --git a/src/lmemmod.c b/src/lmemmod.c index 434fcf0..07c23dc 100644 --- a/src/lmemmod.c +++ b/src/lmemmod.c @@ -9,6 +9,7 @@ static int mem_create (lua_State *L) { if (lua_gettop(L) == 0) { luamem_newref(L); + luamem_setref(L, 1, NULL, 0, luamem_free); } else { char *p; size_t len; @@ -40,11 +41,11 @@ static int mem_create (lua_State *L) { } static int mem_resize (lua_State *L) { - char *mem; size_t len; + luamem_Unref unref; + char *mem = luamem_tomemoryx(L, 1, &len, &unref, NULL); size_t size = luamem_checklenarg(L, 2); - luaL_argcheck(L, luamem_isref(L, 1), 1, "resizable memory expected"); - mem = luamem_tomemory(L, 1, &len); + luaL_argcheck(L, unref == luamem_free, 1, "resizable memory expected"); if (len != size) { mem = luamem_realloc(L, mem, len, size); if (!mem) luaL_error(L, "out of memory"); @@ -55,11 +56,17 @@ static int mem_resize (lua_State *L) { } static int mem_type (lua_State *L) { - int isref; - char *mem = luamem_tomemoryx(L, 1, NULL, &isref); - if (!mem) lua_pushnil(L); - else if (isref) lua_pushliteral(L, "fixed"); - else lua_pushliteral(L, "resizable"); + luamem_Unref unref; + int type; + luamem_tomemoryx(L, 1, NULL, &unref, &type); + if (type == LUAMEM_TALLOC) { + lua_pushliteral(L, "fixed"); + } else if (type == LUAMEM_TREF) { + if (unref == luamem_free) lua_pushliteral(L, "resizable"); + else lua_pushliteral(L, "other"); + } else { + lua_pushnil(L); + } return 1; } @@ -73,7 +80,7 @@ static int mem_len (lua_State *L) { static int mem_tostring (lua_State *L) { size_t len; const char *s = luamem_checkmemory(L, 1, &len); - if (len>0) lua_pushlstring(L, s, len); + if (len > 0) lua_pushlstring(L, s, len); else lua_pushliteral(L, ""); return 1; } diff --git a/test/testall.lua b/test/testall.lua index f2b785f..f4c047f 100644 --- a/test/testall.lua +++ b/test/testall.lua @@ -8,20 +8,7 @@ local function checkerror(msg, f, ...) assert(not s and string.find(err, msg)) end -print "memory.type(value)" -assert(memory.type(nil) == nil) -assert(memory.type(true) == nil) -assert(memory.type(false) == nil) -assert(memory.type(0) == nil) -assert(memory.type(2^70) == nil) -assert(memory.type('123') == nil) -assert(memory.type({}) == nil) -assert(memory.type(function () end) == nil) -assert(memory.type(print) == nil) -assert(memory.type(coroutine.running()) == nil) -assert(memory.type(io.stdout) == nil) - -print "memory.type(string), memory:set(i, d), memory:get(i)" +-- memory.type(string), memory:set(i, d), memory:get(i) local checkmodifiable do local allchars = {} for i = 255, 0, -1 do @@ -49,160 +36,209 @@ local checkmodifiable do end end -do print "memory:set(i, d)" - local b = memory.create(10) - checkerror("value out of range", memory.set, b, 1, 256) - checkerror("value out of range", memory.set, b, 1, 511) - checkerror("value out of range", memory.set, b, 1, -1) -end - -do print "memory.create(string), memory.diff, memory.len, #memory" - local function check(data, str, expi, explt) - local b = memory.create(data) - local i, lt = memory.diff(b, str) - assert(i == expi) - assert(lt == explt) - local i, lt = memory.diff(b, memory.create(str)) - assert(i == expi) - assert(lt == explt) - checkmodifiable(b, #data) +local function newresizable(s, i, j) + local m = memory.create() + if type(s) == "number" then + memory.resize(m, s) + else + memory.resize(m, #s) + memory.fill(m, s, i, j) end - check('alo', 'alo1', 4, true) - check('', 'a', 1, true) - check('alo\0alo', 'alo\0b', 5, true) - check('alo\0alo\0\0', 'alo\0alo\0', 9, false) - check('alo', 'alo\0', 4, true) - check('alo\0', 'alo', 4, false) - check('\0', '\1', 1, true) - check('\0\0', '\0\1', 2, true) - check('\1\0a\0a', '\1\0a\0a', nil, false) - check('\1\0a\0b', '\1\0a\0a', 5, false) - check('\0\0\0', '\0\0\0\0', 4, true) - check('\0\0\0\0', '\0\0\0', 4, false) - check('\0\0\0', '\0\0\0\0', 4, true) - check('\0\0\0', '\0\0\0', nil, false) - check('\0\0b', '\0\0a\0', 3, false) + return m end -do print "memory.create(size)" - local function check(size) - checkmodifiable(memory.create(size), size) - end - check(0) - check(1) - check(2) - check(100) - check(8192) -end +for kind, newmem in pairs{fixedsize=memory.create, resizable=newresizable} do -do print "memory.create(memory|string [, i [, j]])" - local function check(expected, data, ...) - local b = memory.create(data, ...) - assert(memory.diff(b, expected) == nil) - checkmodifiable(b, #expected) - local b = memory.create(memory.create(data), ...) - assert(memory.diff(b, expected) == nil) - checkmodifiable(b, #expected) + do print(kind, "memory.type(value)") + assert(memory.type(nil) == nil) + assert(memory.type(true) == nil) + assert(memory.type(false) == nil) + assert(memory.type(0) == nil) + assert(memory.type(2^70) == nil) + assert(memory.type('123') == nil) + assert(memory.type({}) == nil) + assert(memory.type(function () end) == nil) + assert(memory.type(print) == nil) + assert(memory.type(coroutine.running()) == nil) + assert(memory.type(io.stdout) == nil) + assert(memory.type(memory.create()) == "resizable") + assert(memory.type(memory.create(10)) == "fixed") + assert(memory.type(memory.create("abc")) == "fixed") + assert(memory.type(memory.create("Lua Memory 1.0", 5, -5)) == "fixed") end - check("" , "") - check("" , "", 1) - check("" , "", 1, -1) - check("" , "", mini, maxi) - check("234" , "123456789",2,4) - check("789" , "123456789",7) - check("" , "123456789",7,6) - check("7" , "123456789",7,7) - check("" , "123456789",0,0) - check("123456789", "123456789",-10,10) - check("123456789", "123456789",1,9) - check("" , "123456789",-10,-20) - check("9" , "123456789",-1) - check("6789" , "123456789",-4) - check("456" , "123456789",-6, -4) - check("123456" , "123456789", mini, -4) - check("123456789", "123456789", mini, maxi) - check("" , "123456789", mini, mini) - check("234" , "\000123456789",3,5) -end -do print "memory:fill(string [, i [, j]])" - local data = string.rep(" ", 10) - local full = "1234567890ABCDEF" - local function fillup(space) - return full:sub(1, #space) + do print(kind, "memory:set(i, d)") + local b = memory.create(10) + checkerror("value out of range", memory.set, b, 1, 256) + checkerror("value out of range", memory.set, b, 1, 511) + checkerror("value out of range", memory.set, b, 1, -1) end - local function check(expected, i, j) - for _, S in ipairs({tostring, memory.create}) do + + do print(kind, "memory.create(string), memory.diff, memory.len, #memory") + local function check(data, str, expi, explt) local b = memory.create(data) - memory.fill(b, S"", i, j) - assert(memory.diff(b, data) == nil) - memory.fill(b, S"xuxu", i, j, 5) - assert(memory.diff(b, data) == nil) - memory.fill(b, S"abc", i, j) - assert(memory.diff(b, expected) == nil) - memory.fill(b, 0x55, i, j) - assert(memory.diff(b, expected:gsub("%S", "\x55")) == nil) - memory.fill(b, S"XYZ", i, j, 3) - assert(memory.diff(b, expected:gsub("%S", "Z")) == nil) - memory.fill(b, S"XYZ", i, j, -1) - assert(memory.diff(b, expected:gsub("%S", "Z")) == nil) - memory.fill(b, S(full), i, j) - assert(memory.diff(b, expected:gsub("%S+", fillup)) == nil) + local i, lt = memory.diff(b, str) + assert(i == expi) + assert(lt == explt) + local i, lt = memory.diff(b, memory.create(str)) + assert(i == expi) + assert(lt == explt) + checkmodifiable(b, #data) end + check('alo', 'alo1', 4, true) + check('', 'a', 1, true) + check('alo\0alo', 'alo\0b', 5, true) + check('alo\0alo\0\0', 'alo\0alo\0', 9, false) + check('alo', 'alo\0', 4, true) + check('alo\0', 'alo', 4, false) + check('\0', '\1', 1, true) + check('\0\0', '\0\1', 2, true) + check('\1\0a\0a', '\1\0a\0a', nil, false) + check('\1\0a\0b', '\1\0a\0a', 5, false) + check('\0\0\0', '\0\0\0\0', 4, true) + check('\0\0\0\0', '\0\0\0', 4, false) + check('\0\0\0', '\0\0\0\0', 4, true) + check('\0\0\0', '\0\0\0', nil, false) + check('\0\0b', '\0\0a\0', 3, false) end - check("abcabcabca") - check("abcabcabca", 1) - check("abcabcabca", 1, -1) - check(" abc ", 2, 4) - check(" abca", 7) - check(" ", 7, 6) - check(" a ", 7, 7) - check("abcabcabca",-10, 10) - check("abcabcabc ", 1, 9) - check(" a",-1) - check(" abca",-4) - check(" abc ",-6, -4) - local function check(...) - local b = memory.create(data) - checkerror("index out of bounds", memory.fill, b, "xuxu", ...) - end - check( mini, maxi) - check( mini, mini) - check( mini, maxi) - check( 0, 0) - check(-10,-20) - check( mini, -4) - check( 3, maxi) - do - local b = memory.create(full) - memory.fill(b, b) - assert(memory.diff(b, full) == nil) - end - do - local b = memory.create(full) - memory.fill(b, 0) - assert(memory.diff(b, string.rep("\0", #full)) == nil) - end - do - local b = memory.create(full) - memory.fill(b, b, 11, -1) - assert(memory.diff(b, "1234567890123456") == nil) - end - do - local b = memory.create(full) - memory.fill(b, b, 1, 6, 11) - assert(memory.diff(b, "ABCDEF7890ABCDEF") == nil) + + do print(kind, "memory.create(size)") + local function check(size) + checkmodifiable(memory.create(size), size) + end + check(0) + check(1) + check(2) + check(100) + check(8192) end - do - local b = memory.create(full) - memory.fill(b, b, 1, 10, 7) - assert(memory.diff(b, "7890ABCDEFABCDEF") == nil) + + do print(kind, "memory.create(memory|string [, i [, j]])") + local function check(expected, data, ...) + local b = memory.create(data, ...) + assert(memory.diff(b, expected) == nil) + checkmodifiable(b, #expected) + local b = memory.create(memory.create(data), ...) + assert(memory.diff(b, expected) == nil) + checkmodifiable(b, #expected) + end + check("" , "") + check("" , "", 1) + check("" , "", 1, -1) + check("" , "", mini, maxi) + check("234" , "123456789",2,4) + check("789" , "123456789",7) + check("" , "123456789",7,6) + check("7" , "123456789",7,7) + check("" , "123456789",0,0) + check("123456789", "123456789",-10,10) + check("123456789", "123456789",1,9) + check("" , "123456789",-10,-20) + check("9" , "123456789",-1) + check("6789" , "123456789",-4) + check("456" , "123456789",-6, -4) + check("123456" , "123456789", mini, -4) + check("123456789", "123456789", mini, maxi) + check("" , "123456789", mini, mini) + check("234" , "\000123456789",3,5) end - do - local b = memory.create(full) - memory.fill(b, b, 7, -1) - assert(memory.diff(b, "1234561234567890") == nil) + + do print(kind, "memory:fill(string [, i [, j]])") + local data = string.rep(" ", 10) + local full = "1234567890ABCDEF" + local function fillup(space) + return full:sub(1, #space) + end + local function check(expected, i, j) + for _, S in ipairs({tostring, memory.create}) do + local b = memory.create(data) + memory.fill(b, S"", i, j) + assert(memory.diff(b, data) == nil) + memory.fill(b, S"xuxu", i, j, 5) + assert(memory.diff(b, data) == nil) + memory.fill(b, S"abc", i, j) + assert(memory.diff(b, expected) == nil) + memory.fill(b, 0x55, i, j) + assert(memory.diff(b, expected:gsub("%S", "\x55")) == nil) + memory.fill(b, S"XYZ", i, j, 3) + assert(memory.diff(b, expected:gsub("%S", "Z")) == nil) + memory.fill(b, S"XYZ", i, j, -1) + assert(memory.diff(b, expected:gsub("%S", "Z")) == nil) + memory.fill(b, S(full), i, j) + assert(memory.diff(b, expected:gsub("%S+", fillup)) == nil) + end + end + check("abcabcabca") + check("abcabcabca", 1) + check("abcabcabca", 1, -1) + check(" abc ", 2, 4) + check(" abca", 7) + check(" ", 7, 6) + check(" a ", 7, 7) + check("abcabcabca",-10, 10) + check("abcabcabc ", 1, 9) + check(" a",-1) + check(" abca",-4) + check(" abc ",-6, -4) + local function check(...) + local b = memory.create(data) + checkerror("index out of bounds", memory.fill, b, "xuxu", ...) + end + check( mini, maxi) + check( mini, mini) + check( mini, maxi) + check( 0, 0) + check(-10,-20) + check( mini, -4) + check( 3, maxi) + do + local b = memory.create(full) + memory.fill(b, b) + assert(memory.diff(b, full) == nil) + end + do + local b = memory.create(full) + memory.fill(b, 0) + assert(memory.diff(b, string.rep("\0", #full)) == nil) + end + do + local b = memory.create(full) + memory.fill(b, b, 11, -1) + assert(memory.diff(b, "1234567890123456") == nil) + end + do + local b = memory.create(full) + memory.fill(b, b, 1, 6, 11) + assert(memory.diff(b, "ABCDEF7890ABCDEF") == nil) + end + do + local b = memory.create(full) + memory.fill(b, b, 1, 10, 7) + assert(memory.diff(b, "7890ABCDEFABCDEF") == nil) + end + do + local b = memory.create(full) + memory.fill(b, b, 7, -1) + assert(memory.diff(b, "1234561234567890") == nil) + end end + +end + +do print "memory.resize(m, size)" + local m = memory.create(3) + checkerror("resizable memory expected", memory.resize, m, 10) + + local m = memory.create() + memory.resize(m, 10) + assert(tostring(m) == string.rep('\0', 10)) + + memory.fill(m, "abcde") + memory.resize(m, 15) + assert(tostring(m) == "abcdeabcde\0\0\0\0\0") + + memory.resize(m, 5) + assert(tostring(m) == "abcde") end print "OK" From 4838a8689c4b5a830573562664d4634f506c132e Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Mon, 12 Feb 2018 13:26:37 -0200 Subject: [PATCH 04/24] Fix bug that leads to double deallocation when resizing memory. --- src/lmemmod.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lmemmod.c b/src/lmemmod.c index 07c23dc..2f78348 100644 --- a/src/lmemmod.c +++ b/src/lmemmod.c @@ -47,10 +47,11 @@ static int mem_resize (lua_State *L) { size_t size = luamem_checklenarg(L, 2); luaL_argcheck(L, unref == luamem_free, 1, "resizable memory expected"); if (len != size) { - mem = luamem_realloc(L, mem, len, size); - if (!mem) luaL_error(L, "out of memory"); - if (len < size) memset(mem+len, 0, (size-len)*sizeof(char)); - luamem_setref(L, 1, mem, size, luamem_free); + char *new = luamem_realloc(L, mem, len, size); + if (!new) luaL_error(L, "out of memory"); + luamem_setref(L, 1, mem, len, NULL); /* don't free `mem` again */ + if (len < size) memset(new+len, 0, (size-len)*sizeof(char)); + luamem_setref(L, 1, new, size, luamem_free); } return 0; } From bb95c986fea978b00b059b27984c5d30bd8562bb Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Mon, 12 Feb 2018 13:27:39 -0200 Subject: [PATCH 05/24] Add the C API to the manual. --- README.md | 13 ++-- doc/manual.md | 178 +++++++++++++++++++++++++++++++++++++++++++++++++- src/lmemlib.c | 21 +++--- src/lmemlib.h | 19 +----- 4 files changed, 196 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 1791e94..78061bd 100644 --- a/README.md +++ b/README.md @@ -8,19 +8,18 @@ The library provides the following functionalities: - Support for writable memory areas. - C API to manipulate strings or memory areas in a unified way. -TODO ----- - -- Add the C API to the manual. -- Finish adaptation of `string.pack` tests to test `memory.pack`. -- Add support for bitwise operations on the memory contents. - Documentation ------------- - [Manual](doc/manual.md) - [License](LICENSE) +TODO +---- + +- Finish adaptation of `string.pack` tests to test `memory.pack`. +- Add support for bitwise operations on the memory contents. + History ------- diff --git a/doc/manual.md b/doc/manual.md index 7025736..84b0da7 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -13,6 +13,30 @@ Index - [`memory.unpack`](#memoryunpack-m-i-fmt) - [`tostring`](#tostring-m) +- [`LUAMEM_ALLOC`](#luamem_newalloc) +- [`LUAMEM_REF`](#luamem_newref) +- [`LUAMEM_TALLOC`](#luamem_tomemoryx) +- [`LUAMEM_TNONE`](#luamem_tomemoryx) +- [`LUAMEM_TREF`](#luamem_tomemoryx) +- [`luamem_Unref`](#luamem_Unref) +- [`luamem_addvalue`](#luamem_addvalue) +- [`luamem_checklenarg`](#luamem_checklenarg) +- [`luamem_checkmemory`](#luamem_checkmemory) +- [`luamem_checkstring`](#luamem_checkstring) +- [`luamem_free`](#luamem_free) +- [`luamem_ismemory`](#luamem_ismemory) +- [`luamem_isref`](#luamem_isref) +- [`luamem_isstring`](#luamem_isstring) +- [`luamem_newalloc`](#luamem_newalloc) +- [`luamem_newref`](#luamem_newref) +- [`luamem_pushresbuf`](#luamem_pushresbuf) +- [`luamem_pushresbufsize`](#luamem_pushresbufsize) +- [`luamem_realloc`](#luamem_realloc) +- [`luamem_setref`](#luamem_setref) +- [`luamem_tomemory`](#luamem_tomemory) +- [`luamem_tomemoryx`](#luamem_tomemoryx) +- [`luamem_tostring`](#luamem_tostring) + Contents ======== @@ -53,7 +77,8 @@ Any extra bytes have value zero. ### `memory.type (m)` -Returns `"fixed"` if `m` is a fixed-size memory, or `"resizable"` if it is a resizable memory, or `nil` otherwise. +Returns `"fixed"` if `m` is a fixed-size memory, or `"resizable"` if it is a resizable memory, or `other` if it is an external memory created using the C API. +Otherwise it returns `nil`. ### `memory.len (m)` @@ -108,3 +133,154 @@ After the read values, this function also returns the index of the first unread ### `tostring (m)` Returns a string with the contents of memory `m`. + +C Library API +------------- + +This section describes the C API provided as a separate library to create and manipulate memory areas from C. +There are two distinct types of memory areas in the C API: + +- __allocated__: points to a constant block address with fixed size, which is automatically released when the memory is garbage collected (see [`luamem_newalloc`](#luamem_newalloc)). +- __referenced__: points to a memory area with block address and size provided by the application, which can provide a unrefering function to be used to free the memory area when it is not pointed by the Lua memory object anymore (see [`luamem_newref`](#luamem_newref)). + +### `luamem_newalloc` + +`char *luamem_newalloc (lua_State *L, size_t len);` + +Creates and pushes onto the stack a new allocated memory with the given size, and returns its block address. + +Allocated memory areas uses metatable created with name given by constant `LUAMEM_ALLOC` (see [`luaL_newmetatable`](http://www.lua.org/manual/5.3/manual.html#luaL_newmetatable)). + +### `luamem_Unref` + +`typedef void (*luamem_Unref) (lua_State *L, void *mem, size_t len);` + +Type for memory unrefering functions. + +These functions are called whenever a referenced memory ceases to pointo to block address `mem` which have size of `len` bytes. (see [`luamem_setref`](#luamem_setref)). + +### `luamem_newref` + +`void luamem_newref (lua_State *L);` + +Creates and pushes onto the stack a new reference memory pointing to NULL, with length zero, and no unrefering function (see [`luamem_Unref`](#luamem_Unref)). + +Referenced memory areas uses metatable created with name given by constant `LUAMEM_REF` (see [`luaL_newmetatable`](http://www.lua.org/manual/5.3/manual.html#luaL_newmetatable)). + +### `luamem_setref` + +`int luamem_setref (lua_State *L, int idx, char *mem, size_t len, luamem_Unref unref);` + +Defines the block address (`mem`), size (`len`), and unrefering function (`unref`) of the referenced memory at index `idx`, and returns 1. +If `idx` does not contain a referenced memory it returns 0; + +If `unref` is not `NULL`, it will be called when the memory ceases to point to this block address, either by being garbage collected or if it is updated to point to another block address (by a future call of `luamem_setref`). + +If `mem` points to the same block address currently pointed by referenced memory at index `idx` then the unrefering function previously registered is not invoked. +Therefore, to avoid the call of the current unrefering function of memory at index `idx` you can do: + +```C +size_t len; +char *mem = luamem_tomemory(L, idx, &len); +luamem_setref(L, idx, mem, len, NULL); /* only update `unref` to NULL */ +``` + +### `luamem_isref` + +`int luamem_isref(lua_State *L, int idx);` + +Returns 1 if the value at the given index is a referenced memory, and 0 otherwise. + +### `luamem_ismemory` + +`int luamem_ismemory (lua_State *L, int idx);` + +Returns 1 if the value at the given index is a memory (allocated or referenced), and 0 otherwise. + +### `luamem_tomemory` + +`void *luamem_tomemory (lua_State *L, int idx, size_t *len);` + +Equivalent to `luamem_tomemoryx(L, idx, len, NULL, NULL)`. + +### `luamem_tomemoryx` + +`char *luamem_tomemoryx (lua_State *L, int idx, size_t *len, luamem_Unref *unref, int *type);` + +Return the block address of memory at the given index, or `NULL` if the value is not a memory. + +If `len` is not `NULL`, it sets `*len` with the memory size. +If `unref` is not `NULL`, it sets `*unref` with the unrefering function if the value is a referenced memory, or `NULL` otherwise. +If `type` is not `NULL`, it sets `*type` with `LUAMEM_TREF` if the value is referenced memory, or `LUAMEM_TALLOC` in case of an allocated memory, or `LUAMEM_TNONE` otherwise. + +Because Lua has garbage collection, there is no guarantee that the pointer returned by `luamem_tomemory` will be valid after the corresponding Lua value is removed from the stack. + +### `luamem_checkmemory` + +`char *luamem_checkmemory (lua_State *L, int arg, size_t *len);` + +Checks whether the function argument `arg` is a memory (allocated or referenced) and returns a pointer to its contents; +if `len` is not `NULL` fills `*len` with the memory's length. + +### `luamem_isstring` + +`int luamem_isstring (lua_State *L, int idx);` + +Returns 1 if the value at the given index is a string or memory, and 0 otherwise. + +### `luamem_tostring` + +`const char *luamem_tostring (lua_State *L, int idx, size_t *len);` + +If the value at the given index is a memory it behaves like [`luamem_tomemory`](#luamem_tomemory), but retuning a pointer to constant bytes. +Otherwise, it is equivalent to [`lua_tolstring`](http://www.lua.org/manual/5.3/manual.html#lua_tolstring). + +__Note__: Unlike Lua strings, memory areas are not followed by a null byte (`'\0'`). + +### `luamem_checkstring` + +`const char *luamem_checkstring (lua_State *L, int arg, size_t *len);` + +Checks whether the function argument `arg` is a string or memory and returns a pointer to its contents; +if `len` is not `NULL` fills `*len` with the contents' length. + +This function might use `lua_tolstring` to get its result, so all conversions and caveats of that function apply here. + +### `luamem_checklenarg` + +`size_t luamem_checklenarg (lua_State *L, int arg);` + +Checks whether the function argument `arg` is an integer (or can be converted to an integer) of a valid memory size and returns this integer cast to a `size_t`. + +### `luamem_realloc` + +`void *luamem_realloc (lua_State *L, void *mem, size_t old, size_t new);` + +Reallocates memory pointed by `mem` of size `old` with new size `new` using the allocation function registered by the Lua state (see [`lua_getallocf`](http://www.lua.org/manual/5.3/manual.html#lua_getallocf)). +Returns the reallocated memory. + +### `luamem_free` + +`void luamem_free (lua_State *L, void *mem, size_t sz);` + +Equivalent to `luamem_realloc(L, mem, sz, 0)`. + +__Note__: Any referenced memory which uses this function as the unrefering function is considered a resizable memory by the `memory` module. + +### `luamem_addvalue` + +`void luamem_addvalue (luaL_Buffer *B);` + +Similar to [`luaL_addvalue`](http://www.lua.org/manual/5.3/manual.html#luaL_addvalue), but if the value on top of the stack is a memory, it adds its contents to the buffer without converting it to a Lua string. + +### `luamem_pushresult` + +`void luamem_pushresult (luaL_Buffer *B);` + +Similar to [`luamem_pushresult`](http://www.lua.org/manual/5.3/manual.html#luaL_pushresult), but leaves a memory with the buffer contents on the top of the stack instead of a string. + +### `luamem_pushresultfsize` + +`void luamem_pushresbufsize (luaL_Buffer *B, size_t sz);` + +Equivalent to the sequence [`luaL_addsize`](http://www.lua.org/manual/5.3/manual.html#luaL_addsize), [`luamem_pushresult`](#luamem_pushresult). diff --git a/src/lmemlib.c b/src/lmemlib.c index 14c0a3a..a0fa453 100644 --- a/src/lmemlib.c +++ b/src/lmemlib.c @@ -102,8 +102,9 @@ LUAMEMLIB_API int luamem_isstring (lua_State *L, int idx) { } LUAMEMLIB_API const char *luamem_tostring (lua_State *L, int idx, size_t *len) { - if (lua_isstring(L, idx)) return lua_tolstring(L, idx, len); - return luamem_tomemory(L, idx, len); + const char *s = luamem_tomemory(L, idx, len); + if (s) return s; + return lua_tolstring(L, idx, len); } LUAMEMLIB_API const char *luamem_checkstring (lua_State *L, int idx, size_t *len) { @@ -121,11 +122,9 @@ LUAMEMLIB_API void *luamem_realloc(lua_State *L, void *mem, size_t old, return alloc(userdata, mem, old, new); } -LUAMEMLIB_API void luamem_free(lua_State *L, void *memo, size_t size) +LUAMEMLIB_API void luamem_free(lua_State *L, void *mem, size_t size) { - void *userdata; - lua_Alloc alloc = lua_getallocf(L, &userdata); - alloc(userdata, memo, size, 0); + luamem_realloc(L, mem, size, 0); } LUAMEMLIB_API size_t luamem_checklenarg (lua_State *L, int idx) @@ -213,7 +212,7 @@ LUAMEMLIB_API void luamem_code2char (lua_State *L, int idx, char *p, int n) { #define buffonstack(B) ((B)->b != (B)->initb) -LUAMEMLIB_API void luamem_pushresbuf (luamem_Buffer *B) { +LUAMEMLIB_API void luamem_pushresult (luaL_Buffer *B) { lua_State *L = B->L; if (!buffonstack(B) || B->n < B->size) { char *p = (char *)luamem_newalloc(L, B->n * sizeof(char)); @@ -225,19 +224,19 @@ LUAMEMLIB_API void luamem_pushresbuf (luamem_Buffer *B) { } -LUAMEMLIB_API void luamem_pushresbufsize (luamem_Buffer *B, size_t sz) { +LUAMEMLIB_API void luamem_pushresultsize (luaL_Buffer *B, size_t sz) { luaL_addsize(B, sz); - luamem_pushresbuf(B); + luamem_pushresult(B); } -LUAMEMLIB_API void luamem_addvalue (luamem_Buffer *B) { +LUAMEMLIB_API void luamem_addvalue (luaL_Buffer *B) { lua_State *L = B->L; size_t l; const char *s = luamem_tostring(L, -1, &l); if (buffonstack(B)) lua_insert(L, -2); /* put value below buffer */ - luamem_addlstring(B, s, l); + luaL_addlstring(B, s, l); lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */ } diff --git a/src/lmemlib.h b/src/lmemlib.h index e18c3a1..bfaaf39 100644 --- a/src/lmemlib.h +++ b/src/lmemlib.h @@ -74,22 +74,9 @@ LUAMEMLIB_API void (luamem_code2char) (lua_State *L, int idx, char *p, int n); ** ======================================================= */ -#define luamem_Buffer luaL_Buffer -#define luamem_addchar luaL_addchar -#define luamem_addsize luaL_addsize -#define luamem_prepbuffsize luaL_prepbuffsize -#define luamem_prepbuffer luaL_prepbuffer -#define luamem_addlstring luaL_addlstring -#define luamem_addstring luaL_addstring -#define luamem_pushresult luaL_pushresult -#define luamem_pushresultsize luaL_pushresultsize -#define luamem_buffinit luaL_buffinit -#define luamem_buffinitsize luaL_buffinitsize - - -LUAMEMLIB_API void (luamem_addvalue) (luamem_Buffer *B); -LUAMEMLIB_API void (luamem_pushresbuf) (luamem_Buffer *B); -LUAMEMLIB_API void (luamem_pushresbufsize) (luamem_Buffer *B, size_t sz); +LUAMEMLIB_API void (luamem_addvalue) (luaL_Buffer *B); +LUAMEMLIB_API void (luamem_pushresbuf) (luaL_Buffer *B); +LUAMEMLIB_API void (luamem_pushresbufsize) (luaL_Buffer *B, size_t sz); /* }====================================================== */ From 840f4f54ac3f6a1bdd9dce979736315a12d08ae5 Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Mon, 12 Feb 2018 15:48:31 -0200 Subject: [PATCH 06/24] Make some functions copied from Lua source as internal, not exposed in the API. --- src/lmemlib.c | 38 ------------- src/lmemlib.h | 4 -- src/lmemmod.c | 146 ++++++++++++++++++++++++++++++++------------------ 3 files changed, 93 insertions(+), 95 deletions(-) diff --git a/src/lmemlib.c b/src/lmemlib.c index a0fa453..996a87d 100644 --- a/src/lmemlib.c +++ b/src/lmemlib.c @@ -161,44 +161,6 @@ LUAMEMLIB_API size_t luamem_checklenarg (lua_State *L, int idx) * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -/* macro to 'unsign' a character */ -#define uchar(c) ((unsigned char)(c)) - - -/* translate a relative string position: negative means back from end */ -LUAMEMLIB_API lua_Integer luamem_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; -} - -LUAMEMLIB_API int luamem_str2byte (lua_State *L, const char *s, size_t l) { - lua_Integer posi = luamem_posrelat(luaL_optinteger(L, 2, 1), l); - lua_Integer pose = luamem_posrelat(luaL_optinteger(L, 3, posi), l); - int n, i; - if (posi < 1) posi = 1; - if (pose > (lua_Integer)l) pose = l; - if (posi > pose) return 0; /* empty interval; return no values */ - n = (int)(pose - posi + 1); - if (posi + n <= pose) /* arithmetic overflow? */ - return luaL_error(L, "string slice too long"); - luaL_checkstack(L, n, "string slice too long"); - for (i=0; i - +static lua_Integer posrelat (lua_Integer pos, size_t len); +static int str2byte (lua_State *L, const char *s, size_t l); +static void code2char (lua_State *L, int idx, char *p, int n); static int mem_create (lua_State *L) { if (lua_gettop(L) == 0) { @@ -19,8 +21,8 @@ static int mem_create (lua_State *L) { } else { lua_Integer posi, pose; s = luamem_checkstring(L, 1, &len); - posi = luamem_posrelat(luaL_optinteger(L, 2, 1), len); - pose = luamem_posrelat(luaL_optinteger(L, 3, -1), len); + posi = posrelat(luaL_optinteger(L, 2, 1), len); + pose = posrelat(luaL_optinteger(L, 3, -1), len); if (posi < 1) posi = 1; if (pose > (lua_Integer)len) pose = len; if (posi > pose) { @@ -108,25 +110,25 @@ static int mem_diff (lua_State *L) { static int mem_get (lua_State *L) { size_t len; const char *s = luamem_checkmemory(L, 1, &len); - return luamem_str2byte(L, s, len); + return str2byte(L, s, len); } static int mem_set (lua_State *L) { size_t len; int n = lua_gettop(L)-2; /* number of bytes */ char *p = luamem_checkmemory(L, 1, &len); - lua_Integer i = luamem_posrelat(luaL_checkinteger(L, 2), len); + lua_Integer i = posrelat(luaL_checkinteger(L, 2), len); luaL_argcheck(L, 1 <= i && i <= (lua_Integer)len, 2, "index out of bounds"); len = 1+len-i; - luamem_code2char(L, 3, p+i-1, n= 0) return pos; + else if (0u - (size_t)pos > len) return 0; + else return (lua_Integer)len + pos + 1; +} + +static int str2byte (lua_State *L, const char *s, size_t l) { + lua_Integer posi = posrelat(luaL_optinteger(L, 2, 1), l); + lua_Integer pose = posrelat(luaL_optinteger(L, 3, posi), l); + int n, i; + if (posi < 1) posi = 1; + if (pose > (lua_Integer)l) pose = l; + if (posi > pose) return 0; /* empty interval; return no values */ + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + luaL_checkstack(L, n, "string slice too long"); + for (i=0; i= 0; i--) { res <<= NB; - res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; + res |= (lua_Unsigned)uchar(str[islittle ? i : size - 1 - i]); } if (size < SZINT) { /* real size smaller than lua_Integer? */ if (issigned) { /* needs sign extension? */ @@ -576,7 +655,7 @@ static lua_Integer unpackint (lua_State *L, const char *str, 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) + if (uchar(str[islittle ? i : size - 1 - i]) != mask) luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); } } @@ -588,7 +667,7 @@ static int mem_unpack (lua_State *L) { Header h; size_t ld; const char *data = luamem_checkmemory(L, 1, &ld); - size_t pos = (size_t)luamem_posrelat(luaL_checkinteger(L, 2), ld) - 1; + size_t pos = (size_t)posrelat(luaL_checkinteger(L, 2), ld) - 1; const char *fmt = luaL_checkstring(L, 3); int n = 0; /* number of results */ luaL_argcheck(L, pos <= ld, 2, "initial position out of bounds"); @@ -648,42 +727,3 @@ static int mem_unpack (lua_State *L) { } /* }====================================================== */ - - - -static const luaL_Reg lib[] = { - {"create", mem_create}, - {"type", mem_type}, - {"resize", mem_resize}, - {"len", mem_len}, - {"diff", mem_diff}, - {"fill", mem_fill}, - {"get", mem_get}, - {"set", mem_set}, - {"pack", mem_pack}, - {"unpack", mem_unpack}, - {NULL, NULL} -}; - -static const luaL_Reg meta[] = { - {"__len", mem_len}, - {"__tostring", mem_tostring}, - {NULL, NULL} -}; - - -static void setupmetatable (lua_State *L, const char *name) { - luaL_newmetatable(L, name); - luaL_setfuncs(L, meta, 0); /* add metamethods to new metatable */ - lua_pushvalue(L, -2); /* push library */ - lua_setfield(L, -2, "__index"); /* metatable.__index = library */ - lua_pop(L, 1); /* pop new metatable */ -} - - -LUAMEMMOD_API int luaopen_memory (lua_State *L) { - luaL_newlib(L, lib); - setupmetatable(L, LUAMEM_ALLOC); - setupmetatable(L, LUAMEM_REF); - return 1; -} From 3d3ffa52acab1dcdbfbff80e171d850ab12ed427 Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Tue, 13 Feb 2018 04:05:13 -0200 Subject: [PATCH 07/24] Show actual value type name in error messages of 'luamem_check*' functions. --- src/lmemlib.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/lmemlib.c b/src/lmemlib.c index 996a87d..ddb849d 100644 --- a/src/lmemlib.c +++ b/src/lmemlib.c @@ -7,6 +7,8 @@ #include +static int typeerror (lua_State *L, int arg, const char *tname); + LUAMEMLIB_API char *luamem_newalloc (lua_State *L, size_t l) { char *mem = lua_newuserdata(L, l * sizeof(char)); @@ -90,9 +92,9 @@ LUAMEMLIB_API char *luamem_tomemoryx (lua_State *L, int idx, return mem; } -LUAMEMLIB_API char *luamem_checkmemory (lua_State *L, int idx, size_t *len) { - char *mem = luamem_tomemory(L, idx, len); - if (!mem) luaL_argerror(L, idx, "memory expected"); +LUAMEMLIB_API char *luamem_checkmemory (lua_State *L, int arg, size_t *len) { + char *mem = luamem_tomemory(L, arg, len); + if (!mem) typeerror(L, arg, "memory"); return mem; } @@ -107,28 +109,27 @@ LUAMEMLIB_API const char *luamem_tostring (lua_State *L, int idx, size_t *len) { return lua_tolstring(L, idx, len); } -LUAMEMLIB_API const char *luamem_checkstring (lua_State *L, int idx, size_t *len) { - const char *s = luamem_tostring(L, idx, len); - if (!s) luaL_argerror(L, idx, "string expected"); +LUAMEMLIB_API const char *luamem_checkstring (lua_State *L, + int arg, + size_t *len) { + const char *s = luamem_tostring(L, arg, len); + if (!s) typeerror(L, arg, "string or memory"); return s; } LUAMEMLIB_API void *luamem_realloc(lua_State *L, void *mem, size_t old, - size_t new) -{ + size_t new) { void *userdata; lua_Alloc alloc = lua_getallocf(L, &userdata); return alloc(userdata, mem, old, new); } -LUAMEMLIB_API void luamem_free(lua_State *L, void *mem, size_t size) -{ +LUAMEMLIB_API void luamem_free(lua_State *L, void *mem, size_t size) { luamem_realloc(L, mem, size, 0); } -LUAMEMLIB_API size_t luamem_checklenarg (lua_State *L, int idx) -{ +LUAMEMLIB_API size_t luamem_checklenarg (lua_State *L, int idx) { lua_Integer sz = luaL_checkinteger(L, idx); luaL_argcheck(L, 0 <= sz && sz < (lua_Integer)LUAMEM_MAXALLOC, idx, "invalid size"); @@ -161,6 +162,19 @@ LUAMEMLIB_API size_t luamem_checklenarg (lua_State *L, int idx) * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +static int typeerror (lua_State *L, int arg, const char *tname) { + const char *msg; + const char *typearg; /* name for the type of the actual argument */ + if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING) + typearg = lua_tostring(L, -1); /* use the given type name */ + else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA) + typearg = "light userdata"; /* special name for messages */ + else + typearg = luaL_typename(L, arg); /* standard name */ + msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg); + return luaL_argerror(L, arg, msg); +} + /* ** {====================================================== ** Generic Buffer manipulation From eff77ef4797c7a95b7c8704436856718f9dc411b Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 18 Feb 2018 13:46:14 -0300 Subject: [PATCH 08/24] New operations 'memory.find' and 'memory.tostring'. --- doc/manual.md | 39 +++++++++++++++++++++---------- src/lmemmod.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++-- test/testall.lua | 31 ++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 15 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index 84b0da7..c3d4c61 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -9,9 +9,10 @@ Index - [`memory.get`](#memoryget-m-i-j-) - [`memory.set`](#memoryset-m-i) - [`memory.fill`](#memoryfill-m-s-i-j-o-) +- [`memory.find`](#memoryfind-m-s-i-j-o-) - [`memory.pack`](#memorypack-m-i-fmt-v-) - [`memory.unpack`](#memoryunpack-m-i-fmt) -- [`tostring`](#tostring-m) +- [`memory.tostring`](#memorytostring-m-i-j) - [`LUAMEM_ALLOC`](#luamem_newalloc) - [`LUAMEM_REF`](#luamem_newref) @@ -48,7 +49,6 @@ A memory can have a fixed size or be resizable. When indexing a memory, the first byte is at position 1 (not at 0, as in C). Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the memory. Thus, the last byte is at position -1, and so on. -In this manual we will refer to byte in position `i` as `m[i]`. This library provides all its functions inside the table `memory`. It also sets a metatable for the memory where the `__index` field points to the `memory` table. @@ -57,7 +57,7 @@ For instance, `memory.get(m,i)` can be written as `m:get(i)`, where `m` is a mem ### `memory.create ([s [, i [, j]]])` -Creates a new fixed-size memory of `s` bytes with value zero when `s` is a number. +If `s` is a number, creates a new fixed-size memory of `s` bytes with value zero. If `s` is a string or a memory, then the new memory will have the same size and contents of `s` from position `i` until position `j`; `i` and `j` can be negative. @@ -89,6 +89,19 @@ Returns the size of memory `m`. Returns the index of the first byte which values differ in `m1` and `m2`, or `nil` if both contain the same bytes. It also returns the result of a `m1 < m2` as if they were strings. +Both `m1` and `m2` shall be memory or string. + +### `memory.find (m, s [, i [, j [, o]]])` + +Searches in memory or string `m` from position `i` until `j` for the contents of the memory or string `s` from position `o` of `s`; +`i`, `j` and `o` can be negative. + +If, after the translation of negative indices, `o` is less than 1, it is corrected to 1. +After the translation of negative indices, `i` and `j` must refer to valid positions of `m`. + +If `i` is greater and `j` (empty range), or `o` refers to a position beyond the size of `s` (no contents), or the bytes from `s` are not found in `m` this function returns `nil`. +Otherwise, it return the position of the first byte found in `m`. + ### `memory.get (m [, i [, j]])` Returns the values of bytes in memory `m` from `i` until `j`; @@ -108,11 +121,10 @@ If there are more arguments than bytes in the range from `i` to the end of memor ### `memory.fill (m, s [, i [, j [, o]]])` -Sets the values of all bytes in memory `m` in the range from position `i` until `j` with the contents from position `o` of the string or memory `s`; +Sets the values of all bytes in memory `m` from position `i` until `j` with the contents of the memory or string `s` from position `o` of `s`; `i`, `j` and `o` can be negative. -If, after the translation of negative indices, `o` is less than 1, it is corrected to 1. -After the translation of negative indices, `i` and `j` must refer to valid positions of `m`. +These indices are corrected following the same rules of function [`memory.find`](#memoryfind-m-s-i-j-o-). If `i` is greater and `j` (empty range), or `o` refers to a position beyond the size of `s` (no contents) this function has no effect. Otherwise, the specified contents from `s` (from `o`) are copied repeatedly until they fill all bytes in the specified range of `m` (from `i` to `j`). @@ -120,6 +132,13 @@ Otherwise, the specified contents from `s` (from `o`) are copied repeatedly unti If `s` is a number then all bytes in the specified range of `m` are set with the value of `s`. The value of `o` is ignored in this case. +### `memory.tostring (m [, i [, j]])` + +Returns a string with the contents of memory or string `m` from `i` until `j`; +`i` and `j` can be negative. +The default value for `i` is 1; +the default value for `j` is `i`. + ### `memory.pack (m, i, fmt, v...)` Serializes in memory `m`, from position `i`, the values `v...` in binary form according to the format `fmt` (see the [Lua manual](http://www.lua.org/manual/5.3/manual.html#6.4.2)). @@ -130,10 +149,6 @@ Returns a boolean indicating whether all values were packed in memory `m`, follo Returns the values encoded in position `i` of memory or string `m`, according to the format `fmt`, as in function [memory.pack](#memorypack-m-i-fmt-v-). After the read values, this function also returns the index of the first unread byte in `m`. -### `tostring (m)` - -Returns a string with the contents of memory `m`. - C Library API ------------- @@ -226,7 +241,7 @@ if `len` is not `NULL` fills `*len` with the memory's length. `int luamem_isstring (lua_State *L, int idx);` -Returns 1 if the value at the given index is a string or memory, and 0 otherwise. +Returns 1 if the value at the given index is a memory or string, and 0 otherwise. ### `luamem_tostring` @@ -241,7 +256,7 @@ __Note__: Unlike Lua strings, memory areas are not followed by a null byte (`'\0 `const char *luamem_checkstring (lua_State *L, int arg, size_t *len);` -Checks whether the function argument `arg` is a string or memory and returns a pointer to its contents; +Checks whether the function argument `arg` is a memory or string and returns a pointer to its contents; if `len` is not `NULL` fills `*len` with the contents' length. This function might use `lua_tolstring` to get its result, so all conversions and caveats of that function apply here. diff --git a/src/lmemmod.c b/src/lmemmod.c index c4ae297..9f7d818 100644 --- a/src/lmemmod.c +++ b/src/lmemmod.c @@ -7,6 +7,8 @@ static lua_Integer posrelat (lua_Integer pos, size_t len); static int str2byte (lua_State *L, const char *s, size_t l); static void code2char (lua_State *L, int idx, char *p, int n); +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2); static int mem_create (lua_State *L) { if (lua_gettop(L) == 0) { @@ -82,8 +84,16 @@ static int mem_len (lua_State *L) { static int mem_tostring (lua_State *L) { size_t len; - const char *s = luamem_checkmemory(L, 1, &len); - if (len > 0) lua_pushlstring(L, s, len); + const char *s = luamem_checkstring(L, 1, &len); + lua_Integer posi = posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer pose = posrelat(luaL_optinteger(L, 3, -1), len); + int n; + if (posi < 1) posi = 1; + if (pose > (lua_Integer)len) pose = len; + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + if (n > 0) lua_pushlstring(L, s + posi -1, n); else lua_pushliteral(L, ""); return 1; } @@ -124,6 +134,30 @@ static int mem_set (lua_State *L) { return 0; } +static int mem_find (lua_State *L) { + size_t len, sl; + const char *p = luamem_checkstring(L, 1, &len); + const char *s = luamem_checkstring(L, 2, &sl); + lua_Integer i = posrelat(luaL_optinteger(L, 3, 1), len); + lua_Integer j = posrelat(luaL_optinteger(L, 4, -1), len); + lua_Integer os = posrelat(luaL_optinteger(L, 5, 1), sl); + if (os < 1) os = 1; + if (i <= j && os <= (lua_Integer)sl) { + int n = (int)(j - i + 1); + if (i + n <= j) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + --os; + sl -= os; + s = lmemfind(p + i - 1, (size_t)n, s + os, sl); + if (s) { + lua_pushinteger(L, (s - p) + 1); + lua_pushinteger(L, (s - p) + sl); + return 2; + } + } + return 0; +} + static int mem_fill (lua_State *L) { size_t len, sl; char *p = luamem_checkmemory(L, 1, &len); @@ -170,11 +204,13 @@ static const luaL_Reg lib[] = { {"resize", mem_resize}, {"len", mem_len}, {"diff", mem_diff}, + {"find", mem_find}, {"fill", mem_fill}, {"get", mem_get}, {"set", mem_set}, {"pack", mem_pack}, {"unpack", mem_unpack}, + {"tostring", mem_tostring}, {NULL, NULL} }; @@ -265,6 +301,26 @@ static void code2char (lua_State *L, int idx, char *p, int n) { } } +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ + else { + const char *init; /* to search for a '*s2' inside 's1' */ + l2--; /* 1st char will be checked by 'memchr' */ + l1 = l1-l2; /* 's2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct 'l1' and 's1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} /* ** {====================================================== diff --git a/test/testall.lua b/test/testall.lua index f4c047f..ccf4f3c 100644 --- a/test/testall.lua +++ b/test/testall.lua @@ -113,7 +113,7 @@ for kind, newmem in pairs{fixedsize=memory.create, resizable=newresizable} do check(8192) end - do print(kind, "memory.create(memory|string [, i [, j]])") + do print(kind, "memory.create(memory|string [, i [, j]]), memory.tostring(memory, [, i [, j]])") local function check(expected, data, ...) local b = memory.create(data, ...) assert(memory.diff(b, expected) == nil) @@ -121,6 +121,7 @@ for kind, newmem in pairs{fixedsize=memory.create, resizable=newresizable} do local b = memory.create(memory.create(data), ...) assert(memory.diff(b, expected) == nil) checkmodifiable(b, #expected) + assert(memory.tostring(memory.create(data), ...) == expected) end check("" , "") check("" , "", 1) @@ -143,6 +144,34 @@ for kind, newmem in pairs{fixedsize=memory.create, resizable=newresizable} do check("234" , "\000123456789",3,5) end + do print(kind, "memory:find(string [, i [, j [, o]]])") + for _, C1 in ipairs({tostring, memory.create}) do + for _, C2 in ipairs({tostring, memory.create}) do + local m = C1"1234567890123456789" + local e = C1"" + local s = C2"345" + assert(memory.find(m, s) == 3) + local a, b = memory.find(m, s) + assert(a == 3) + assert(b == 5, b) + assert(memory.find(m, s, 3) == 3) + assert(memory.find(m, s, 4) == 13) + assert(memory.find(m, C2"346", 4) == nil) + assert(memory.find(m, s, -9) == 13) + assert(memory.find(m, s, 5, 1) == nil) + assert(memory.find(m, C2"\0", 5) == nil) + assert(memory.find(m, s, 1, -1, 4) == nil) + assert(memory.find(m, s, 20, 30) == nil) + assert(memory.find(m, s, -30, -20) == nil) + assert(memory.find(e, s, 1, -1, 4) == nil) + assert(memory.find(e, C2"") == nil) + assert(memory.find(e, C2"", 1) == nil) + assert(memory.find(e, C2"", 2) == nil) + assert(memory.find(e, C2"aaa", 1) == nil) + end + end + end + do print(kind, "memory:fill(string [, i [, j]])") local data = string.rep(" ", 10) local full = "1234567890ABCDEF" From aed829fc4735197af72b7ff79e91bb3546a984d4 Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 18 Feb 2018 13:56:02 -0300 Subject: [PATCH 09/24] Format the index as a table. --- doc/manual.md | 50 ++++++++++++++------------------------------------ 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index c3d4c61..dddd721 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -1,42 +1,20 @@ Index ===== -- [`memory.create`](#memorycreate-s-i-j-) -- [`memory.resize`](#memoryresize-m-l) -- [`memory.type`](#memorytype-m) -- [`memory.len`](#memorylen-m) -- [`memory.diff`](#memorydiff-m1-m2) -- [`memory.get`](#memoryget-m-i-j-) -- [`memory.set`](#memoryset-m-i) -- [`memory.fill`](#memoryfill-m-s-i-j-o-) -- [`memory.find`](#memoryfind-m-s-i-j-o-) -- [`memory.pack`](#memorypack-m-i-fmt-v-) -- [`memory.unpack`](#memoryunpack-m-i-fmt) -- [`memory.tostring`](#memorytostring-m-i-j) - -- [`LUAMEM_ALLOC`](#luamem_newalloc) -- [`LUAMEM_REF`](#luamem_newref) -- [`LUAMEM_TALLOC`](#luamem_tomemoryx) -- [`LUAMEM_TNONE`](#luamem_tomemoryx) -- [`LUAMEM_TREF`](#luamem_tomemoryx) -- [`luamem_Unref`](#luamem_Unref) -- [`luamem_addvalue`](#luamem_addvalue) -- [`luamem_checklenarg`](#luamem_checklenarg) -- [`luamem_checkmemory`](#luamem_checkmemory) -- [`luamem_checkstring`](#luamem_checkstring) -- [`luamem_free`](#luamem_free) -- [`luamem_ismemory`](#luamem_ismemory) -- [`luamem_isref`](#luamem_isref) -- [`luamem_isstring`](#luamem_isstring) -- [`luamem_newalloc`](#luamem_newalloc) -- [`luamem_newref`](#luamem_newref) -- [`luamem_pushresbuf`](#luamem_pushresbuf) -- [`luamem_pushresbufsize`](#luamem_pushresbufsize) -- [`luamem_realloc`](#luamem_realloc) -- [`luamem_setref`](#luamem_setref) -- [`luamem_tomemory`](#luamem_tomemory) -- [`luamem_tomemoryx`](#luamem_tomemoryx) -- [`luamem_tostring`](#luamem_tostring) +[Lua functions](#writable-byte-sequences) | [C API](#c-library-api) | [C API](#c-library-api) +-------------------------------------------|---------------------------------------------|-------------------------------------------------- +[`memory.create`](#memorycreate-s-i-j-) | [`luamem_Unref`](#luamem_Unref) | [`luamem_pushresbufsize`](#luamem_pushresbufsize) +[`memory.resize`](#memoryresize-m-l) | [`luamem_addvalue`](#luamem_addvalue) | [`luamem_realloc`](#luamem_realloc) +[`memory.type`](#memorytype-m) | [`luamem_checklenarg`](#luamem_checklenarg) | [`luamem_setref`](#luamem_setref) +[`memory.len`](#memorylen-m) | [`luamem_checkmemory`](#luamem_checkmemory) | [`luamem_tomemory`](#luamem_tomemory) +[`memory.diff`](#memorydiff-m1-m2) | [`luamem_checkstring`](#luamem_checkstring) | [`luamem_tomemoryx`](#luamem_tomemoryx) +[`memory.get`](#memoryget-m-i-j-) | [`luamem_free`](#luamem_free) | [`luamem_tostring`](#luamem_tostring) +[`memory.set`](#memoryset-m-i) | [`luamem_ismemory`](#luamem_ismemory) | +[`memory.fill`](#memoryfill-m-s-i-j-o-) | [`luamem_isref`](#luamem_isref) | [`LUAMEM_ALLOC`](#luamem_newalloc) +[`memory.find`](#memoryfind-m-s-i-j-o-) | [`luamem_isstring`](#luamem_isstring) | [`LUAMEM_REF`](#luamem_newref) +[`memory.pack`](#memorypack-m-i-fmt-v-) | [`luamem_newalloc`](#luamem_newalloc) | [`LUAMEM_TALLOC`](#luamem_tomemoryx) +[`memory.unpack`](#memoryunpack-m-i-fmt) | [`luamem_newref`](#luamem_newref) | [`LUAMEM_TNONE`](#luamem_tomemoryx) +[`memory.tostring`](#memorytostring-m-i-j) | [`luamem_pushresbuf`](#luamem_pushresbuf) | [`LUAMEM_TREF`](#luamem_tomemoryx) Contents ======== From 71d5d2d2d8740e3a4d7eb901465d420d15bca714 Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 18 Feb 2018 17:00:43 -0300 Subject: [PATCH 10/24] Use C syntax hightlight on C API function signatures. --- doc/manual.md | 72 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index dddd721..226a6be 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -138,7 +138,9 @@ There are two distinct types of memory areas in the C API: ### `luamem_newalloc` -`char *luamem_newalloc (lua_State *L, size_t len);` +```C +char *luamem_newalloc (lua_State *L, size_t len); +``` Creates and pushes onto the stack a new allocated memory with the given size, and returns its block address. @@ -146,7 +148,9 @@ Allocated memory areas uses metatable created with name given by constant `LUAME ### `luamem_Unref` -`typedef void (*luamem_Unref) (lua_State *L, void *mem, size_t len);` +```C +typedef void (*luamem_Unref) (lua_State *L, void *mem, size_t len); +``` Type for memory unrefering functions. @@ -154,7 +158,9 @@ These functions are called whenever a referenced memory ceases to pointo to bloc ### `luamem_newref` -`void luamem_newref (lua_State *L);` +```C +void luamem_newref (lua_State *L); +``` Creates and pushes onto the stack a new reference memory pointing to NULL, with length zero, and no unrefering function (see [`luamem_Unref`](#luamem_Unref)). @@ -162,7 +168,9 @@ Referenced memory areas uses metatable created with name given by constant `LUAM ### `luamem_setref` -`int luamem_setref (lua_State *L, int idx, char *mem, size_t len, luamem_Unref unref);` +```C +int luamem_setref (lua_State *L, int idx, char *mem, size_t len, luamem_Unref unref); +``` Defines the block address (`mem`), size (`len`), and unrefering function (`unref`) of the referenced memory at index `idx`, and returns 1. If `idx` does not contain a referenced memory it returns 0; @@ -180,25 +188,33 @@ luamem_setref(L, idx, mem, len, NULL); /* only update `unref` to NULL */ ### `luamem_isref` -`int luamem_isref(lua_State *L, int idx);` +```C +int luamem_isref(lua_State *L, int idx); +``` Returns 1 if the value at the given index is a referenced memory, and 0 otherwise. ### `luamem_ismemory` -`int luamem_ismemory (lua_State *L, int idx);` +```C +int luamem_ismemory (lua_State *L, int idx); +``` Returns 1 if the value at the given index is a memory (allocated or referenced), and 0 otherwise. ### `luamem_tomemory` -`void *luamem_tomemory (lua_State *L, int idx, size_t *len);` +```C +void *luamem_tomemory (lua_State *L, int idx, size_t *len); +``` Equivalent to `luamem_tomemoryx(L, idx, len, NULL, NULL)`. ### `luamem_tomemoryx` -`char *luamem_tomemoryx (lua_State *L, int idx, size_t *len, luamem_Unref *unref, int *type);` +```C +char *luamem_tomemoryx (lua_State *L, int idx, size_t *len, luamem_Unref *unref, int *type); +``` Return the block address of memory at the given index, or `NULL` if the value is not a memory. @@ -210,20 +226,26 @@ Because Lua has garbage collection, there is no guarantee that the pointer retur ### `luamem_checkmemory` -`char *luamem_checkmemory (lua_State *L, int arg, size_t *len);` +```C +char *luamem_checkmemory (lua_State *L, int arg, size_t *len); +``` Checks whether the function argument `arg` is a memory (allocated or referenced) and returns a pointer to its contents; if `len` is not `NULL` fills `*len` with the memory's length. ### `luamem_isstring` -`int luamem_isstring (lua_State *L, int idx);` +```C +int luamem_isstring (lua_State *L, int idx); +``` Returns 1 if the value at the given index is a memory or string, and 0 otherwise. ### `luamem_tostring` -`const char *luamem_tostring (lua_State *L, int idx, size_t *len);` +```C +const char *luamem_tostring (lua_State *L, int idx, size_t *len); +``` If the value at the given index is a memory it behaves like [`luamem_tomemory`](#luamem_tomemory), but retuning a pointer to constant bytes. Otherwise, it is equivalent to [`lua_tolstring`](http://www.lua.org/manual/5.3/manual.html#lua_tolstring). @@ -232,7 +254,9 @@ __Note__: Unlike Lua strings, memory areas are not followed by a null byte (`'\0 ### `luamem_checkstring` -`const char *luamem_checkstring (lua_State *L, int arg, size_t *len);` +```C +const char *luamem_checkstring (lua_State *L, int arg, size_t *len); +``` Checks whether the function argument `arg` is a memory or string and returns a pointer to its contents; if `len` is not `NULL` fills `*len` with the contents' length. @@ -241,20 +265,26 @@ This function might use `lua_tolstring` to get its result, so all conversions an ### `luamem_checklenarg` -`size_t luamem_checklenarg (lua_State *L, int arg);` +```C +size_t luamem_checklenarg (lua_State *L, int arg); +``` Checks whether the function argument `arg` is an integer (or can be converted to an integer) of a valid memory size and returns this integer cast to a `size_t`. ### `luamem_realloc` -`void *luamem_realloc (lua_State *L, void *mem, size_t old, size_t new);` +```C +void *luamem_realloc (lua_State *L, void *mem, size_t old, size_t new); +``` Reallocates memory pointed by `mem` of size `old` with new size `new` using the allocation function registered by the Lua state (see [`lua_getallocf`](http://www.lua.org/manual/5.3/manual.html#lua_getallocf)). Returns the reallocated memory. ### `luamem_free` -`void luamem_free (lua_State *L, void *mem, size_t sz);` +```C +void luamem_free (lua_State *L, void *mem, size_t sz); +``` Equivalent to `luamem_realloc(L, mem, sz, 0)`. @@ -262,18 +292,24 @@ __Note__: Any referenced memory which uses this function as the unrefering funct ### `luamem_addvalue` -`void luamem_addvalue (luaL_Buffer *B);` +```C +void luamem_addvalue (luaL_Buffer *B); +``` Similar to [`luaL_addvalue`](http://www.lua.org/manual/5.3/manual.html#luaL_addvalue), but if the value on top of the stack is a memory, it adds its contents to the buffer without converting it to a Lua string. ### `luamem_pushresult` -`void luamem_pushresult (luaL_Buffer *B);` +```C +void luamem_pushresult (luaL_Buffer *B); +``` Similar to [`luamem_pushresult`](http://www.lua.org/manual/5.3/manual.html#luaL_pushresult), but leaves a memory with the buffer contents on the top of the stack instead of a string. ### `luamem_pushresultfsize` -`void luamem_pushresbufsize (luaL_Buffer *B, size_t sz);` +```C +void luamem_pushresbufsize (luaL_Buffer *B, size_t sz); +``` Equivalent to the sequence [`luaL_addsize`](http://www.lua.org/manual/5.3/manual.html#luaL_addsize), [`luamem_pushresult`](#luamem_pushresult). From 065df8b64c7211b36c7af6e8f5675dfbf9b3bb2a Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 18 Feb 2018 17:08:36 -0300 Subject: [PATCH 11/24] Fix index links. --- doc/manual.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index 226a6be..1ef396c 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -1,20 +1,20 @@ Index ===== -[Lua functions](#writable-byte-sequences) | [C API](#c-library-api) | [C API](#c-library-api) --------------------------------------------|---------------------------------------------|-------------------------------------------------- -[`memory.create`](#memorycreate-s-i-j-) | [`luamem_Unref`](#luamem_Unref) | [`luamem_pushresbufsize`](#luamem_pushresbufsize) -[`memory.resize`](#memoryresize-m-l) | [`luamem_addvalue`](#luamem_addvalue) | [`luamem_realloc`](#luamem_realloc) -[`memory.type`](#memorytype-m) | [`luamem_checklenarg`](#luamem_checklenarg) | [`luamem_setref`](#luamem_setref) -[`memory.len`](#memorylen-m) | [`luamem_checkmemory`](#luamem_checkmemory) | [`luamem_tomemory`](#luamem_tomemory) -[`memory.diff`](#memorydiff-m1-m2) | [`luamem_checkstring`](#luamem_checkstring) | [`luamem_tomemoryx`](#luamem_tomemoryx) -[`memory.get`](#memoryget-m-i-j-) | [`luamem_free`](#luamem_free) | [`luamem_tostring`](#luamem_tostring) -[`memory.set`](#memoryset-m-i) | [`luamem_ismemory`](#luamem_ismemory) | -[`memory.fill`](#memoryfill-m-s-i-j-o-) | [`luamem_isref`](#luamem_isref) | [`LUAMEM_ALLOC`](#luamem_newalloc) -[`memory.find`](#memoryfind-m-s-i-j-o-) | [`luamem_isstring`](#luamem_isstring) | [`LUAMEM_REF`](#luamem_newref) -[`memory.pack`](#memorypack-m-i-fmt-v-) | [`luamem_newalloc`](#luamem_newalloc) | [`LUAMEM_TALLOC`](#luamem_tomemoryx) -[`memory.unpack`](#memoryunpack-m-i-fmt) | [`luamem_newref`](#luamem_newref) | [`LUAMEM_TNONE`](#luamem_tomemoryx) -[`memory.tostring`](#memorytostring-m-i-j) | [`luamem_pushresbuf`](#luamem_pushresbuf) | [`LUAMEM_TREF`](#luamem_tomemoryx) +[Lua functions](#writable-byte-sequences) | [C API](#c-library-api) | [C API](#c-library-api) +---------------------------------------------|---------------------------------------------|---------------------------------------------------- +[`memory.create`](#memorycreate-s--i--j) | [`luamem_Unref`](#luamem_unref) | [`luamem_pushresultfsize`](#luamem_pushresultfsize) +[`memory.resize`](#memoryresize-m-l) | [`luamem_addvalue`](#luamem_addvalue) | [`luamem_realloc`](#luamem_realloc) +[`memory.type`](#memorytype-m) | [`luamem_checklenarg`](#luamem_checklenarg) | [`luamem_setref`](#luamem_setref) +[`memory.len`](#memorylen-m) | [`luamem_checkmemory`](#luamem_checkmemory) | [`luamem_tomemory`](#luamem_tomemory) +[`memory.diff`](#memorydiff-m1-m2) | [`luamem_checkstring`](#luamem_checkstring) | [`luamem_tomemoryx`](#luamem_tomemoryx) +[`memory.get`](#memoryget-m--i--j) | [`luamem_free`](#luamem_free) | [`luamem_tostring`](#luamem_tostring) +[`memory.set`](#memoryset-m-i-) | [`luamem_ismemory`](#luamem_ismemory) | +[`memory.fill`](#memoryfill-m-s--i--j--o) | [`luamem_isref`](#luamem_isref) | [`LUAMEM_ALLOC`](#luamem_newalloc) +[`memory.find`](#memoryfind-m-s--i--j--o) | [`luamem_isstring`](#luamem_isstring) | [`LUAMEM_REF`](#luamem_newref) +[`memory.pack`](#memorypack-m-i-fmt-v) | [`luamem_newalloc`](#luamem_newalloc) | [`LUAMEM_TALLOC`](#luamem_tomemoryx) +[`memory.unpack`](#memoryunpack-m-i-fmt) | [`luamem_newref`](#luamem_newref) | [`LUAMEM_TNONE`](#luamem_tomemoryx) +[`memory.tostring`](#memorytostring-m--i--j) | [`luamem_pushresult`](#luamem_pushresult) | [`LUAMEM_TREF`](#luamem_tomemoryx) Contents ======== From 8480c7a3d1efcbcb905c0aa6a25d5dabbb7f2521 Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 18 Feb 2018 17:18:44 -0300 Subject: [PATCH 12/24] Changing the position of 'i' argument in 'memory.pack' and 'memory.unpack'. --- doc/manual.md | 11 ++-- src/lmemmod.c | 12 ++--- test/tpack.lua | 140 ++++++++++++++++++++++++------------------------- 3 files changed, 81 insertions(+), 82 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index 1ef396c..4459623 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -12,8 +12,8 @@ Index [`memory.set`](#memoryset-m-i-) | [`luamem_ismemory`](#luamem_ismemory) | [`memory.fill`](#memoryfill-m-s--i--j--o) | [`luamem_isref`](#luamem_isref) | [`LUAMEM_ALLOC`](#luamem_newalloc) [`memory.find`](#memoryfind-m-s--i--j--o) | [`luamem_isstring`](#luamem_isstring) | [`LUAMEM_REF`](#luamem_newref) -[`memory.pack`](#memorypack-m-i-fmt-v) | [`luamem_newalloc`](#luamem_newalloc) | [`LUAMEM_TALLOC`](#luamem_tomemoryx) -[`memory.unpack`](#memoryunpack-m-i-fmt) | [`luamem_newref`](#luamem_newref) | [`LUAMEM_TNONE`](#luamem_tomemoryx) +[`memory.pack`](#memorypack-m-fmt-i-v) | [`luamem_newalloc`](#luamem_newalloc) | [`LUAMEM_TALLOC`](#luamem_tomemoryx) +[`memory.unpack`](#memoryunpack-m-fmt--i) | [`luamem_newref`](#luamem_newref) | [`LUAMEM_TNONE`](#luamem_tomemoryx) [`memory.tostring`](#memorytostring-m--i--j) | [`luamem_pushresult`](#luamem_pushresult) | [`LUAMEM_TREF`](#luamem_tomemoryx) Contents @@ -117,14 +117,15 @@ Returns a string with the contents of memory or string `m` from `i` until `j`; The default value for `i` is 1; the default value for `j` is `i`. -### `memory.pack (m, i, fmt, v...)` +### `memory.pack (m, fmt, i, v...)` Serializes in memory `m`, from position `i`, the values `v...` in binary form according to the format `fmt` (see the [Lua manual](http://www.lua.org/manual/5.3/manual.html#6.4.2)). Returns a boolean indicating whether all values were packed in memory `m`, followed by the index of the first unwritten byte in `m` and all the values `v...` that were not packed. -### `memory.unpack (m, i, fmt)` +### `memory.unpack (m, fmt [, i])` -Returns the values encoded in position `i` of memory or string `m`, according to the format `fmt`, as in function [memory.pack](#memorypack-m-i-fmt-v-). +Returns the values encoded in position `i` of memory or string `m`, according to the format `fmt`, as in function [memory.pack](#memorypack-m-i-fmt-v-); +The default value for `i` is 1. After the read values, this function also returns the index of the first unread byte in `m`. C Library API diff --git a/src/lmemmod.c b/src/lmemmod.c index 9f7d818..f7e29b0 100644 --- a/src/lmemmod.c +++ b/src/lmemmod.c @@ -598,10 +598,10 @@ static int mem_pack (lua_State *L) { Header h; size_t lb; char *mem = luamem_checkmemory(L, 1, &lb); - size_t i = (size_t)posrelat(luaL_checkinteger(L, 2), lb) - 1; - const char *fmt = luaL_checkstring(L, 3); /* format string */ + const char *fmt = luaL_checkstring(L, 2); /* format string */ + size_t i = (size_t)posrelat(luaL_checkinteger(L, 3), lb) - 1; int arg = 3; /* current argument to pack */ - luaL_argcheck(L, 0 <= i && i <= (lua_Integer)lb-1, 2, "index out of bounds"); + luaL_argcheck(L, 0 <= i && i <= (lua_Integer)lb-1, 3, "index out of bounds"); initheader(L, &h); while (*fmt != '\0') { int size, ntoalign; @@ -723,10 +723,10 @@ static int mem_unpack (lua_State *L) { Header h; size_t ld; const char *data = luamem_checkmemory(L, 1, &ld); - size_t pos = (size_t)posrelat(luaL_checkinteger(L, 2), ld) - 1; - const char *fmt = luaL_checkstring(L, 3); + const char *fmt = luaL_checkstring(L, 2); + size_t pos = (size_t)posrelat(luaL_optinteger(L, 3, 1), ld) - 1; int n = 0; /* number of results */ - luaL_argcheck(L, pos <= ld, 2, "initial position out of bounds"); + luaL_argcheck(L, pos <= ld, 3, "initial position out of bounds"); initheader(L, &h); while (*fmt != '\0') { int size, ntoalign; diff --git a/test/tpack.lua b/test/tpack.lua index 94e136a..b22f01c 100644 --- a/test/tpack.lua +++ b/test/tpack.lua @@ -14,7 +14,7 @@ do end local m = memory.create(0) - failpack(1, {123,456,789}, pack(m, 1, "b", 123,456,789)) + failpack(1, {123,456,789}, pack(m, "b", 1, 123,456,789)) local m = memory.create(10) memory.fill(m, 0x55) @@ -28,7 +28,7 @@ do 0x33333333, 0x44444444, } - failpack(9, expectvals, pack(m, 1, "i4i4i4i4", table.unpack(values))) + failpack(9, expectvals, pack(m, "i4i4i4i4", 1, table.unpack(values))) end --[[ @@ -107,13 +107,13 @@ local function assertunpack(sz, vals, ...) return ... end --- minimum behavior for integer formats +print("minimum behavior for integer formats") do local function testpack(fmt, val) local sz = packsize(fmt) local b = memory.create(sz) - assertpack(sz, 1, pack(b, 1, fmt, val)) - assertunpack(sz, {val}, unpack(b, 1, fmt)) + assertpack(sz, 1, pack(b, fmt, 1, val)) + assertunpack(sz, {val}, unpack(b, fmt)) end testpack("B", 0xff) testpack("b", 0x7f) @@ -130,52 +130,52 @@ for i = 1, NB do -- small numbers with signal extension ("\xFF...") local s = string.rep("\xff", i) local b = memory.create(i) - assertpack(i, 1, pack(b, 1, "i" .. i, -1)) + assertpack(i, 1, pack(b, "i" .. i, 1, -1)) assert(tostring(b) == s) - assertunpack(i, {-1}, unpack(b, 1, "i" .. i)) + assertunpack(i, {-1}, unpack(b, "i" .. i)) -- small unsigned number ("\0...\xAA") s = "\xAA" .. string.rep("\0", i - 1) - assertpack(i, 1, pack(b, 1, "I" .. i, 0xAA)) + assertunpack(i, {0xAA}, unpack(b, "I" .. i, 1, 0xAA)) assert(tostring(b) == s:reverse()) - assertunpack(i, {0xAA}, unpack(b, 1, ">I" .. i)) + assertunpack(i, {0xAA}, unpack(b, ">I" .. i)) end do local b = memory.create(sizeLI+1) local lnum = 0x13121110090807060504030201 - assertpack(sizeLI, 1, pack(b, 1, "i" .. i)) + assertunpack(i, {-lnum}, unpack(b, ">i" .. i)) -- overflows memory.fill(b, 0, 1, i-1) memory.set(b, i, 1) - checkerror("does not fit", unpack, b, 1, "i" .. i) + checkerror("does not fit", unpack, b, ">i" .. i) end end @@ -185,14 +185,14 @@ for i = 1, sizeLI do local n = lnum & (~(-1 << (i * 8))) local s = string.sub(lstr, 1, i) local b = memory.create(i) - assertpack(i, 1, pack(b, 1, "i" .. i, n)) + assertpack(i, 1, pack(b, ">i" .. i, 1, n)) assert(tostring(b) == s:reverse()) - assertunpack(i, {n}, unpack(b, 1, ">i" .. i)) + assertunpack(i, {n}, unpack(b, ">i" .. i)) end --- sign extension +print("sign extension") do local u = 0xf0 for i = 1, sizeLI - 1 do @@ -201,89 +201,87 @@ do if i>=2 then memory.fill(b, 0xff, 2, i) end - assertunpack(i, {-16}, unpack(b, 1, "I"..i)) + assertunpack(i, {-16}, unpack(b, "I"..i)) u = u * 256 + 0xff end end --- mixed endianness +print("mixed endianness") do local b = memory.create(4) - assertpack(4, 2, pack(b, 1, ">i2 i2 i2")) - assertpack(4, 1, pack(b, 1, "=i4", 2001)) + assertunpack(4, {10, 20}, unpack(b, "i2")) + assertpack(4, 1, pack(b, "=i4", 1, 2001)) local s = tostring(b) - assertpack(4, 1, pack(b, 1, "i4", 2001)) + assertpack(4, 1, pack(b, "i4", 1, 2001)) assert(tostring(b) == s) end -print("testing invalid formats") - +print("invalid formats") do local b = memory.create(math.max(16, NB+1)) - checkerror("out of limits", pack, b, 1, "i0", 0) - checkerror("out of limits", pack, b, 1, "i" .. NB + 1, 0) - checkerror("out of limits", pack, b, 1, "!" .. NB + 1, 0) - checkerror("%(17%) out of limits %[1,16%]", pack, b, 1, "Xi" .. NB + 1) - checkerror("invalid format option 'r'", pack, b, 1, "i3r", 0) + checkerror("out of limits", pack, b, "i0", 1, 0) + checkerror("out of limits", pack, b, "i" .. NB + 1, 1, 0) + checkerror("out of limits", pack, b, "!" .. NB + 1, 1, 0) + checkerror("%(17%) out of limits %[1,16%]", pack, b, "Xi" .. NB + 1, 1) + checkerror("invalid format option 'r'", pack, b, "i3r", 1, 0) memory.fill(b, 16, 1, 16) - checkerror("16%-byte integer", unpack, b, 1, "i16") - checkerror("not power of 2", pack, b, 1, "!4i3", 0); - checkerror("missing size", pack, b, 1, "c", "") + checkerror("16%-byte integer", unpack, b, "i16", 1) + checkerror("not power of 2", pack, b, "!4i3", 1, 0); + checkerror("missing size", pack, b, "c", 1, "") end --- overflow in packing +print("overflow in packing") for i = 1, sizeLI - 1 do local b = memory.create(i) local umax = (1 << (i * 8)) - 1 local max = umax >> 1 local min = ~max - checkerror("overflow", pack, b, 1, "I" .. i, umax + 1) - - checkerror("overflow", pack, b, 1, ">i" .. i, umax) - checkerror("overflow", pack, b, 1, ">i" .. i, max + 1) - checkerror("overflow", pack, b, 1, "i" .. i, max)) - assertunpack(i, {max}, unpack(b, 1, ">i" .. i)) - assertpack(i, 1, pack(b, 1, "I" .. i, umax)) - assertunpack(i, {umax}, unpack(b, 1, ">I" .. i)) + checkerror("overflow", pack, b, "I" .. i, 1, umax + 1) + + checkerror("overflow", pack, b, ">i" .. i, 1, umax) + checkerror("overflow", pack, b, ">i" .. i, 1, max + 1) + checkerror("overflow", pack, b, "i" .. i, 1, max)) + assertunpack(i, {max}, unpack(b, ">i" .. i)) + assertpack(i, 1, pack(b, "I" .. i, 1, umax)) + assertunpack(i, {umax}, unpack(b, ">I" .. i)) end --- Lua integer size +print("Lua integer size") do local b = memory.create(sizeLI) - assertpack(sizeLI, 1, pack(b, 1, ">j", math.maxinteger)) - assertunpack(sizeLI, {math.maxinteger}, unpack(b, 1, ">j")) - assertpack(sizeLI, 1, pack(b, 1, "j", 1, math.maxinteger)) + assertunpack(sizeLI, {math.maxinteger}, unpack(b, ">j")) + assertpack(sizeLI, 1, pack(b, "f", 24) + pack(b2, ">f", 1, 24) end assert(tostring(b1) == tostring(b2)) end -do return end +do return print("OK") end print "testing pack/unpack of floating-point numbers" - for _, n in ipairs{0, -1.1, 1.9, 1/0, -1/0, 1e20, -1e20, 0.1, 2000.7} do assert(unpack("n", pack("n", n)) == n) assert(unpack(" Date: Mon, 4 Jun 2018 09:59:14 -0300 Subject: [PATCH 13/24] Fix to honor parameter . --- src/lmemmod.c | 1 + test/tpack.lua | 28 +++++++++++++++------------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/lmemmod.c b/src/lmemmod.c index f7e29b0..25dd58e 100644 --- a/src/lmemmod.c +++ b/src/lmemmod.c @@ -603,6 +603,7 @@ static int mem_pack (lua_State *L) { int arg = 3; /* current argument to pack */ luaL_argcheck(L, 0 <= i && i <= (lua_Integer)lb-1, 3, "index out of bounds"); initheader(L, &h); + mem += i; while (*fmt != '\0') { int size, ntoalign; KOption opt = getdetails(&h, i, &fmt, &size, &ntoalign); diff --git a/test/tpack.lua b/test/tpack.lua index b22f01c..d070a3a 100644 --- a/test/tpack.lua +++ b/test/tpack.lua @@ -109,21 +109,23 @@ end print("minimum behavior for integer formats") do - local function testpack(fmt, val) - local sz = packsize(fmt) + local function testpack(fmt, val, pos) + local sz = packsize(fmt)+pos-1 local b = memory.create(sz) - assertpack(sz, 1, pack(b, fmt, 1, val)) - assertunpack(sz, {val}, unpack(b, fmt)) + assertpack(sz, 1, pack(b, fmt, pos, val)) + assertunpack(sz, {val}, unpack(b, fmt, pos)) + end + for i=1, 10 do + testpack("B", 0xff, i) + testpack("b", 0x7f, i) + testpack("b", -0x80, i) + testpack("H", 0xffff, i) + testpack("h", 0x7fff, i) + testpack("h", -0x8000, i) + testpack("L", 0xffffffff, i) + testpack("l", 0x7fffffff, i) + testpack("l", -0x80000000, i) end - testpack("B", 0xff) - testpack("b", 0x7f) - testpack("b", -0x80) - testpack("H", 0xffff) - testpack("h", 0x7fff) - testpack("h", -0x8000) - testpack("L", 0xffffffff) - testpack("l", 0x7fffffff) - testpack("l", -0x80000000) end for i = 1, NB do From 671a9adc7005f068a28d1801c6490b0b1cb553a0 Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Mon, 4 Jun 2018 10:00:54 -0300 Subject: [PATCH 14/24] First draft of a layout support like LuaData. --- lua/memory/demo.lua | 86 +++++++++++++++++++++++ lua/memory/layout.lua | 154 ++++++++++++++++++++++++++++++++++++++++++ lua/memory/test.lua | 53 +++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 lua/memory/demo.lua create mode 100644 lua/memory/layout.lua create mode 100644 lua/memory/test.lua diff --git a/lua/memory/demo.lua b/lua/memory/demo.lua new file mode 100644 index 0000000..dc8d884 --- /dev/null +++ b/lua/memory/demo.lua @@ -0,0 +1,86 @@ +local IPAddr = { + [4] = layout.create{ + { key = "route", value = "IPv4" }, + { key = "iface", bits = 32 }, + }, + [6] = layout.create{ + { key = "route", bits = 64 }, + { key = "iface", bits = 64 }, + }, +} + +local IPHeader = { + [4] = layout.create{ + { key = "version" , bits = 4 }, + { key = "ihl" , bits = 4 }, + { key = "tos" , bits = 6 }, + { key = "ecn" , bits = 2 }, + { key = "tot_len" , bits = 16 }, + { key = "id" , bits = 16 }, + { key = "flags" , bits = 3 }, + { key = "frag_off", bits = 13 }, + { key = "ttl" , bits = 8 }, + { key = "protocol", bits = 8 }, + { key = "check" , bits = 16 }, + { key = "src" , layout = IPAddr[4] }, + { key = "dst" , layout = IPAddr[4] }, + }, + [6] = layout.create{ + { key = "version" , bits = 4 }, + { key = "tc" , bits = 8 }, + { key = "fl" , bits = 20 }, + { key = "tot_len" , bits = 16 }, + { key = "nh" , bits = 8 }, + { key = "hl" , bits = 8 }, + { key = "src" , layout = IPAddr[6] }, + { key = "dst" , layout = IPAddr[6] }, + }, +} + +local uint2 = standard.uint(2) +local uint3 = standard.uint(3) +local uint4 = standard.uint(4) +local uint6 = standard.uint(6) +local uint8 = standard.uint8 +local uint13 = standard.uint(13) +local uint16 = standard.uint16 +local uint20 = standard.uint(20) +local uint64 = standard.uint64 +local ipv4addr = standard.array{ length = 4, mapping = uint8 } +local ipv6addr = standard.tuple{ + { key = "routing" , mapping = uint64 }, + { key = "interface", mapping = uint64 }, +} +local IPHeader = standard.select{ + selector = { key = "version", mapping = uint4 }, + cases = { + [4] = standard.tuple{ + { key = "ihl" , mapping = uint4 }, + { key = "tos" , mapping = uint6 }, + { key = "ecn" , mapping = uint2 }, + { key = "tot_len" , mapping = uint16 }, + { key = "id" , mapping = uint16 }, + { key = "flags" , mapping = uint3 }, + { key = "frag_off", mapping = uint13 }, + { key = "ttl" , mapping = uint8 }, + { key = "protocol", mapping = uint8 }, + { key = "check" , mapping = uint16 }, + { key = "src" , mapping = ipv4addr }, + { key = "dst" , mapping = ipv4addr }, + }, + [6] = standard.tuple{ + { key = "tc" , mapping = uint8 }, + { key = "fl" , mapping = uint20 }, + { key = "tot_len", mapping = uint16 }, + { key = "nh" , mapping = uint8 }, + { key = "hl" , mapping = uint8 }, + { key = "src" , mapping = ipv6addr }, + { key = "dst" , mapping = ipv6addr }, + }, + }, +} + +local IPv6ExtHeader = standard.tuple{ + { key = "nh" , mapping = uint8 }, + { key = "hdr", mapping = standard.embedded8 }, +} diff --git a/lua/memory/layout.lua b/lua/memory/layout.lua new file mode 100644 index 0000000..a0e7a4e --- /dev/null +++ b/lua/memory/layout.lua @@ -0,0 +1,154 @@ +local _G = require "_G" +local assert = _G.assert +local ipairs = _G.ipairs +local rawset = _G.rawset +local setmetatable = _G.setmetatable + +local memory = require "memory" +local newmem = memory.create +local packmem = memory.pack +local unpackmem = memory.unpack + +local Pointer = {} + +function Pointer:__index(key) + local field = self.struct[key] + if field ~= nil then + local value = field.value + if value == nil then + local format = field.format + if format ~= nil then + value = unpackmem(self.buffer, field.format, field.pos) + local shift, mask = field.shift, field.mask + if shift > 0 then + value = value>>shift + end + if mask ~= ~0 then + value = value&mask + end + else + -- TODO + end + end + return value + end +end + +function Pointer:__newindex(key, value) + local field = self.struct[key] + if field ~= nil then + if field.value ~= nil then + assert(field.value == value, "invalid value") + else + local format = field.format + if format ~= nil then + local current = unpackmem(self.buffer, field.format, field.pos) + local shift, mask = field.shift, field.mask + assert(value <= mask, "value is too large") + +io.write(key," = ",string.format("%02x", current)," -> ") + + current = (current&~(mask< 0 then -- byte align + byteidx = byteidx+1 + bitoff = 0 + end + bits = bytes*8 + end + end + + local bitused = bitoff+bits + local bitpart = bitused%8 + local bytes = bitused//8 + + field = { + pos = byteidx, + bitoff = bitoff, + bits = bits, + mask = (~0>>(LuaIntBits-bits)), + shift = bitoff, + value = field.value, + } + + byteidx = byteidx+bytes + if bitpart > 0 then + bytes = bytes+1 + bitoff = bitpart + end + + field.bytes = bytes + field.format = "I"..bytes + + return field, byteidx, bitoff +end + +function layoutstruct(fields, byteidx, bitoff) -- local defined above + local struct = { + pos = byteidx, + bitoff = bitoff, + bits = 0, + bytes = 0, + } + for _, field in ipairs(fields) do + local key = field.key + field, byteidx, bitoff = layoutfield(field, byteidx, bitoff) + + struct.bytes = field.pos-1+field.bytes + struct.bits = 8*(byteidx-1)+bitoff + + if key ~= nil then + assert(field.bits <= LuaIntBits, "value is too big") + assert(field.bytes <= 8) + struct[key] = field + end + end + return struct, byteidx, bitoff +end + + +local module = {} + +function module.newstruct(fields) + return layoutstruct(fields, 1, 0) +end + +local empty = newmem(0) +function module.newpointer(struct) + return setmetatable({ + struct = struct, + buffer = empty, + }, Pointer) +end + +function module.setpointer(pointer, buffer, pos) + rawset(pointer, "buffer", buffer) + rawset(pointer, "pos", pos) +end + +return module diff --git a/lua/memory/test.lua b/lua/memory/test.lua new file mode 100644 index 0000000..43332d7 --- /dev/null +++ b/lua/memory/test.lua @@ -0,0 +1,53 @@ +local memory = require "memory" +local layout = require "memory.layout" + +local s = layout.newstruct{ + { key = "onebit", bits = 1 }, + { key = "twobits", bits = 2 }, + { key = "threebits", bits = 3 }, + { key = "fourbits", bits = 4 }, + { key = "eightbits", bits = 8 }, + { key = "onebyte", bytes = 1 }, +} +local p = layout.newpointer(s) + +local m = memory.create(s.bytes) +memory.fill(m, 0x55) +layout.setpointer(p, m) + +require("loop.debug.Viewer"):write(s); print() + +assert(p.onebit == 1, p.onebit) -- 1 +assert(p.twobits == 2, p.twobits) -- 10 +assert(p.threebits == 2, p.threebits) -- 010 +assert(p.fourbits == 5, p.fourbits) -- 0101 +assert(p.eightbits == 0x55, p.eightbits) -- 01010101 +assert(p.onebyte == 0x55, p.onebyte) -- 01010101 + +p.onebit = 0 +p.twobits = 3 +p.threebits = 0 +p.fourbits = 0xF +p.eightbits = 0 +p.onebyte = 0xFF + +for i = 1, #m do + io.write(string.format("%02x", memory.get(m, i))) +end +print() + +assert(p.onebit == 0) +assert(p.twobits == 3) +assert(p.threebits == 0) +assert(p.fourbits == 0xF) +assert(p.eightbits == 0) +assert(p.onebyte == 0xFF) + +assert(tostring(m) == string.char(0xC6, 0x03, 0x54, 0xFF)) +-- 0101 0100 0000 0011 1100 0110 + +--[[ +c60354ff +47d401ff + +--]] \ No newline at end of file From ecb4c0313498b0d100862e3f83202dcee5e9de3d Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 1 Jul 2018 17:13:51 -0300 Subject: [PATCH 15/24] fixup! First draft of a layout support like LuaData. --- lua/memory/layout.lua | 33 ++++--- lua/memory/test.lua | 53 ----------- test/testlayout.lua | 199 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 65 deletions(-) delete mode 100644 lua/memory/test.lua create mode 100644 test/testlayout.lua diff --git a/lua/memory/layout.lua b/lua/memory/layout.lua index a0e7a4e..9d2ffdf 100644 --- a/lua/memory/layout.lua +++ b/lua/memory/layout.lua @@ -12,7 +12,7 @@ local unpackmem = memory.unpack local Pointer = {} function Pointer:__index(key) - local field = self.struct[key] + local field = self.struct.fields[key] if field ~= nil then local value = field.value if value == nil then @@ -35,7 +35,7 @@ function Pointer:__index(key) end function Pointer:__newindex(key, value) - local field = self.struct[key] + local field = self.struct.fields[key] if field ~= nil then if field.value ~= nil then assert(field.value == value, "invalid value") @@ -45,13 +45,7 @@ function Pointer:__newindex(key, value) local current = unpackmem(self.buffer, field.format, field.pos) local shift, mask = field.shift, field.mask assert(value <= mask, "value is too large") - -io.write(key," = ",string.format("%02x", current)," -> ") - current = (current&~(mask< 0 or bitpart > 0 then + assert(endian == nil or endian == "little", "bigendian bits not supported") + endian = "<" + elseif endian == "little" then + endian = "<" + elseif endian == "big" then + endian = ">" + else + assert(endian == nil or endian == "native", "illegal endianess") + endian = "=" + end + field = { pos = byteidx, bitoff = bitoff, @@ -103,19 +110,20 @@ local function layoutfield(field, byteidx, bitoff) end field.bytes = bytes - field.format = "I"..bytes + field.format = endian.."I"..bytes return field, byteidx, bitoff end -function layoutstruct(fields, byteidx, bitoff) -- local defined above +function layoutstruct(spec, byteidx, bitoff) -- local defined above local struct = { pos = byteidx, bitoff = bitoff, bits = 0, bytes = 0, } - for _, field in ipairs(fields) do + local fields = {} + for _, field in ipairs(spec) do local key = field.key field, byteidx, bitoff = layoutfield(field, byteidx, bitoff) @@ -125,9 +133,10 @@ function layoutstruct(fields, byteidx, bitoff) -- local defined above if key ~= nil then assert(field.bits <= LuaIntBits, "value is too big") assert(field.bytes <= 8) - struct[key] = field + fields[key] = field end end + struct.fields = fields return struct, byteidx, bitoff end diff --git a/lua/memory/test.lua b/lua/memory/test.lua deleted file mode 100644 index 43332d7..0000000 --- a/lua/memory/test.lua +++ /dev/null @@ -1,53 +0,0 @@ -local memory = require "memory" -local layout = require "memory.layout" - -local s = layout.newstruct{ - { key = "onebit", bits = 1 }, - { key = "twobits", bits = 2 }, - { key = "threebits", bits = 3 }, - { key = "fourbits", bits = 4 }, - { key = "eightbits", bits = 8 }, - { key = "onebyte", bytes = 1 }, -} -local p = layout.newpointer(s) - -local m = memory.create(s.bytes) -memory.fill(m, 0x55) -layout.setpointer(p, m) - -require("loop.debug.Viewer"):write(s); print() - -assert(p.onebit == 1, p.onebit) -- 1 -assert(p.twobits == 2, p.twobits) -- 10 -assert(p.threebits == 2, p.threebits) -- 010 -assert(p.fourbits == 5, p.fourbits) -- 0101 -assert(p.eightbits == 0x55, p.eightbits) -- 01010101 -assert(p.onebyte == 0x55, p.onebyte) -- 01010101 - -p.onebit = 0 -p.twobits = 3 -p.threebits = 0 -p.fourbits = 0xF -p.eightbits = 0 -p.onebyte = 0xFF - -for i = 1, #m do - io.write(string.format("%02x", memory.get(m, i))) -end -print() - -assert(p.onebit == 0) -assert(p.twobits == 3) -assert(p.threebits == 0) -assert(p.fourbits == 0xF) -assert(p.eightbits == 0) -assert(p.onebyte == 0xFF) - -assert(tostring(m) == string.char(0xC6, 0x03, 0x54, 0xFF)) --- 0101 0100 0000 0011 1100 0110 - ---[[ -c60354ff -47d401ff - ---]] \ No newline at end of file diff --git a/test/testlayout.lua b/test/testlayout.lua new file mode 100644 index 0000000..977b44e --- /dev/null +++ b/test/testlayout.lua @@ -0,0 +1,199 @@ +local memory = require "memory" +local layout = require "memory.layout" + +local function asserterr(msg, f, ...) + local ok, res = pcall(f, ...) + assert(ok == false) + assert(string.find(res, msg, 1, true) ~= nil) +end + +for _, bits in ipairs{3, 9} do + asserterr("bigendian bits not supported", + layout.newstruct, {{ key = "unsupported", bits = bits, endian = "big" }}) + asserterr("bigendian bits not supported", + layout.newstruct, {{ key = "unsupported", bits = bits, endian = "native" }}) +end + +local function assertbits(m, bits) + local index = 0 + local count = 0 + local value = 0 + for char in string.gmatch(bits, "[01]") do + if char == "1" then + value = value+(1< Date: Sun, 1 Jul 2018 18:34:09 -0300 Subject: [PATCH 16/24] fixup! Draft of Lua Memory library. (yes, yet another library name) --- src/Makefile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index bc26785..8af8cd6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -34,12 +34,11 @@ API_O= lmemlib.o MEM_M= memory.so API_S= libluamemlib.so -LIB_S= libluamemory.so LIB_A= libluamemory.a ALL_O= $(MEM_O) $(API_O) ALL_A= $(LIB_A) -ALL_S= $(LIB_S) $(API_S) +ALL_S= $(API_S) ALL_M= $(MEM_M) ALL_T= $(ALL_A) $(ALL_S) $(ALL_M) @@ -61,9 +60,6 @@ $(API_S): $(API_O) $(MEM_M): $(MEM_O) $(API_S) $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) -$(LIB_S): $(LIB_O) $(API_S) $(ALL_M) - $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) - $(LIB_A): $(ALL_O) $(AR) $@ $^ $(RANLIB) $@ From eedfc3006f6c530d64b75e6982acc606000951e2 Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 1 Jul 2018 18:39:05 -0300 Subject: [PATCH 17/24] Add support for boolean fields --- lua/memory/layout.lua | 7 +++++++ test/testlayout.lua | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lua/memory/layout.lua b/lua/memory/layout.lua index 9d2ffdf..3b44798 100644 --- a/lua/memory/layout.lua +++ b/lua/memory/layout.lua @@ -26,6 +26,9 @@ function Pointer:__index(key) if mask ~= ~0 then value = value&mask end + if field.type == "boolean" then + value = value ~= 0 + end else -- TODO end @@ -42,6 +45,9 @@ function Pointer:__newindex(key, value) else local format = field.format if format ~= nil then + if field.type == "boolean" then + value = value and 1 or 0 + end local current = unpackmem(self.buffer, field.format, field.pos) local shift, mask = field.shift, field.mask assert(value <= mask, "value is too large") @@ -101,6 +107,7 @@ local function layoutfield(field, byteidx, bitoff) mask = (~0>>(LuaIntBits-bits)), shift = bitoff, value = field.value, + type = field.type or "number", } byteidx = byteidx+bytes diff --git a/test/testlayout.lua b/test/testlayout.lua index 977b44e..65f2fbe 100644 --- a/test/testlayout.lua +++ b/test/testlayout.lua @@ -197,3 +197,34 @@ do p.bytes = 0xaaf00f; assertbits(m, "010 01010|01010101|00001111|11110000|101 010 10") p.morebits = 0x5 ; assertbits(m, "010 01010|01010101|00001111|11110000|101 101 10") end + +do + local s = layout.newstruct{ + { key = "bit", bits = 1, type = "boolean" }, + { key = "bits", bits = 4, type = "boolean" }, + { key = "byte", bytes = 1, type = "boolean", endian = "native" }, + { key = "little", bytes = 2, type = "boolean", endian = "little" }, + { key = "big", bytes = 2, type = "boolean", endian = "big" }, + } + local p = layout.newpointer(s) + local m = memory.create(6) + layout.setpointer(p, m) + + memory.fill(m, 0xff) + assert(p.bit == true) + assert(p.bits == true) + assert(p.byte == true) + assert(p.little == true) + assert(p.big == true) + memory.fill(m, 0) + assert(p.bit == false) + assert(p.bits == false) + assert(p.byte == false) + assert(p.little == false) + assert(p.big == false) + p.bit = true ; assert(p.bit == true) ; assertbytes(m, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00) + p.bits = true ; assert(p.bits == true) ; assertbytes(m, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00) + p.byte = true ; assert(p.byte == true) ; assertbytes(m, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00) + p.little = true; assert(p.little == true); assertbytes(m, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00) + p.big = true ; assert(p.big == true) ; assertbytes(m, 0x03, 0x01, 0x01, 0x00, 0x00, 0x01) +end From 6efbc768ee79af7163d0bbbcad3b2b283a1dfbff Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 1 Jul 2018 23:42:11 -0300 Subject: [PATCH 18/24] Add support for string fields --- demo/layout.lua | 27 +++++++ lua/memory/demo.lua | 86 -------------------- lua/memory/layout.lua | 181 ++++++++++++++++++++++-------------------- test/testlayout.lua | 86 ++++++++++++++------ 4 files changed, 183 insertions(+), 197 deletions(-) create mode 100644 demo/layout.lua delete mode 100644 lua/memory/demo.lua diff --git a/demo/layout.lua b/demo/layout.lua new file mode 100644 index 0000000..ca32ab6 --- /dev/null +++ b/demo/layout.lua @@ -0,0 +1,27 @@ +local ipv = { + [4] = layout.struct{ + { key = "version" , bits = 4 }, + { key = "ihl" , bits = 4 }, + { key = "tos" , bits = 6 }, + { key = "ecn" , bits = 2 }, + { key = "tot_len" , bits = 16 }, + { key = "id" , bits = 16 }, + { key = "flags" , bits = 3 }, + { key = "frag_off", bits = 13 }, + { key = "ttl" , bits = 8 }, + { key = "protocol", bits = 8 }, + { key = "check" , bits = 16 }, + { key = "src" , bits = 32, type = "string" }, + { key = "dst" , bits = 32, type = "string" }, + }, + [6] = layout.struct{ + { key = "version" , bits = 4 }, + { key = "tc" , bits = 8 }, + { key = "fl" , bits = 20 }, + { key = "tot_len" , bits = 16 }, + { key = "nh" , bits = 8 }, + { key = "hl" , bits = 8 }, + { key = "src" , bits = 128, type = "string" }, + { key = "dst" , bits = 128, type = "string" }, + }, +} diff --git a/lua/memory/demo.lua b/lua/memory/demo.lua deleted file mode 100644 index dc8d884..0000000 --- a/lua/memory/demo.lua +++ /dev/null @@ -1,86 +0,0 @@ -local IPAddr = { - [4] = layout.create{ - { key = "route", value = "IPv4" }, - { key = "iface", bits = 32 }, - }, - [6] = layout.create{ - { key = "route", bits = 64 }, - { key = "iface", bits = 64 }, - }, -} - -local IPHeader = { - [4] = layout.create{ - { key = "version" , bits = 4 }, - { key = "ihl" , bits = 4 }, - { key = "tos" , bits = 6 }, - { key = "ecn" , bits = 2 }, - { key = "tot_len" , bits = 16 }, - { key = "id" , bits = 16 }, - { key = "flags" , bits = 3 }, - { key = "frag_off", bits = 13 }, - { key = "ttl" , bits = 8 }, - { key = "protocol", bits = 8 }, - { key = "check" , bits = 16 }, - { key = "src" , layout = IPAddr[4] }, - { key = "dst" , layout = IPAddr[4] }, - }, - [6] = layout.create{ - { key = "version" , bits = 4 }, - { key = "tc" , bits = 8 }, - { key = "fl" , bits = 20 }, - { key = "tot_len" , bits = 16 }, - { key = "nh" , bits = 8 }, - { key = "hl" , bits = 8 }, - { key = "src" , layout = IPAddr[6] }, - { key = "dst" , layout = IPAddr[6] }, - }, -} - -local uint2 = standard.uint(2) -local uint3 = standard.uint(3) -local uint4 = standard.uint(4) -local uint6 = standard.uint(6) -local uint8 = standard.uint8 -local uint13 = standard.uint(13) -local uint16 = standard.uint16 -local uint20 = standard.uint(20) -local uint64 = standard.uint64 -local ipv4addr = standard.array{ length = 4, mapping = uint8 } -local ipv6addr = standard.tuple{ - { key = "routing" , mapping = uint64 }, - { key = "interface", mapping = uint64 }, -} -local IPHeader = standard.select{ - selector = { key = "version", mapping = uint4 }, - cases = { - [4] = standard.tuple{ - { key = "ihl" , mapping = uint4 }, - { key = "tos" , mapping = uint6 }, - { key = "ecn" , mapping = uint2 }, - { key = "tot_len" , mapping = uint16 }, - { key = "id" , mapping = uint16 }, - { key = "flags" , mapping = uint3 }, - { key = "frag_off", mapping = uint13 }, - { key = "ttl" , mapping = uint8 }, - { key = "protocol", mapping = uint8 }, - { key = "check" , mapping = uint16 }, - { key = "src" , mapping = ipv4addr }, - { key = "dst" , mapping = ipv4addr }, - }, - [6] = standard.tuple{ - { key = "tc" , mapping = uint8 }, - { key = "fl" , mapping = uint20 }, - { key = "tot_len", mapping = uint16 }, - { key = "nh" , mapping = uint8 }, - { key = "hl" , mapping = uint8 }, - { key = "src" , mapping = ipv6addr }, - { key = "dst" , mapping = ipv6addr }, - }, - }, -} - -local IPv6ExtHeader = standard.tuple{ - { key = "nh" , mapping = uint8 }, - { key = "hdr", mapping = standard.embedded8 }, -} diff --git a/lua/memory/layout.lua b/lua/memory/layout.lua index 3b44798..21b65e1 100644 --- a/lua/memory/layout.lua +++ b/lua/memory/layout.lua @@ -14,115 +14,122 @@ local Pointer = {} function Pointer:__index(key) local field = self.struct.fields[key] if field ~= nil then - local value = field.value - if value == nil then - local format = field.format - if format ~= nil then - value = unpackmem(self.buffer, field.format, field.pos) - local shift, mask = field.shift, field.mask - if shift > 0 then - value = value>>shift - end - if mask ~= ~0 then - value = value&mask - end - if field.type == "boolean" then - value = value ~= 0 - end - else - -- TODO - end - end - return value + return field.read(self) end end function Pointer:__newindex(key, value) local field = self.struct.fields[key] if field ~= nil then - if field.value ~= nil then - assert(field.value == value, "invalid value") - else - local format = field.format - if format ~= nil then - if field.type == "boolean" then - value = value and 1 or 0 - end - local current = unpackmem(self.buffer, field.format, field.pos) - local shift, mask = field.shift, field.mask - assert(value <= mask, "value is too large") - current = (current&~(mask< 0 then -- byte align - byteidx = byteidx+1 - bitoff = 0 - end - bits = bytes*8 + bytes = field.bytes + if bitoff > 0 then -- byte align + byteidx = byteidx+1 + bitoff = 0 end - end - - local bitused = bitoff+bits - local bitpart = bitused%8 - local bytes = bitused//8 - - local endian = field.endian - if bitoff > 0 or bitpart > 0 then - assert(endian == nil or endian == "little", "bigendian bits not supported") - endian = "<" - elseif endian == "little" then - endian = "<" - elseif endian == "big" then - endian = ">" + bits = bytes*8 + bitpart = 0 else - assert(endian == nil or endian == "native", "illegal endianess") - endian = "=" + local bitused = bitoff+bits + bitpart = bitused%8 + bytes = bitused//8 end - - field = { + local spec = { pos = byteidx, bitoff = bitoff, bits = bits, - mask = (~0>>(LuaIntBits-bits)), - shift = bitoff, - value = field.value, - type = field.type or "number", + bytes = bytes, } + return spec, byteidx, bitoff, bytes, bits, bitpart +end + +local layout = {} + +function layout.string(field, ...) + local spec, byteidx, bitoff, bytes, bits, bitpart = calcsizes(field, ...) + + assert(bitoff == 0 and bitpart == 0, "unsupported type") + local format = "c"..bytes + function spec.read(self) + return unpackmem(self.buffer, format, byteidx+self.pos-1) + end + function spec.write(self, value) + packmem(self.buffer, format, byteidx+self.pos-1, value) + end - byteidx = byteidx+bytes - if bitpart > 0 then - bytes = bytes+1 - bitoff = bitpart + return spec, byteidx+bytes, bitoff +end + +function layout.number(field, ...) + local spec, byteidx, bitoff, bytes, bits, bitpart = calcsizes(field, ...) + assert(bits <= LuaIntBits, "size is too big") + + if bitoff == 0 and bitpart == 0 then + local endian = field.endian + if endian == nil then endian = "native" end + endian = assert(endianflag[endian], "illegal endianess") + local format = endian.."I"..bytes + function spec.read(self) + return unpackmem(self.buffer, format, byteidx+self.pos-1) + end + function spec.write(self, value) + packmem(self.buffer, format, byteidx+self.pos-1, value) + end + else + local mask = (~0>>(LuaIntBits-bits)) + local shift = bitoff + if bitpart > 0 then + bitoff = bitpart + spec.bytes = bytes+1 + end + local format = ">shift)&mask + end + function spec.write(self, value) + assert(value <= mask, "unsigned overflow") + local buffer = self.buffer + local pos = byteidx+self.pos-1 + local current = unpackmem(buffer, format, pos) + value = (current&~(mask< 0, "invalid type") local struct = { pos = byteidx, bitoff = bitoff, @@ -132,14 +139,15 @@ function layoutstruct(spec, byteidx, bitoff) -- local defined above local fields = {} for _, field in ipairs(spec) do local key = field.key - field, byteidx, bitoff = layoutfield(field, byteidx, bitoff) + local type = field.type + if type == nil then type = "number" end + local build = layout[type] or layoutstruct + field, byteidx, bitoff = build(field, byteidx, bitoff) struct.bytes = field.pos-1+field.bytes struct.bits = 8*(byteidx-1)+bitoff if key ~= nil then - assert(field.bits <= LuaIntBits, "value is too big") - assert(field.bytes <= 8) fields[key] = field end end @@ -159,12 +167,13 @@ function module.newpointer(struct) return setmetatable({ struct = struct, buffer = empty, + pos = 1, }, Pointer) end function module.setpointer(pointer, buffer, pos) rawset(pointer, "buffer", buffer) - rawset(pointer, "pos", pos) + rawset(pointer, "pos", pos or 1) end return module diff --git a/test/testlayout.lua b/test/testlayout.lua index 65f2fbe..3be9da5 100644 --- a/test/testlayout.lua +++ b/test/testlayout.lua @@ -4,14 +4,7 @@ local layout = require "memory.layout" local function asserterr(msg, f, ...) local ok, res = pcall(f, ...) assert(ok == false) - assert(string.find(res, msg, 1, true) ~= nil) -end - -for _, bits in ipairs{3, 9} do - asserterr("bigendian bits not supported", - layout.newstruct, {{ key = "unsupported", bits = bits, endian = "big" }}) - asserterr("bigendian bits not supported", - layout.newstruct, {{ key = "unsupported", bits = bits, endian = "native" }}) + assert(string.find(res, msg, 1, true) ~= nil, res) end local function assertbits(m, bits) @@ -41,15 +34,14 @@ local function assertbytes(m, ...) assert(memory.len(m) == count) end -for _, endian in ipairs{"little", false} do - endian = endian or nil +do local s = layout.newstruct{ - { key = "one", bits = 1, endian = endian }, - { key = "two", bits = 2, endian = endian }, - { key = "three", bits = 3, endian = endian }, - { key = "four", bits = 4, endian = endian }, - { key = "eight", bits = 8, endian = endian }, - { key = "six", bits = 6, endian = endian }, + { key = "one", bits = 1 }, + { key = "two", bits = 2 }, + { key = "three", bits = 3 }, + { key = "four", bits = 4 }, + { key = "eight", bits = 8 }, + { key = "six", bits = 6 }, } local p = layout.newpointer(s) @@ -87,12 +79,12 @@ for _, endian in ipairs{"little", false} do p.eight = 0xAA ; assertbits(m, "0 10 101 01|01 010101|01 101010") p.six = 0x2A ; assertbits(m, "0 10 101 01|01 010101|01 010101") - asserterr("value is too large", function () p.one = 2 end) - asserterr("value is too large", function () p.two = 4 end) - asserterr("value is too large", function () p.three = 8 end) - asserterr("value is too large", function () p.four = 16 end) - asserterr("value is too large", function () p.eight = 256 end) - asserterr("value is too large", function () p.six = 64 end) + asserterr("unsigned overflow", function () p.one = 2 end) + asserterr("unsigned overflow", function () p.two = 4 end) + asserterr("unsigned overflow", function () p.three = 8 end) + asserterr("unsigned overflow", function () p.four = 16 end) + asserterr("unsigned overflow", function () p.eight = 256 end) + asserterr("unsigned overflow", function () p.six = 64 end) assertbits(m, "0 10 101 01|01 010101|01 010101") end @@ -114,9 +106,9 @@ do p.two = 0xaaaa ; assertbytes(m, 0x55, 0xaa, 0xaa, 0xff, 0xff, 0xff); p.three = 0x333333 ; assertbytes(m, 0x55, 0xaa, 0xaa, 0x33, 0x33, 0x33); - asserterr("value is too large", function () p.one = 1<<8 end) - asserterr("value is too large", function () p.two = 1<<16 end) - asserterr("value is too large", function () p.three = 1<<24 end) + asserterr("unsigned overflow", function () p.one = 1<<8 end) + asserterr("unsigned overflow", function () p.two = 1<<16 end) + asserterr("unsigned overflow", function () p.three = 1<<24 end) end do @@ -228,3 +220,47 @@ do p.little = true; assert(p.little == true); assertbytes(m, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00) p.big = true ; assert(p.big == true) ; assertbytes(m, 0x03, 0x01, 0x01, 0x00, 0x00, 0x01) end + +do + for _, bits in ipairs{1, 2, 3, 4, 7, 9, 15, 17} do + asserterr("unsupported type", + layout.newstruct, {{ key = "unsupported", bits = bits, type = "string" }}) + for _, size in ipairs{8, 16, 32, 64} do + asserterr("unsupported type", + layout.newstruct, { + { bits = bits }, + { key = "unsupported", bits = size, type = "string" }, + }) + end + end + + local s = layout.newstruct{ + { key = "one", bytes = 1, type = "string" }, + { key = "two", bytes = 2, type = "string" }, + { key = "eight", bytes = 8, type = "string" }, + { key = "nine", bytes = 9, type = "string" }, + } + local p = layout.newpointer(s) + local m = memory.create(20) + layout.setpointer(p, m) + + memory.fill(m, 0) + assert(p.one == string.rep("\0", 1)) + assert(p.two == string.rep("\0", 2)) + assert(p.eight == string.rep("\0", 8)) + assert(p.nine == string.rep("\0", 9)) + + local function assertmem(f, i, j) + local v = string.rep("\x55", 1+j-i) + p[f] = v + assert(p[f] == v) + for k = 1, i-1 do assert(memory.get(m, k) == 0) end + for k = i, j do assert(memory.get(m, k) == 0x55) end + for k = j+1, memory.len(m) do assert(memory.get(m, k) == 0) end + memory.fill(m, 0) + end + assertmem("one", 1, 1) + assertmem("two", 2, 3) + assertmem("eight", 4, 11) + assertmem("nine", 12, 20) +end From 180f2969cd9f0d4ddb4f0128ad4ac0e8f328124e Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Fri, 13 Jul 2018 07:42:01 -0300 Subject: [PATCH 19/24] Support for nested structures. --- lua/memory/layout.lua | 50 +++++++++++++++++++++++++++++-------------- test/testlayout.lua | 31 ++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/lua/memory/layout.lua b/lua/memory/layout.lua index 21b65e1..0eef452 100644 --- a/lua/memory/layout.lua +++ b/lua/memory/layout.lua @@ -14,14 +14,14 @@ local Pointer = {} function Pointer:__index(key) local field = self.struct.fields[key] if field ~= nil then - return field.read(self) + return field.read(self.parent) end end function Pointer:__newindex(key, value) local field = self.struct.fields[key] if field ~= nil then - field.write(self, value) + field.write(self.parent, value) end end @@ -122,37 +122,53 @@ function layout.boolean(...) return read(...) ~= 0 end local write = spec.write - function spec.write(buffer, value, ...) - return write(buffer, value and 1 or 0, ...) + function spec.write(self, value, ...) + return write(self, value and 1 or 0, ...) end return spec, byteidx, bitoff end -local function layoutstruct(spec, byteidx, bitoff) - assert(type(spec) == "table" and #spec > 0, "invalid type") - local struct = { +local function layoutstruct(fields, byteidx, bitoff) + assert(type(fields) == "table" and #fields > 0, "invalid type") + local spec = { pos = byteidx, bitoff = bitoff, bits = 0, bytes = 0, } - local fields = {} - for _, field in ipairs(spec) do + local specs = {} + for _, field in ipairs(fields) do local key = field.key local type = field.type if type == nil then type = "number" end - local build = layout[type] or layoutstruct + local build = assert(layout[type], "unsupported type") field, byteidx, bitoff = build(field, byteidx, bitoff) - struct.bytes = field.pos-1+field.bytes - struct.bits = 8*(byteidx-1)+bitoff + spec.bytes = field.pos-1+field.bytes + spec.bits = 8*(byteidx-1)+bitoff if key ~= nil then - fields[key] = field + specs[key] = field + end + end + spec.fields = specs + return spec, byteidx, bitoff +end + +function layout.struct(field, ...) + local spec, byteidx, bitoff = layoutstruct(field, ...) + function spec.read(self) + local pointer = self[spec] + if pointer == nil then + pointer = setmetatable({ struct = spec, parent = self }, Pointer) + self[spec] = pointer end + return pointer end - struct.fields = fields - return struct, byteidx, bitoff + function spec.write(self, value, ...) + + end + return spec, byteidx, bitoff end @@ -164,11 +180,13 @@ end local empty = newmem(0) function module.newpointer(struct) - return setmetatable({ + local pointer = setmetatable({ struct = struct, buffer = empty, pos = 1, }, Pointer) + rawset(pointer, "parent", pointer) + return pointer end function module.setpointer(pointer, buffer, pos) diff --git a/test/testlayout.lua b/test/testlayout.lua index 3be9da5..4473d0d 100644 --- a/test/testlayout.lua +++ b/test/testlayout.lua @@ -2,7 +2,7 @@ local memory = require "memory" local layout = require "memory.layout" local function asserterr(msg, f, ...) - local ok, res = pcall(f, ...) + local ok, res = xpcall(f, debug.traceback, ...) assert(ok == false) assert(string.find(res, msg, 1, true) ~= nil, res) end @@ -264,3 +264,32 @@ do assertmem("eight", 4, 11) assertmem("nine", 12, 20) end + +do + local s = layout.newstruct{ + { bits = 2 }, + { key = "half", bits = 4 }, + { key = "double", bytes = 2, endian = "big" }, + { key = "nested", type = "struct", + { bits = 2 }, + { key = "half", bits = 4 }, + { key = "double", bytes = 2, endian = "little" }, + { key = "single", bytes = 1 }, + }, + } + local p = layout.newpointer(s) + local m = memory.create(7) + layout.setpointer(p, m) + + memory.fill(m, 0x55) ; assertbits(m, "10 1010 10|10101010|10101010|10 1010 10|10101010|10101010|10101010") + assert(p.half == 0x5) + assert(p.double == 0x5555) + assert(p.nested.half == 0x5) + assert(p.nested.double == 0x5555) + assert(p.nested.single == 0x55) + p.half = 0xa ; assertbits(m, "10 0101 10|10101010|10101010|10 1010 10|10101010|10101010|10101010") + p.double = 0xaaf0 ; assertbits(m, "10 0101 10|01010101|00001111|10 1010 10|10101010|10101010|10101010") + p.nested.half = 0x5 ; assertbits(m, "10 0101 10|01010101|00001111|10 0101 10|10101010|10101010|10101010") + p.nested.double = 0xaaf0; assertbits(m, "10 0101 10|01010101|00001111|10 0101 10|00001111|01010101|10101010") + p.nested.single = 0xaa ; assertbits(m, "10 0101 10|01010101|00001111|10 0101 10|00001111|01010101|01010101") +end From 094c5591c6e836874224e8b39a9a2afaf1a4ecbe Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 15 Jul 2018 16:55:21 -0300 Subject: [PATCH 20/24] Provide uniform 'out of bounds' error message. --- src/lmemmod.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lmemmod.c b/src/lmemmod.c index 25dd58e..9c2e356 100644 --- a/src/lmemmod.c +++ b/src/lmemmod.c @@ -601,7 +601,7 @@ static int mem_pack (lua_State *L) { const char *fmt = luaL_checkstring(L, 2); /* format string */ size_t i = (size_t)posrelat(luaL_checkinteger(L, 3), lb) - 1; int arg = 3; /* current argument to pack */ - luaL_argcheck(L, 0 <= i && i <= (lua_Integer)lb-1, 3, "index out of bounds"); + luaL_argcheck(L, 0 <= i && i <= (lua_Integer)lb-1, 3, "out of bounds"); initheader(L, &h); mem += i; while (*fmt != '\0') { @@ -727,13 +727,13 @@ static int mem_unpack (lua_State *L) { const char *fmt = luaL_checkstring(L, 2); size_t pos = (size_t)posrelat(luaL_optinteger(L, 3, 1), ld) - 1; int n = 0; /* number of results */ - luaL_argcheck(L, pos <= ld, 3, "initial position out of bounds"); + luaL_argcheck(L, pos <= ld, 3, "out of bounds"); 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, 1, "data too short"); + luaL_argerror(L, 1, "out of bounds"); pos += ntoalign; /* skip alignment */ /* stack space for item + next position */ luaL_checkstack(L, 1, "too many results"); From eef84ed842655f1fc62e285a5bc4923b920a57e1 Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 15 Jul 2018 17:04:31 -0300 Subject: [PATCH 21/24] Fix calculation of bitwise field after it algines to 8 bits. --- lua/memory/layout.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lua/memory/layout.lua b/lua/memory/layout.lua index 0eef452..124a6cb 100644 --- a/lua/memory/layout.lua +++ b/lua/memory/layout.lua @@ -95,7 +95,6 @@ function layout.number(field, ...) local mask = (~0>>(LuaIntBits-bits)) local shift = bitoff if bitpart > 0 then - bitoff = bitpart spec.bytes = bytes+1 end local format = " Date: Sun, 15 Jul 2018 17:05:19 -0300 Subject: [PATCH 22/24] Add support for endianess of bitwise fields. --- lua/memory/layout.lua | 50 ++++-- test/testlayout.lua | 387 +++++++++++++++++++++++------------------- 2 files changed, 248 insertions(+), 189 deletions(-) diff --git a/lua/memory/layout.lua b/lua/memory/layout.lua index 124a6cb..e7c498a 100644 --- a/lua/memory/layout.lua +++ b/lua/memory/layout.lua @@ -5,9 +5,10 @@ local rawset = _G.rawset local setmetatable = _G.setmetatable local memory = require "memory" -local newmem = memory.create -local packmem = memory.pack -local unpackmem = memory.unpack +local memnew = memory.create +local memget = memory.get +local mempack = memory.pack +local memunpack = memory.unpack local Pointer = {} @@ -27,12 +28,22 @@ end local LuaIntBits = 64 -local endianflag = { - native = "=", +local EndianFlag = { + native = "", little = "<", big = ">", } +local LittleEndian do + local bytes = memnew(2) + mempack(bytes, "I2", 1, 1) + LittleEndian = { + [">"] = false, + ["<"] = true, + [""] = memget(bytes, 1) == 1, + } +end + local function calcsizes(field, byteidx, bitoff) local bitpart local bytes @@ -67,10 +78,10 @@ function layout.string(field, ...) assert(bitoff == 0 and bitpart == 0, "unsupported type") local format = "c"..bytes function spec.read(self) - return unpackmem(self.buffer, format, byteidx+self.pos-1) + return memunpack(self.buffer, format, byteidx+self.pos-1) end function spec.write(self, value) - packmem(self.buffer, format, byteidx+self.pos-1, value) + mempack(self.buffer, format, byteidx+self.pos-1, value) end return spec, byteidx+bytes, bitoff @@ -80,34 +91,37 @@ function layout.number(field, ...) local spec, byteidx, bitoff, bytes, bits, bitpart = calcsizes(field, ...) assert(bits <= LuaIntBits, "size is too big") + local endian = field.endian + if endian == nil then endian = "native" end + endian = assert(EndianFlag[endian], "illegal endianess") + if bitoff == 0 and bitpart == 0 then - local endian = field.endian - if endian == nil then endian = "native" end - endian = assert(endianflag[endian], "illegal endianess") local format = endian.."I"..bytes function spec.read(self) - return unpackmem(self.buffer, format, byteidx+self.pos-1) + return memunpack(self.buffer, format, byteidx+self.pos-1) end function spec.write(self, value) - packmem(self.buffer, format, byteidx+self.pos-1, value) + if not mempack(self.buffer, format, byteidx+self.pos-1, value) then + error("out of bounds") + end end else local mask = (~0>>(LuaIntBits-bits)) - local shift = bitoff + local shift = LittleEndian[endian] and bitoff or (8-bitpart)%8 if bitpart > 0 then spec.bytes = bytes+1 end - local format = ">shift)&mask + return (memunpack(self.buffer, format, byteidx+self.pos-1)>>shift)&mask end function spec.write(self, value) assert(value <= mask, "unsigned overflow") local buffer = self.buffer local pos = byteidx+self.pos-1 - local current = unpackmem(buffer, format, pos) + local current = memunpack(buffer, format, pos) value = (current&~(mask< 011|0 1001 1010 1011 + p.fewbigbits = 0x3 ; assertbytes(m, 0xfe, 0xdc, 0xba, 0x98, 0x69, 0xab, 0xcd, 0xef) + -- 011|0 1001 1010 1011 --> 011|1 0110 0101 0100 + p.manybigbits = 0x1654 ; assertbytes(m, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0xcd, 0xef) + --..1100 1][101] [1110 1111.. --> ..1100 1][010] [1110 1111.. + p.fewlittlebits = 0x2 ; assertbytes(m, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0xca, 0xef) + --..1100 1][010] [1110 1111.. --> ..0011 0][010] [0001 0000.. + p.manylittlebits = 0x206; assertbytes(m, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10) end do - local s = layout.newstruct{ - { key = "bit", bits = 1, type = "boolean" }, - { key = "bits", bits = 4, type = "boolean" }, + local p = layout.newpointer(layout.newstruct{ + { key = "bit", bits = 1, type = "boolean", endian = "little" }, + { key = "bits", bits = 4, type = "boolean", endian = "little" }, { key = "byte", bytes = 1, type = "boolean", endian = "native" }, { key = "little", bytes = 2, type = "boolean", endian = "little" }, { key = "big", bytes = 2, type = "boolean", endian = "big" }, - } - local p = layout.newpointer(s) + }) local m = memory.create(6) layout.setpointer(p, m) @@ -234,13 +278,12 @@ do end end - local s = layout.newstruct{ + local p = layout.newpointer(layout.newstruct{ { key = "one", bytes = 1, type = "string" }, { key = "two", bytes = 2, type = "string" }, { key = "eight", bytes = 8, type = "string" }, { key = "nine", bytes = 9, type = "string" }, - } - local p = layout.newpointer(s) + }) local m = memory.create(20) layout.setpointer(p, m) @@ -265,7 +308,7 @@ do assertmem("nine", 12, 20) end -do +while false do local s = layout.newstruct{ { bits = 2 }, { key = "half", bits = 4 }, @@ -293,3 +336,5 @@ do p.nested.double = 0xaaf0; assertbits(m, "10 0101 10|01010101|00001111|10 0101 10|00001111|01010101|10101010") p.nested.single = 0xaa ; assertbits(m, "10 0101 10|01010101|00001111|10 0101 10|00001111|01010101|01010101") end + +print("Success!") From be9e8c405030367a5c410a783f4fd91df7e5fcaf Mon Sep 17 00:00:00 2001 From: Renato Maia Date: Sun, 15 Jul 2018 21:24:08 -0300 Subject: [PATCH 23/24] fixup! Support for nested structures. --- lua/memory/layout.lua | 17 ++++++++++++--- test/testlayout.lua | 51 ++++++++++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/lua/memory/layout.lua b/lua/memory/layout.lua index e7c498a..936a24f 100644 --- a/lua/memory/layout.lua +++ b/lua/memory/layout.lua @@ -6,6 +6,7 @@ local setmetatable = _G.setmetatable local memory = require "memory" local memnew = memory.create +local memcopy = memory.fill local memget = memory.get local mempack = memory.pack local memunpack = memory.unpack @@ -149,6 +150,7 @@ local function layoutstruct(fields, byteidx, bitoff) bits = 0, bytes = 0, } + local specs = {} for _, field in ipairs(fields) do local key = field.key @@ -157,8 +159,8 @@ local function layoutstruct(fields, byteidx, bitoff) local build = assert(layout[type], "unsupported type") field, byteidx, bitoff = build(field, byteidx, bitoff) - spec.bytes = field.pos-1+field.bytes - spec.bits = 8*(byteidx-1)+bitoff + spec.bytes = byteidx-spec.pos + spec.bits = 8*(spec.bytes)+bitoff-spec.bitoff if key ~= nil then specs[key] = field @@ -179,7 +181,16 @@ function layout.struct(field, ...) return pointer end function spec.write(self, value, ...) - + if getmetatable(value) == Pointer then + local src, dst = value.struct, spec + assert(src.bitoff == 0 and src.bits%8 == 0 + and dst.bitoff == 0 and dst.bits%8 == 0, "unsupported") + assert(dst.bytes == src.bytes, "size mismatch") + memcopy(self.parent.buffer, value.parent.buffer, + dst.pos, dst.pos+dst.bytes-1, src.pos) + else + error("unsupported") + end end return spec, byteidx, bitoff end diff --git a/test/testlayout.lua b/test/testlayout.lua index 64e302e..ae46286 100644 --- a/test/testlayout.lua +++ b/test/testlayout.lua @@ -308,33 +308,48 @@ do assertmem("nine", 12, 20) end -while false do - local s = layout.newstruct{ +do + local p = layout.newpointer(layout.newstruct{ { bits = 2 }, - { key = "half", bits = 4 }, + { key = "half", bits = 4, endian = "little" }, { key = "double", bytes = 2, endian = "big" }, { key = "nested", type = "struct", { bits = 2 }, - { key = "half", bits = 4 }, + { key = "half", bits = 4, endian = "big" }, { key = "double", bytes = 2, endian = "little" }, { key = "single", bytes = 1 }, }, - } - local p = layout.newpointer(s) - local m = memory.create(7) + { key = "single", bytes = 1 }, + }) + local m = memory.create(8) layout.setpointer(p, m) - memory.fill(m, 0x55) ; assertbits(m, "10 1010 10|10101010|10101010|10 1010 10|10101010|10101010|10101010") - assert(p.half == 0x5) - assert(p.double == 0x5555) - assert(p.nested.half == 0x5) - assert(p.nested.double == 0x5555) - assert(p.nested.single == 0x55) - p.half = 0xa ; assertbits(m, "10 0101 10|10101010|10101010|10 1010 10|10101010|10101010|10101010") - p.double = 0xaaf0 ; assertbits(m, "10 0101 10|01010101|00001111|10 1010 10|10101010|10101010|10101010") - p.nested.half = 0x5 ; assertbits(m, "10 0101 10|01010101|00001111|10 0101 10|10101010|10101010|10101010") - p.nested.double = 0xaaf0; assertbits(m, "10 0101 10|01010101|00001111|10 0101 10|00001111|01010101|10101010") - p.nested.single = 0xaa ; assertbits(m, "10 0101 10|01010101|00001111|10 0101 10|00001111|01010101|01010101") + memory.set(m, 1, 0x10, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef) + assert(p.half == 0x4) + assert(p.double == 0x2345) + assert(p.nested.half == 0x9) + assert(p.nested.double == 0xab89) + assert(p.nested.single == 0xcd) + assert(p.single == 0xef) + + memory.fill(m, 0x55) + p.half = 0xa ; assertbytes(m, 0x69, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55) + p.double = 0xaaf0 ; assertbytes(m, 0x69, 0xaa, 0xf0, 0x55, 0x55, 0x55, 0x55, 0x55) + p.nested.half = 0xa ; assertbytes(m, 0x69, 0xaa, 0xf0, 0x69, 0x55, 0x55, 0x55, 0x55) + p.nested.double = 0xaaf0; assertbytes(m, 0x69, 0xaa, 0xf0, 0x69, 0xf0, 0xaa, 0x55, 0x55) + p.nested.single = 0xaa ; assertbytes(m, 0x69, 0xaa, 0xf0, 0x69, 0xf0, 0xaa, 0xaa, 0x55) + p.single = 0xaa ; assertbytes(m, 0x69, 0xaa, 0xf0, 0x69, 0xf0, 0xaa, 0xaa, 0xaa) + + local p2 = layout.newpointer(layout.newstruct{ + { bytes = 1 }, + { key = "nested", type = "struct", { bytes = 4 } }, + }) + local m2 = memory.create(6) + layout.setpointer(p2, m2) + p2.nested = p.nested ; assertbytes(m2, 0x00, 0x69, 0xf0, 0xaa, 0xaa, 0x00) + + memory.fill(m2, 0xff) + p.nested = p2.nested ; assertbytes(m, 0x69, 0xaa, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xaa) end print("Success!") From 0c803c0ce356e2a45ef56990a4599df24b76a5e2 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 8 Aug 2018 16:04:02 -0300 Subject: [PATCH 24/24] Add support for tostring in structs and unions --- lua/memory/layout.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lua/memory/layout.lua b/lua/memory/layout.lua index 936a24f..745cb0a 100644 --- a/lua/memory/layout.lua +++ b/lua/memory/layout.lua @@ -10,6 +10,7 @@ local memcopy = memory.fill local memget = memory.get local mempack = memory.pack local memunpack = memory.unpack +local memtostring = memory.tostring local Pointer = {} @@ -27,6 +28,12 @@ function Pointer:__newindex(key, value) end end +function Pointer:__tostring() + assert(self.struct.bitoff == 0 and self.struct.bits%8 == 0, 'unsupported') + local startpos = self.parent.pos+self.struct.pos-1 + return memtostring(self.parent.buffer, startpos, startpos + self.struct.bytes-1) +end + local LuaIntBits = 64 local EndianFlag = {