├── LICENSE ├── Makefile ├── README.md ├── demo ├── compare.lua ├── fill.lua ├── fixed.lua ├── inspect.lua ├── packing.lua └── resizable.lua ├── doc ├── install.md └── manual.md ├── etc ├── Makefile.win ├── luamem.def ├── memory-scm-1.rockspec └── memory.def ├── src ├── Makefile ├── lmemlib.c ├── luamem.c └── luamem.h └── test └── testall.lua /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015-2021 Renato Maia 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= 2 | 3 | # Your platform. See PLATS for possible values. 4 | PLAT= guess 5 | 6 | # Where to install. The installation starts in the src directory, 7 | # so take care if INSTALL_DIR is not an absolute path. See the local target. 8 | # You may want to make INSTALL_CMODDIR consistent with LUA_ROOT, and LUA_CDIR 9 | # in luaconf.h. 10 | INSTALL_DIR= /usr/local 11 | INSTALL_INCDIR= $(INSTALL_DIR)/include 12 | INSTALL_LIBDIR= $(INSTALL_DIR)/lib 13 | INSTALL_CMODDIR= $(INSTALL_DIR)/lib/lua/$(LUA_VER) 14 | 15 | # How to install. If your install program does not support "-p", then 16 | # you may have to run ranlib on the installed liblua.a. 17 | INSTALL_DATA= install -p -m 0644 18 | # 19 | # If you don't have "install" you can use "cp" instead. 20 | # INSTALL_DATA= cp -p 21 | 22 | # Other utilities. 23 | MKDIR= mkdir -p 24 | RM= rm -f 25 | 26 | # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= 27 | 28 | # Convenience platforms targets. 29 | PLATS= guess generic linux macosx solaris 30 | 31 | # What to install. 32 | TO_INC= luamem.h 33 | TO_LIB= libluamem.so libluamemory.a 34 | TO_CMOD= memory.so 35 | 36 | # Lua version and release. 37 | LUA_VER= 5.4 38 | 39 | # Targets start here. 40 | all: $(PLAT) 41 | 42 | lib: 43 | @cd src && $(MAKE) $(PLAT) ALL=lib 44 | 45 | $(PLATS) help clean: 46 | @cd src && $(MAKE) $@ 47 | 48 | install: install_lib install_mod 49 | 50 | install_lib: 51 | cd src && $(MKDIR) $(INSTALL_INCDIR) $(INSTALL_LIBDIR) 52 | cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INCDIR) 53 | cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIBDIR) 54 | 55 | install_mod: 56 | cd src && $(MKDIR) $(INSTALL_CMODDIR) 57 | cd src && $(INSTALL_DATA) $(TO_CMOD) $(INSTALL_CMODDIR) 58 | 59 | uninstall: uninstall_lib uninstall_mod 60 | 61 | uninstall_lib: 62 | cd src && cd $(INSTALL_INCDIR) && $(RM) $(TO_INC) 63 | cd src && cd $(INSTALL_LIBDIR) && $(RM) $(TO_LIB) 64 | 65 | uninstall_mod: 66 | cd src && cd $(INSTALL_CMODDIR) && $(RM) $(TO_CMOD) 67 | 68 | local: 69 | $(MAKE) install INSTALL_DIR=../install 70 | 71 | # make may get confused with install/ if it does not support .PHONY. 72 | dummy: 73 | 74 | # Echo config parameters. 75 | echo: 76 | @cd src && $(MAKE) -s echo 77 | @echo "PLAT= $(PLAT)" 78 | @echo "LUA_VER= $LUA_VER" 79 | @echo "TO_INC= $(TO_INC)" 80 | @echo "TO_LIB= $(TO_LIB)" 81 | @echo "TO_CMOD= $(TO_CMOD)" 82 | @echo "INSTALL_DIR= $(INSTALL_DIR)" 83 | @echo "INSTALL_INCDIR= $(INSTALL_INCDIR)" 84 | @echo "INSTALL_LIBDIR= $(INSTALL_LIBDIR)" 85 | @echo "INSTALL_CMODDIR= $(INSTALL_CMODDIR)" 86 | @echo "INSTALL_DATA= $(INSTALL_DATA)" 87 | 88 | # Targets that do not create files (not all makes understand .PHONY). 89 | .PHONY: all $(PLATS) help clean install install_lib install_mod \ 90 | uninstall uninstall_lib uninstall_mod local dummy echo pc 91 | 92 | # (end of Makefile) 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lua Memory 2 | ========== 3 | 4 | The purpose of this project is to support manipulation of memory areas in Lua. 5 | 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. 6 | The library provides the following components: 7 | 8 | - [Module](doc/manual.md#lua-module) to manipulate memory areas in Lua. 9 | - [C API](doc/manual.md#c-library) for manipulation memory areas in similar fashion to the [Lua C API](http://www.lua.org/manual/5.4/manual.html#4). 10 | 11 | Documentation 12 | ------------- 13 | 14 | - [License](LICENSE) 15 | - [Install](doc/install.md) 16 | - [Manual](doc/manual.md) 17 | - [Demos](demo/) 18 | - [Create Fixed-Size Memory](demo/fixed.lua) 19 | - [Create Resizable Memory](demo/resizable.lua) 20 | - [Change Contents](demo/fill.lua) 21 | - [Inspect Contents](demo/inspect.lua) 22 | - [Compare Contents](demo/compare.lua) 23 | - [(Un)packing Data](demo/packing.lua) 24 | - C API Use 25 | - [Module `memory` Source](src/lmemlib.c) 26 | - [Adapting Lua Standard Libraries](https://github.com/renatomaia/lua/commit/ceddb5af05061937034d8e80f4a867d7c8126831) 27 | 28 | History 29 | ------- 30 | 31 | ### Version 2.0 32 | - Updated to Lua 5.4. 33 | - [Referenced](doc/manual.md#luamem_newref) (and [resizable](doc/manual.md#memorycreate-m--i--j)) memories are [closeable](http://www.lua.org/manual/5.4/manual.html#3.3.8). 34 | - Memories now support [concat](http://www.lua.org/manual/5.4/manual.html#2.4) operator (`..`) to produce strings. 35 | - `memory.get` now requires the indice argument `i`. 36 | - C library and header are renamed to `*luamem.*`. 37 | - Functions `luamem_pushresult` and `luamem_pushresultsize` are removed. 38 | - Functions `luamem_*string` are renamed to `luamem_*array` to explicit that their result are not null-terminated. 39 | - New function `luamem_resetref` to reset a referenced memory without releasing its current value. 40 | 41 | ### Version 1.0 42 | - Lua module 43 | - C Library 44 | -------------------------------------------------------------------------------- /demo/compare.lua: -------------------------------------------------------------------------------- 1 | -- create a memory 2 | m = memory.create("Hello, World!") 3 | 4 | -- no difference found 5 | assert(memory.diff(m, "Hello, World!") == nil) 6 | 7 | -- finding the difference 8 | local idx, less = memory.diff(m, "Hello, world!") 9 | assert(idx == 8) 10 | assert(less == true) 11 | -------------------------------------------------------------------------------- /demo/fill.lua: -------------------------------------------------------------------------------- 1 | -- create a memory 2 | eight = memory.create(8) 3 | 4 | -- set memory contents 5 | memory.set(eight, 1, 0x01, 0x23, 0x45, 0x67, 6 | 0x89, 0xab, 0xcd, 0xef) 7 | assert(memory.tostring(eight) == "\x01\x23\x45\x67\x89\xab\xcd\xef") 8 | 9 | -- fill memory with contents from string 10 | memory.fill(eight, "Hello, world!") 11 | assert(memory.tostring(eight) == "Hello, w") 12 | 13 | -- fill a portion of the memory 14 | memory.fill(eight, "boy!", 5) 15 | assert(memory.tostring(eight) == "Hellboy!") 16 | 17 | -- sets last byte to zero 18 | memory.set(eight, -1, 0) 19 | assert(memory.tostring(eight) == "Hellboy\0") 20 | 21 | -- fill positions 3 to 8 with contents from position 2 22 | memory.fill(eight, eight, 3, 8, 2) 23 | assert(memory.tostring(eight) == "Heellboy") 24 | 25 | -- fill initial portion of memory 26 | memory.fill(eight, "#h", 1, 2) 27 | assert(memory.tostring(eight) == "#hellboy") 28 | -------------------------------------------------------------------------------- /demo/fixed.lua: -------------------------------------------------------------------------------- 1 | -- create memory with zeros 2 | zeros = memory.create(8) 3 | assert(memory.type(zeros) == "fixed") 4 | assert(memory.tostring(zeros) == string.rep("\0", 8)) 5 | 6 | -- create memory with contents from string 7 | copy = memory.create("Hello, world!") 8 | assert(memory.type(copy) == "fixed") 9 | assert(memory.tostring(copy) == "Hello, world!") 10 | -------------------------------------------------------------------------------- /demo/inspect.lua: -------------------------------------------------------------------------------- 1 | -- create a memory 2 | eight = memory.create("\x01\x23\x45\x67\x89\xab\xcd\xef") 3 | 4 | -- get last byte 5 | assert(memory.get(eight, -1) == 0xef) 6 | 7 | -- get bytes from a portion of the memory 8 | local a, b, c = memory.get(eight, 3, 5) 9 | assert(a == 0x45) 10 | assert(b == 0x67) 11 | assert(c == 0x89) 12 | 13 | -- search for bytes in memory 14 | assert(memory.find(eight, "\x45\x67\x89") == 3) 15 | -------------------------------------------------------------------------------- /demo/packing.lua: -------------------------------------------------------------------------------- 1 | m = memory.create(2) 2 | 3 | memory.pack(m, "I2", 1, 0x0001) 4 | if memory.unpack(m, "B") == 0 then 5 | print("big-endian platform") 6 | else 7 | print("little-endian platform") 8 | end 9 | -------------------------------------------------------------------------------- /demo/resizable.lua: -------------------------------------------------------------------------------- 1 | -- create empty memory 2 | resizable = memory.create() 3 | assert(memory.type(resizable) == "resizable") 4 | assert(memory.len(resizable) == 0) 5 | 6 | -- create memory with zeros 7 | memory.resize(resizable, 4) 8 | assert(memory.tostring(resizable) == "\0\0\0\0") 9 | 10 | -- increase memory with data 11 | memory.resize(resizable, 8, "\xff") 12 | assert(memory.tostring(resizable) == "\0\0\0\0\xff\xff\xff\xff") 13 | 14 | -- shrink memory 15 | memory.resize(resizable, 6) 16 | assert(memory.tostring(resizable) == "\0\0\0\0\xff\xff") 17 | 18 | -- reset to the contents of a string 19 | memory.resize(resizable, 0) 20 | memory.resize(resizable, 13, "Hello, world!") 21 | assert(memory.tostring(resizable) == "Hello, world!") 22 | -------------------------------------------------------------------------------- /doc/install.md: -------------------------------------------------------------------------------- 1 | Summary 2 | ======= 3 | 4 | - [UNIX](#unix) 5 | - [Windows](#windows) 6 | - [LuaRocks](#luarocks) 7 | 8 | --- 9 | 10 | UNIX 11 | ==== 12 | 13 | Read the [`Makefile`](../Makefile) for further details, 14 | but you can usually build and install the [C library](manual.md#c-library) and the [Lua module](manual.md#lua-module) using the following commands: 15 | 16 | ```shell 17 | make 18 | make install 19 | ``` 20 | 21 | Windows 22 | ======= 23 | 24 | Read the [`etc/Makefile.win`](../etc/Makefile.win) for further details, 25 | but you should be able to build and install the [C library](manual.md#c-library) and the [Lua module](manual.md#lua-module) using the `nmake` utility provided by Microsoft Visual C++. 26 | For instance, 27 | if your Lua is installed in `C:\Lua`, 28 | you can type the following commands in a Microsoft Visual C++ console: 29 | 30 | ```shell 31 | nmake /f etc/Makefile.win LUA_DIR=C:\Lua 32 | nmake /f etc/Makefile.win install INSTALL_DIR=C:\Lua 33 | ``` 34 | 35 | LuaRocks 36 | ======== 37 | 38 | Prior to install the [Lua module](manual.md#lua-module) as a rock, 39 | you should first build and install the [C library](manual.md#c-library). 40 | This way, 41 | you can provide the C library to [LuaRocks](https://luarocks.org/) as an [external dependency](https://github.com/luarocks/luarocks/wiki/Platform-agnostic-external-dependencies) to be used for building rocks. 42 | You can use the following `make` targets to build and install only the C library 43 | (the same targets are available for `nmake /f etc/Makefile.win` for [Windows installation](#windows)): 44 | 45 | ```shell 46 | make lib 47 | make install_lib 48 | ``` 49 | 50 | This will install the C library in `/usr/local` by default. 51 | Now you can install the Lua module as a rock using the provided [rockspec](../etc/luamemory-scm-1.rockspec): 52 | 53 | ```shell 54 | luarocks make etc/luamemory-scm-1.rockspec 55 | ``` 56 | -------------------------------------------------------------------------------- /doc/manual.md: -------------------------------------------------------------------------------- 1 | Summary 2 | ======= 3 | 4 | - [Lua Module](#lua-module) 5 | - [C Library](#c-library) 6 | - [Index](#index) 7 | 8 | --- 9 | 10 | Lua Module 11 | ========== 12 | 13 | Module `memory` provides generic functions for manipulation of writable memory areas. 14 | A memory can have a fixed size or be resizable. 15 | When indexing a memory, the first byte is at position 1 (not at 0, as in C). 16 | Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the memory. 17 | Thus, the last byte is at position -1, and so on. 18 | 19 | Unless stated otherwise, arguments `i` and `j` in the functions below are indices of memory or string `m`, 20 | and are corrected following the same rules of these arguments in function [`string.sub`](http://www.lua.org/manual/5.4/manual.html#pdf-string.sub). 21 | Moreover, 22 | when these arguments are optional, 23 | the default value for `i` is 1, 24 | and the default value for `j` is `-1` 25 | (which is the same as the length of `m`). 26 | 27 | This library provides all its functions inside the table `memory`. 28 | It also sets a metatable for the memory where the `__index` field points to the `memory` table. 29 | Therefore, you can use the library functions in object-oriented style. 30 | For instance, `memory.get(m,i)` can be written as `m:get(i)`, where `m` is a memory. 31 | Other metamethods provided for the memory are: 32 | 33 | - `__concat`: `v1..v2` produces a string with the concatenation of the contents of `v1` and `v2` if they are memory or string, 34 | or calls metamethod `__concat` of the other value if available. 35 | Otherwise raises an error. 36 | - `__len`: `#m` is equivalent to [`memory.len`](#memorylen-m)`(m)`. 37 | - `__tostring`: [`tostring`](http://www.lua.org/manual/5.4/manual.html#pdf-tostring)`(m)` is equivalent to [`memory.tostring`](#memorytostring-m--i--j)`(m)`. 38 | 39 | Finally, 40 | a resizable memory can be assigned to [to-be-closed](http://www.lua.org/manual/5.4/manual.html#3.3.8) variables. 41 | When closed, 42 | it becomes an empty external memory 43 | ([`memory.type`](#memorytype-m)`(m) == "other"`) 44 | with all its contents discarded, 45 | and it cannot be resized nor changed anymore. 46 | 47 | ### `memory.create ([m [, i [, j]]])` 48 | 49 | Returns a new memory. 50 | 51 | If `m` is a number, 52 | creates a new fixed-size memory of `m` bytes with value zero. 53 | 54 | If `m` is a string or a memory, 55 | creates a new fixed-size memory with the same size and contents of the portion of `m` from position `i` until position `j`. 56 | 57 | If `m` is not provided, 58 | a resizable memory of zero bytes (empty) is created. 59 | 60 | ### `memory.type (m)` 61 | 62 | Returns `"fixed"` if `m` is a fixed-size memory, 63 | or `"resizable"` if it is a resizable memory, 64 | or `"other"` if it is an external memory created using the C API. 65 | Otherwise it returns `nil`. 66 | 67 | ### `memory.len (m)` 68 | 69 | Returns the size of memory `m`. 70 | 71 | ### `memory.resize (m, l [, s])` 72 | 73 | Changes resizable memory `m` to contain `l` bytes. 74 | 75 | All the initial bytes that fit in the new size are preserved. 76 | Any extra bytes are set with the contents of string or memory `s` when it is provided 77 | (the contents from `s` are copied repeatedly until they fill all the extra bytes). 78 | Otherwise, 79 | the extra bytes are set to zero. 80 | 81 | ### `memory.diff (m1, m2)` 82 | 83 | Returns the index of the first byte which values differ in `m1` and `m2`, 84 | or `nil` if both contain the same bytes. 85 | 86 | It also returns the result of `m1 < m2` as if they were strings. 87 | 88 | `m1` and `m2` can be memory or string. 89 | 90 | ### `memory.tostring (m [, i [, j]])` 91 | 92 | Returns a string with the contents of memory or string `m` from `i` until `j`. 93 | 94 | ### `memory.get (m, i [, j])` 95 | 96 | Returns the values of bytes in memory `m` from `i` until `j`. 97 | The default value for `j` is `i`. 98 | 99 | If the range from `i` until `j` is empty, 100 | no values are returned. 101 | 102 | ### `memory.set (m, i, ...)` 103 | 104 | Sets the values of bytes in memory `m` from position `i` with values indicated by numbers received as arguments `...`. 105 | If there are more arguments than bytes in the range from `i` to the end of memory `m`, 106 | the extra arguments are ignored. 107 | 108 | ### `memory.find (m, s [, i [, j [, o]]])` 109 | 110 | 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` that fits in this range. 111 | 112 | `o` can also be negative (to count backwards, from the end of `s`). 113 | The default value for `o` is 1. 114 | If, after the translation of negative indice, `o` is less than 1, it is corrected to 1. 115 | 116 | If `i` is after `j` (empty range), 117 | or `o` refers to a position beyond the size of `s` (no contents), 118 | or the bytes from `s` are not found in `m`, 119 | then this function returns `nil`. 120 | Otherwise, it return the position of the first byte found in `m`. 121 | 122 | ### `memory.fill (m, s [, i [, j [, o]]])` 123 | 124 | 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`. 125 | 126 | Indice `o` follows the same rules as in function [`memory.find`](#memoryfind-m-s--i--j--o). 127 | 128 | If `i` is greater and `j` (empty range), 129 | or `o` refers to a position beyond the size of `s` (no contents), 130 | then this function has no effect. 131 | 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`). 132 | 133 | If `s` is a number then all bytes in the specified range of `m` are set with the value of `s`. 134 | The value of `o` is ignored in this case. 135 | 136 | ### `memory.pack (m, fmt, i, v...)` 137 | 138 | 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)). 139 | 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. 140 | 141 | ### `memory.unpack (m, fmt [, i])` 142 | 143 | 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-); 144 | The default value for `i` is 1. 145 | After the read values, this function also returns the index of the first unread byte in `m`. 146 | 147 | C Library 148 | ========= 149 | 150 | This section describes the C API provided as a separate library (`luamem`) to create and manipulate memory areas from C. 151 | All API functions and related types and constants are declared in the header file `luamem.h`. 152 | 153 | There are two distinct types of memory areas in the C API: 154 | 155 | - __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)). 156 | - __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)). 157 | 158 | __Warning__: unlike Lua strings, memory areas are not followed by a null byte (`'\0'`). 159 | 160 | ### `luamem_newalloc` 161 | 162 | ```C 163 | char *luamem_newalloc (lua_State *L, size_t len); 164 | ``` 165 | 166 | Creates and pushes onto the stack a new allocated memory with the given size, and returns its block address. 167 | 168 | 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)). 169 | 170 | ### `luamem_Unref` 171 | 172 | ```C 173 | typedef void (*luamem_Unref) (lua_State *L, void *mem, size_t len); 174 | ``` 175 | 176 | Type for memory unrefering functions. 177 | 178 | These functions are called whenever a referenced memory ceases to pointo to block address `mem` which have size of `len` bytes. (see [`luamem_resetref`](#luamem_resetref)). 179 | 180 | ### `luamem_newref` 181 | 182 | ```C 183 | void luamem_newref (lua_State *L); 184 | ``` 185 | 186 | Creates and pushes onto the stack a new referenced memory pointing to NULL, with length zero, and no unrefering function (see [`luamem_resetref`](#luamem_resetref)). 187 | 188 | Referenced memory areas uses a metatable created with name given by constant `LUAMEM_REF` (see [`luaL_newmetatable`](http://www.lua.org/manual/5.3/manual.html#luaL_newmetatable)). 189 | 190 | Moreover, 191 | a referenced memory is [closeable](http://www.lua.org/manual/5.4/manual.html#lua_closeslot). 192 | Closing a memory at index `idx` is equivalent to `luamem_setref(L, idx, NULL, 0, NULL)`. 193 | 194 | ### `luamem_setref` 195 | 196 | ```C 197 | int luamem_setref (lua_State *L, int idx, char *mem, size_t len, luamem_Unref unref); 198 | ``` 199 | 200 | Equivalent to `luamem_resetref(L, idx, mem, len, unref, 1)`. 201 | 202 | ### `luamem_resetref` 203 | 204 | ```C 205 | int luamem_resetref (lua_State *L, int idx, char *mem, size_t len, luamem_Unref unref, int cleanup); 206 | ``` 207 | 208 | Defines the block address (`mem`), size (`len`), and unrefering function (`unref`) of the referenced memory at index `idx`, and returns 1. 209 | If `idx` does not contain a referenced memory, it returns 0. 210 | 211 | If `unref` is not `NULL`, it will be called when the memory ceases to point to this block address, 212 | either by being garbage collected or if it is updated to point to another block address 213 | (by a future call of `luamem_setref` for instance). 214 | 215 | If `mem` points to the same block address currently pointed by referenced memory at index `idx`, 216 | or `cleanup` is zero, 217 | then the unrefering function previously registered is not invoked. 218 | 219 | ### `luamem_type` 220 | 221 | ```C 222 | int luamem_type(lua_State *L, int idx); 223 | ``` 224 | 225 | Returns `LUAMEM_TREF` if the value at the given index is a referenced memory, or `LUAMEM_TALLOC` in case of an allocated memory, or `LUAMEM_TNONE` otherwise. 226 | 227 | ### `luamem_ismemory` 228 | 229 | ```C 230 | int luamem_ismemory (lua_State *L, int idx); 231 | ``` 232 | 233 | Returns 1 if the value at the given index is a memory (allocated or referenced), and 0 otherwise. 234 | 235 | ### `luamem_tomemory` 236 | 237 | ```C 238 | char *luamem_tomemory (lua_State *L, int idx, size_t *len); 239 | ``` 240 | 241 | Equivalent to `luamem_tomemoryx(L, idx, len, NULL, NULL)`. 242 | 243 | ### `luamem_tomemoryx` 244 | 245 | ```C 246 | char *luamem_tomemoryx (lua_State *L, int idx, size_t *len, luamem_Unref *unref, int *type); 247 | ``` 248 | 249 | Return the block address of memory at the given index, or `NULL` if the value is not a memory. 250 | 251 | If `len` is not `NULL`, it sets `*len` with the memory size. 252 | If `unref` is not `NULL`, it sets `*unref` with the unrefering function if the value is a referenced memory, or `NULL` otherwise. 253 | If `type` is not `NULL`, it sets `*type` with the result of [`luamem_type`](#luamem_type)`(L, idx)`. 254 | 255 | 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. 256 | 257 | ### `luamem_checkmemory` 258 | 259 | ```C 260 | char *luamem_checkmemory (lua_State *L, int arg, size_t *len); 261 | ``` 262 | 263 | Checks whether the function argument `arg` is a memory (allocated or referenced) and returns a pointer to its contents; 264 | if `len` is not `NULL` fills `*len` with the memory's length. 265 | 266 | ### `luamem_isarray` 267 | 268 | ```C 269 | int luamem_isarray (lua_State *L, int idx); 270 | ``` 271 | 272 | Returns 1 if the value at the given index is a memory or string, and 0 otherwise. 273 | 274 | ### `luamem_toarray` 275 | 276 | ```C 277 | const char *luamem_toarray (lua_State *L, int idx, size_t *len); 278 | ``` 279 | 280 | If the value at the given index is a memory it behaves like [`luamem_tomemory`](#luamem_tomemory), but retuning a pointer to constant bytes. 281 | Otherwise, it is equivalent to [`lua_tolstring`](http://www.lua.org/manual/5.3/manual.html#lua_tolstring). 282 | 283 | ### `luamem_asarray` 284 | 285 | ```C 286 | const char *luamem_asarray (lua_State *L, int idx, size_t *len); 287 | ``` 288 | 289 | If the value at the given index is a memory it behaves like [`luamem_tomemory`](#luamem_tomemory), but retuning a pointer to constant bytes. 290 | Otherwise, it is equivalent to [`luaL_tolstring`](http://www.lua.org/manual/5.3/manual.html#luaL_tolstring). 291 | 292 | ### `luamem_checkarray` 293 | 294 | ```C 295 | const char *luamem_checkarray (lua_State *L, int arg, size_t *len); 296 | ``` 297 | 298 | Checks whether the function argument `arg` is a memory or string and returns a pointer to its contents; 299 | if `len` is not `NULL` fills `*len` with the contents' length. 300 | 301 | This function might use [`lua_tolstring`](http://www.lua.org/manual/5.3/manual.html#lua_tolstring) to get its result, so all conversions and caveats of that function apply here. 302 | 303 | ### `luamem_checklenarg` 304 | 305 | ```C 306 | size_t luamem_checklenarg (lua_State *L, int arg); 307 | ``` 308 | 309 | 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`. 310 | 311 | ### `luamem_realloc` 312 | 313 | ```C 314 | void *luamem_realloc (lua_State *L, void *mem, size_t old, size_t new); 315 | ``` 316 | 317 | 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)). 318 | Returns the reallocated memory. 319 | 320 | ### `luamem_free` 321 | 322 | ```C 323 | void luamem_free (lua_State *L, void *mem, size_t sz); 324 | ``` 325 | 326 | Equivalent to `luamem_realloc(L, mem, sz, 0)`. 327 | 328 | __Note__: any referenced memory which uses this function as the unrefering function is considered a resizable memory by the `memory` module. 329 | 330 | ### `luamem_addvalue` 331 | 332 | ```C 333 | void luamem_addvalue (luaL_Buffer *B); 334 | ``` 335 | 336 | 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. 337 | 338 | ### `luamem_pushresult` 339 | 340 | ```C 341 | void luamem_pushresult (luaL_Buffer *B); 342 | ``` 343 | 344 | 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. 345 | 346 | ### `luamem_pushresultsize` 347 | 348 | ```C 349 | void luamem_pushresultsize (luaL_Buffer *B, size_t sz); 350 | ``` 351 | 352 | Equivalent to the sequence [`luaL_addsize`](http://www.lua.org/manual/5.3/manual.html#luaL_addsize), [`luamem_pushresult`](#luamem_pushresult). 353 | 354 | Index 355 | ===== 356 | 357 | [Lua functions](#lua-module) | [C API](#c-library) | [C API](#c-library) 358 | ---|---|--- 359 | [`memory.create`](#memorycreate-m--i--j) | [`LUAMEM_ALLOC`](#luamem_newalloc) | [`luamem_free`](#luamem_free) 360 | [`memory.diff`](#memorydiff-m1-m2) | [`LUAMEM_REF`](#luamem_newref) | [`luamem_isarray`](#luamem_isarray) 361 | [`memory.fill`](#memoryfill-m-s--i--j--o) | [`LUAMEM_TALLOC`](#luamem_tomemoryx) | [`luamem_ismemory`](#luamem_ismemory) 362 | [`memory.find`](#memoryfind-m-s--i--j--o) | [`LUAMEM_TNONE`](#luamem_tomemoryx) | [`luamem_newalloc`](#luamem_newalloc) 363 | [`memory.get`](#memoryget-m-i--j) | [`LUAMEM_TREF`](#luamem_tomemoryx) | [`luamem_newref`](#luamem_newref) 364 | [`memory.len`](#memorylen-m) | | [`luamem_realloc`](#luamem_realloc) 365 | [`memory.pack`](#memorypack-m-fmt-i-v) | [`luamem_Unref`](#luamem_unref) | [`luamem_resetref`](#luamem_resetref) 366 | [`memory.resize`](#memoryresize-m-l--s) | [`luamem_addvalue`](#luamem_addvalue) | [`luamem_setref`](#luamem_setref) 367 | [`memory.set`](#memoryset-m-i-) | [`luamem_asarray`](#luamem_asarray) | [`luamem_toarray`](#luamem_toarray) 368 | [`memory.tostring`](#memorytostring-m--i--j) | [`luamem_checkarray`](#luamem_checkarray) | [`luamem_tomemory`](#luamem_tomemory) 369 | [`memory.type`](#memorytype-m) | [`luamem_checklenarg`](#luamem_checklenarg) | [`luamem_tomemoryx`](#luamem_tomemoryx) 370 | [`memory.unpack`](#memoryunpack-m-fmt--i) | [`luamem_checkmemory`](#luamem_checkmemory) | [`luamem_type`](#luamem_type) 371 | -------------------------------------------------------------------------------- /etc/Makefile.win: -------------------------------------------------------------------------------- 1 | # See ../README.md for installation and 2 | # == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= 3 | 4 | LUA_DIR= C:\Installed 5 | LUA_INCDIR= $(LUA_DIR)\include 6 | LUA_LIBDIR= $(LUA_DIR)\lib 7 | LUA_LIB= lua.lib 8 | 9 | INSTALL_DIR= $(LUA_DIR) 10 | INSTALL_BINDIR= $(INSTALL_DIR)\bin 11 | INSTALL_INCDIR= $(INSTALL_DIR)\include 12 | INSTALL_LIBDIR= $(INSTALL_DIR)\lib 13 | INSTALL_CMODDIR= $(INSTALL_DIR)\lib\lua\5.4 14 | 15 | MOD_L= memory 16 | LIB_L= luamem 17 | 18 | CFLAGS= /Isrc /I$(LUA_INCDIR) $(SYSCFLAGS) $(MYCFLAGS) 19 | LDFLAGS= /LIBPATH:$(LUA_LIBDIR) $(SYSLDFLAGS) $(MYLDFLAGS) 20 | LIBS= $(LUA_LIB) $(SYSLIBS) $(MYLIBS) 21 | 22 | LINK= link.exe 23 | CC= cl.exe 24 | RM= del 25 | COPY= copy 26 | MKDIR= mkdir 27 | 28 | SYSCFLAGS= 29 | SYSLDFLAGS= 30 | SYSLIBS= 31 | 32 | MYCFLAGS= 33 | MYLDFLAGS= 34 | MYLIBS= 35 | 36 | # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= 37 | 38 | MOD_O= lmemlib.obj 39 | LIB_O= luamem.obj 40 | MOD_T= $(MOD_L).dll 41 | LIB_T= $(LIB_L).dll 42 | LIB_A= $(LIB_T) $(LIB_L).dll.manifest $(LIB_L).exp $(LIB_L).lib 43 | MOD_A= $(MOD_T) $(MOD_L).dll.manifest $(MOD_L).exp $(MOD_L).lib 44 | 45 | ALL_O= $(LIB_O) $(MOD_O) 46 | ALL_T= $(LIB_T) $(MOD_T) 47 | ALL_A= $(LIB_A) $(MOD_A) 48 | 49 | {src\}.c.obj: 50 | $(CC) $(CFLAGS) /c $< 51 | 52 | # Targets start here. 53 | all: $(ALL_T) 54 | 55 | obj: $(ALL_O) 56 | 57 | lib: $(LIB_T) 58 | 59 | mod: $(MOD_T) 60 | 61 | $(MOD_T): $(MOD_O) $(LIB_T) 62 | $(LINK) /dll /def:etc\memory.def /out:$@ $(LDFLAGS) $(MOD_O) $(LIB_L).lib $(LIBS) 63 | 64 | $(LIB_T): $(LIB_O) 65 | $(LINK) /dll /def:etc\luamem.def /out:$@ $(LDFLAGS) $** $(LIBS) 66 | 67 | clean: 68 | $(RM) $(ALL_A) $(ALL_O) 69 | 70 | echo: 71 | @echo "CC= $(CC)" 72 | @echo "CFLAGS= $(CFLAGS)" 73 | @echo "LDFLAGS= $(LDFLAGS)" 74 | @echo "LIBS= $(LIBS)" 75 | @echo "LINK= $(LINK)" 76 | @echo "RM= $(RM)" 77 | @echo "MKDIR= $(MKDIR)" 78 | @echo "INSTALL_BINDIR= $(INSTALL_BINDIR)" 79 | @echo "INSTALL_INCDIR= $(INSTALL_INCDIR)" 80 | @echo "INSTALL_LIBDIR= $(INSTALL_LIBDIR)" 81 | @echo "INSTALL_CMODDIR= $(INSTALL_CMODDIR)" 82 | 83 | $(INSTALL_BINDIR) $(INSTALL_INCDIR) $(INSTALL_LIBDIR) $(INSTALL_CMODDIR): 84 | $(MKDIR) $@ 85 | 86 | install: install_lib install_mod 87 | 88 | install_lib: $(INSTALL_BINDIR) $(INSTALL_INCDIR) $(INSTALL_LIBDIR) $(LIB_T) 89 | $(COPY) src\luamem.h $(INSTALL_INCDIR) 90 | $(COPY) $(LIB_L).lib $(INSTALL_LIBDIR) 91 | $(COPY) $(LIB_T) $(INSTALL_BINDIR) 92 | 93 | install_mod: $(INSTALL_CMODDIR) $(MOD_T) 94 | $(COPY) $(MOD_T) $(INSTALL_CMODDIR) 95 | 96 | # list targets that do not create files (but not all makes understand .PHONY) 97 | .PHONY: all obj lib mod clean echo install 98 | -------------------------------------------------------------------------------- /etc/luamem.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | luamem_newalloc 3 | luamem_newref 4 | luamem_setref 5 | luamem_type 6 | luamem_tomemoryx 7 | luamem_checkmemory 8 | luamem_isarray 9 | luamem_toarray 10 | luamem_checkarray 11 | luamem_optarray 12 | luamem_realloc 13 | luamem_free 14 | luamem_checklenarg 15 | luamem_addvalue 16 | luamem_pushresult 17 | luamem_pushresultsize 18 | -------------------------------------------------------------------------------- /etc/memory-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package="memory" 2 | version="scm-1" 3 | source = { 4 | url = "git://github.com/renatomaia/lua-memory", 5 | } 6 | description = { 7 | summary = "Manipulation of writable memory areas in Lua", 8 | detailed = [[ 9 | Memory areas are much like Lua strings, but their contents can be 10 | modified in place and have an identity (selfness) independent from 11 | their contents. 12 | ]], 13 | homepage = "https://github.com/renatomaia/lua-memory", 14 | license = "MIT/X11" 15 | } 16 | dependencies = { 17 | "lua >= 5.4", 18 | } 19 | external_dependencies = { 20 | LUAMEM = { 21 | header = "luamem.h", 22 | library = "luamem", 23 | }, 24 | } 25 | build = { 26 | type = "builtin", 27 | modules = { 28 | memory = { 29 | sources = "src/lmemlib.c", 30 | libdirs = "$(LUAMEM_LIBDIR)", 31 | incdirs = "$(LUAMEM_INCDIR)", 32 | libraries = external_dependencies.LUAMEM.library, 33 | }, 34 | }, 35 | } 36 | -------------------------------------------------------------------------------- /etc/memory.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | luaopen_memory 3 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= 2 | 3 | LUA_DIR= /usr/local 4 | LUA_INCDIR= $(LUA_DIR)/include 5 | 6 | # Your platform. See PLATS for possible values. 7 | PLAT= guess 8 | 9 | CC= gcc -std=gnu99 10 | CFLAGS= -O2 -Wall -I$(LUA_INCDIR) $(SYSCFLAGS) $(MYCFLAGS) 11 | LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS) 12 | LIBS= $(SYSLIBS) $(MYLIBS) 13 | 14 | AR= ar rcu 15 | RANLIB= ranlib 16 | RM= rm -f 17 | UNAME= uname 18 | 19 | SYSCFLAGS= 20 | SYSLDFLAGS= 21 | SYSLIBS= 22 | 23 | MYCFLAGS= 24 | MYLDFLAGS= 25 | MYLIBS= 26 | 27 | # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= 28 | 29 | PLATS= guess generic linux macosx solaris 30 | 31 | MEM_O= lmemlib.o 32 | API_O= luamem.o 33 | 34 | MEM_M= memory.so 35 | API_S= libluamem.so 36 | LIB_A= libluamemory.a 37 | 38 | ALL_O= $(MEM_O) $(API_O) 39 | ALL_A= $(LIB_A) 40 | ALL_S= $(API_S) 41 | ALL_M= $(MEM_M) 42 | ALL_T= $(ALL_A) $(ALL_S) $(ALL_M) 43 | 44 | default: $(PLAT) 45 | 46 | all: $(ALL_T) 47 | 48 | o: $(ALL_O) 49 | 50 | a: $(ALL_A) 51 | 52 | so: $(ALL_S) 53 | 54 | lib: a so 55 | 56 | mod: $(ALL_M) 57 | 58 | $(API_S): $(API_O) 59 | $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) 60 | 61 | $(MEM_M): $(MEM_O) $(API_S) 62 | $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) 63 | 64 | $(LIB_A): $(ALL_O) 65 | $(AR) $@ $^ 66 | $(RANLIB) $@ 67 | 68 | clean: 69 | $(RM) $(ALL_T) $(ALL_O) 70 | 71 | depend: 72 | @$(CC) $(CFLAGS) -MM l*.c 73 | 74 | echo: 75 | @echo "PLAT= $(PLAT)" 76 | @echo "CC= $(CC)" 77 | @echo "CFLAGS= $(CFLAGS)" 78 | @echo "LDFLAGS= $(SYSLDFLAGS)" 79 | @echo "LIBS= $(LIBS)" 80 | @echo "AR= $(AR)" 81 | @echo "RANLIB= $(RANLIB)" 82 | @echo "RM= $(RM)" 83 | @echo "UNAME= $(UNAME)" 84 | 85 | # Convenience targets for usual platforms 86 | ALL= all 87 | 88 | help: 89 | @echo "Do 'make PLATFORM' where PLATFORM is one of these:" 90 | @echo " $(PLATS)" 91 | 92 | guess: 93 | @echo Guessing `$(UNAME)` 94 | @$(MAKE) `$(UNAME)` 95 | 96 | generic: $(ALL) 97 | 98 | Linux linux: 99 | $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -fpic" \ 100 | SYSLDFLAGS="-shared" 101 | 102 | Darwin macos macosx: 103 | $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX -fno-common" \ 104 | SYSLDFLAGS="-bundle -undefined dynamic_lookup" \ 105 | CC='export MACOSX_DEPLOYMENT_TARGET="10.10"; gcc' \ 106 | LD='export MACOSX_DEPLOYMENT_TARGET="10.10"; gcc' 107 | 108 | SunOS solaris: 109 | $(MAKE) $(ALL) SYSCFLAGS="-fpic" SYSLDFLAGS="-O -shared" 110 | 111 | # (end of Makefile) 112 | -------------------------------------------------------------------------------- /src/lmemlib.c: -------------------------------------------------------------------------------- 1 | #define lmemlib_c 2 | #define LUA_LIB 3 | 4 | #include "luamem.h" 5 | 6 | #include 7 | #include 8 | 9 | static size_t posrelatI (lua_Integer pos, size_t len); 10 | static size_t getendpos (lua_State *L, int arg, lua_Integer def, size_t len); 11 | static int str2byte (lua_State *L, const char *s, size_t l); 12 | static void code2char (lua_State *L, int idx, char *p, size_t n); 13 | static const char *lmemfind (const char *s1, size_t l1, 14 | const char *s2, size_t l2); 15 | 16 | static int mem_create (lua_State *L) { 17 | if (lua_gettop(L) == 0) { 18 | luamem_newref(L); 19 | luamem_resetref(L, 1, NULL, 0, luamem_free, 0); 20 | } else { 21 | char *p; 22 | size_t len; 23 | const char *s = NULL; 24 | if (lua_type(L, 1) == LUA_TNUMBER) { 25 | len = luamem_checklenarg(L, 1); 26 | } else { 27 | size_t posi, pose; 28 | s = luamem_checkarray(L, 1, &len); 29 | posi = posrelatI(luaL_optinteger(L, 2, 1), len); 30 | pose = getendpos(L, 3, -1, len); 31 | if (posi > pose) { 32 | len = 0; 33 | } else { 34 | len = (pose-posi)+1; 35 | if (posi+len <= pose) /* arithmetic overflow? */ 36 | return luaL_error(L, "string slice too long"); 37 | s += posi-1; 38 | } 39 | } 40 | p = luamem_newalloc(L, len); 41 | if (s) memcpy(p, s, len*sizeof(char)); 42 | else memset(p, 0, len*sizeof(char)); 43 | } 44 | return 1; 45 | } 46 | 47 | static void memfill (char *mem, size_t size, const char *s, size_t len) { 48 | do { 49 | size_t n = size < len ? size : len; 50 | memmove(mem, s, n*sizeof(char)); 51 | mem += n; 52 | size -= n; 53 | } while (size > 0); 54 | } 55 | 56 | static int mem_resize (lua_State *L) { 57 | size_t len; 58 | luamem_Unref unref; 59 | char *mem = luamem_tomemoryx(L, 1, &len, &unref, NULL); 60 | size_t size = luamem_checklenarg(L, 2); 61 | luaL_argcheck(L, unref == luamem_free, 1, "resizable memory expected"); 62 | if (len != size) { 63 | size_t sl, n = len < size ? size-len : 0; 64 | const char *s = luamem_optarray(L, 3, NULL, &sl); 65 | char *resized = (char *)luamem_realloc(L, mem, len, size); 66 | if (size && !resized) return luaL_error(L, "out of memory"); 67 | luamem_resetref(L, 1, resized, size, luamem_free, 0 /* don't free 'mem' */); 68 | if (n) { 69 | resized += len; 70 | if (sl) memfill(resized, n, s, sl); 71 | else memset(resized, 0, n*sizeof(char)); 72 | } 73 | } 74 | return 0; 75 | } 76 | 77 | static int mem_type (lua_State *L) { 78 | luamem_Unref unref; 79 | int type; 80 | luamem_tomemoryx(L, 1, NULL, &unref, &type); 81 | if (type == LUAMEM_TALLOC) { 82 | lua_pushliteral(L, "fixed"); 83 | } else if (type == LUAMEM_TREF) { 84 | if (unref == luamem_free) lua_pushliteral(L, "resizable"); 85 | else lua_pushliteral(L, "other"); 86 | } else { 87 | lua_pushnil(L); 88 | } 89 | return 1; 90 | } 91 | 92 | static int mem_len (lua_State *L) { 93 | size_t len; 94 | luamem_checkmemory(L, 1, &len); 95 | lua_pushinteger(L, (lua_Integer)len); 96 | return 1; 97 | } 98 | 99 | static int mem_tostring (lua_State *L) { 100 | size_t len; 101 | const char *s = luamem_checkarray(L, 1, &len); 102 | size_t start = posrelatI(luaL_optinteger(L, 2, 1), len); 103 | size_t end = getendpos(L, 3, -1, len); 104 | if (start <= end) lua_pushlstring(L, s+start-1, (end-start)+1); 105 | else lua_pushliteral(L, ""); 106 | return 1; 107 | } 108 | 109 | static int mem_diff (lua_State *L) { 110 | size_t l1, l2; 111 | const char *s1 = luamem_checkarray(L, 1, &l1); 112 | const char *s2 = luamem_checkarray(L, 2, &l2); 113 | size_t i, n=(l1 0) 303 | return (size_t)pos; 304 | else if (pos == 0) 305 | return 1; 306 | else if (pos < -(lua_Integer)len) /* inverted comparison */ 307 | return 1; /* clip to 1 */ 308 | else return len + (size_t)pos + 1; 309 | } 310 | 311 | /* 312 | ** Gets an optional ending string position from argument 'arg', 313 | ** with default value 'def'. 314 | ** Negative means back from end: clip result to [0, len] 315 | */ 316 | static size_t getendpos (lua_State *L, int arg, lua_Integer def, 317 | size_t len) { 318 | lua_Integer pos = luaL_optinteger(L, arg, def); 319 | if (pos > (lua_Integer)len) 320 | return len; 321 | else if (pos >= 0) 322 | return (size_t)pos; 323 | else if (pos < -(lua_Integer)len) 324 | return 0; 325 | else return len + (size_t)pos + 1; 326 | } 327 | 328 | static int str2byte (lua_State *L, const char *s, size_t l) { 329 | lua_Integer pi = luaL_checkinteger(L, 2); 330 | size_t posi = posrelatI(pi, l); 331 | size_t pose = getendpos(L, 3, pi, l); 332 | int n, i; 333 | if (posi > pose) return 0; /* empty interval; return no values */ 334 | if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */ 335 | return luaL_error(L, "string slice too long"); 336 | n = (int)(pose - posi) + 1; 337 | luaL_checkstack(L, n, "string slice too long"); 338 | for (i=0; i l1) return NULL; /* avoids a negative 'l1' */ 356 | else { 357 | const char *init; /* to search for a '*s2' inside 's1' */ 358 | l2--; /* 1st char will be checked by 'memchr' */ 359 | l1 = l1-l2; /* 's2' cannot be found after that */ 360 | while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { 361 | init++; /* 1st char is already checked */ 362 | if (memcmp(init, s2+1, l2) == 0) 363 | return init-1; 364 | else { /* correct 'l1' and 's1' to try again */ 365 | l1 -= init-s1; 366 | s1 = init; 367 | } 368 | } 369 | return NULL; /* not found */ 370 | } 371 | } 372 | 373 | /* 374 | ** {====================================================== 375 | ** PACK/UNPACK 376 | ** ======================================================= 377 | */ 378 | 379 | /* value used for padding */ 380 | #if !defined(LUAL_PACKPADBYTE) 381 | #define LUAL_PACKPADBYTE 0x00 382 | #endif 383 | 384 | /* maximum size for the binary representation of an integer */ 385 | #define MAXINTSIZE 16 386 | 387 | /* number of bits in a character */ 388 | #define NB CHAR_BIT 389 | 390 | /* mask for one character (NB 1's) */ 391 | #define MC ((1 << NB) - 1) 392 | 393 | /* size of a lua_Integer */ 394 | #define SZINT ((int)sizeof(lua_Integer)) 395 | 396 | 397 | /* dummy union to get native endianness */ 398 | static const union { 399 | int dummy; 400 | char little; /* true iff machine is little endian */ 401 | } nativeendian = {1}; 402 | 403 | 404 | /* dummy structure to get native alignment requirements */ 405 | struct cD { 406 | char c; 407 | union { double d; void *p; lua_Integer i; lua_Number n; } u; 408 | }; 409 | 410 | #define MAXALIGN (offsetof(struct cD, u)) 411 | 412 | 413 | /* 414 | ** Union for serializing floats 415 | */ 416 | typedef union Ftypes { 417 | float f; 418 | double d; 419 | lua_Number n; 420 | char buff[5 * sizeof(lua_Number)]; /* enough for any float type */ 421 | } Ftypes; 422 | 423 | 424 | /* 425 | ** information to pack/unpack stuff 426 | */ 427 | typedef struct Header { 428 | lua_State *L; 429 | int islittle; 430 | int maxalign; 431 | } Header; 432 | 433 | 434 | /* 435 | ** options for pack/unpack 436 | */ 437 | typedef enum KOption { 438 | Kint, /* signed integers */ 439 | Kuint, /* unsigned integers */ 440 | Kfloat, /* floating-point numbers */ 441 | Kchar, /* fixed-length strings */ 442 | Kstring, /* strings with prefixed length */ 443 | Kzstr, /* zero-terminated strings */ 444 | Kpadding, /* padding */ 445 | Kpaddalign, /* padding for alignment */ 446 | Knop /* no-op (configuration or spaces) */ 447 | } KOption; 448 | 449 | 450 | /* 451 | ** Read an integer numeral from string 'fmt' or return 'df' if 452 | ** there is no numeral 453 | */ 454 | static int digit (int c) { return '0' <= c && c <= '9'; } 455 | 456 | static int getnum (const char **fmt, int df) { 457 | if (!digit(**fmt)) /* no number? */ 458 | return df; /* return default value */ 459 | else { 460 | int a = 0; 461 | do { 462 | a = a*10 + (*((*fmt)++) - '0'); 463 | } while (digit(**fmt) && a <= ((int)LUAMEM_MAXSIZE - 9)/10); 464 | return a; 465 | } 466 | } 467 | 468 | 469 | /* 470 | ** Read an integer numeral and raises an error if it is larger 471 | ** than the maximum size for integers. 472 | */ 473 | static int getnumlimit (Header *h, const char **fmt, int df) { 474 | int sz = getnum(fmt, df); 475 | if (sz > MAXINTSIZE || sz <= 0) 476 | return luaL_error(h->L, "integral size (%d) out of limits [1,%d]", 477 | sz, MAXINTSIZE); 478 | return sz; 479 | } 480 | 481 | 482 | /* 483 | ** Initialize Header 484 | */ 485 | static void initheader (lua_State *L, Header *h) { 486 | h->L = L; 487 | h->islittle = nativeendian.little; 488 | h->maxalign = 1; 489 | } 490 | 491 | 492 | /* 493 | ** Read and classify next option. 'size' is filled with option's size. 494 | */ 495 | static KOption getoption (Header *h, const char **fmt, int *size) { 496 | int opt = *((*fmt)++); 497 | *size = 0; /* default */ 498 | switch (opt) { 499 | case 'b': *size = sizeof(char); return Kint; 500 | case 'B': *size = sizeof(char); return Kuint; 501 | case 'h': *size = sizeof(short); return Kint; 502 | case 'H': *size = sizeof(short); return Kuint; 503 | case 'l': *size = sizeof(long); return Kint; 504 | case 'L': *size = sizeof(long); return Kuint; 505 | case 'j': *size = sizeof(lua_Integer); return Kint; 506 | case 'J': *size = sizeof(lua_Integer); return Kuint; 507 | case 'T': *size = sizeof(size_t); return Kuint; 508 | case 'f': *size = sizeof(float); return Kfloat; 509 | case 'd': *size = sizeof(double); return Kfloat; 510 | case 'n': *size = sizeof(lua_Number); return Kfloat; 511 | case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint; 512 | case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; 513 | case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; 514 | case 'c': 515 | *size = getnum(fmt, -1); 516 | if (*size == -1) 517 | luaL_error(h->L, "missing size for format option 'c'"); 518 | return Kchar; 519 | case 'z': return Kzstr; 520 | case 'x': *size = 1; return Kpadding; 521 | case 'X': return Kpaddalign; 522 | case ' ': break; 523 | case '<': h->islittle = 1; break; 524 | case '>': h->islittle = 0; break; 525 | case '=': h->islittle = nativeendian.little; break; 526 | case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break; 527 | default: luaL_error(h->L, "invalid format option '%c'", opt); 528 | } 529 | return Knop; 530 | } 531 | 532 | 533 | /* 534 | ** Read, classify, and fill other details about the next option. 535 | ** 'psize' is filled with option's size, 'notoalign' with its 536 | ** alignment requirements. 537 | ** Local variable 'size' gets the size to be aligned. (Kpadal option 538 | ** always gets its full alignment, other options are limited by 539 | ** the maximum alignment ('maxalign'). Kchar option needs no alignment 540 | ** despite its size. 541 | */ 542 | static KOption getdetails (Header *h, size_t totalsize, 543 | const char **fmt, int *psize, int *ntoalign) { 544 | KOption opt = getoption(h, fmt, psize); 545 | int align = *psize; /* usually, alignment follows size */ 546 | if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ 547 | if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) 548 | luaL_argerror(h->L, 1, "invalid next option for option 'X'"); 549 | } 550 | if (align <= 1 || opt == Kchar) /* need no alignment? */ 551 | *ntoalign = 0; 552 | else { 553 | if (align > h->maxalign) /* enforce maximum alignment */ 554 | align = h->maxalign; 555 | if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */ 556 | luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); 557 | *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); 558 | } 559 | return opt; 560 | } 561 | 562 | 563 | static char *getbytes (char **b, size_t *i, size_t lb, size_t sz) { 564 | size_t newtotal = *i+sz; 565 | if (newtotal<=lb) { 566 | char *res = *b; 567 | *b += sz; 568 | *i = newtotal; 569 | return res; 570 | } 571 | return NULL; 572 | } 573 | 574 | static int packfailed (lua_State *L, size_t i, size_t arg) { 575 | lua_pushboolean(L, 0); 576 | lua_replace(L, arg-2); 577 | lua_pushinteger(L, i+1); 578 | lua_replace(L, arg-1); 579 | return 3+lua_gettop(L)-arg; 580 | } 581 | 582 | static int packchar (char **b, size_t *i, size_t lb, const char c) { 583 | if (*i 0) { /* avoid 'memcpy' when 's' can be NULL */ 594 | char *d = getbytes(b, i, lb, sl); 595 | if (d == NULL) return 0; 596 | memcpy(d, s, sl * sizeof(char)); 597 | } 598 | return 1; 599 | } 600 | 601 | 602 | /* 603 | ** Pack integer 'n' with 'size' bytes and 'islittle' endianness. 604 | ** The final 'if' handles the case when 'size' is larger than 605 | ** the size of a Lua integer, correcting the extra sign-extension 606 | ** bytes if necessary (by default they would be zeros). 607 | */ 608 | static int packint (char **b, size_t *pos, size_t lb, 609 | lua_Unsigned n, int islittle, int size, int neg) { 610 | char *buff = getbytes(b, pos, lb, size); 611 | if (buff) { 612 | int i; 613 | buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ 614 | for (i = 1; i < size; i++) { 615 | n >>= NB; 616 | buff[islittle ? i : size - 1 - i] = (char)(n & MC); 617 | } 618 | if (neg && size > SZINT) { /* negative number need sign extension? */ 619 | for (i = SZINT; i < size; i++) /* correct extra bytes */ 620 | buff[islittle ? i : size - 1 - i] = (char)MC; 621 | } 622 | return 1; 623 | } 624 | return 0; 625 | } 626 | 627 | 628 | /* 629 | ** Copy 'size' bytes from 'src' to 'dest', correcting endianness if 630 | ** given 'islittle' is different from native endianness. 631 | */ 632 | static void copywithendian (volatile char *dest, volatile const char *src, 633 | int size, int islittle) { 634 | if (islittle == nativeendian.little) { 635 | while (size-- != 0) 636 | *(dest++) = *(src++); 637 | } 638 | else { 639 | dest += size - 1; 640 | while (size-- != 0) 641 | *(dest--) = *(src++); 642 | } 643 | } 644 | 645 | static int mem_pack (lua_State *L) { 646 | Header h; 647 | size_t lb; 648 | char *mem = luamem_checkmemory(L, 1, &lb); 649 | const char *fmt = luaL_checkstring(L, 2); /* format string */ 650 | size_t i = posrelatI(luaL_checkinteger(L, 3), lb) - 1; 651 | int arg = 3; /* current argument to pack */ 652 | luaL_argcheck(L, i <= lb, 3, "index out of bounds"); 653 | initheader(L, &h); 654 | mem += i; 655 | while (*fmt != '\0') { 656 | int size, ntoalign; 657 | KOption opt = getdetails(&h, i, &fmt, &size, &ntoalign); 658 | arg++; 659 | if (!getbytes(&mem, &i, lb, ntoalign)) /* skip alignment */ 660 | return packfailed(L, i, arg); 661 | switch (opt) { 662 | case Kint: { /* signed integers */ 663 | lua_Integer n = luaL_checkinteger(L, arg); 664 | if (size < SZINT) { /* need overflow check? */ 665 | lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); 666 | luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); 667 | } 668 | if (!packint(&mem, &i, lb, (lua_Unsigned)n, h.islittle, size, (n < 0))) 669 | return packfailed(L, i, arg); 670 | break; 671 | } 672 | case Kuint: { /* unsigned integers */ 673 | lua_Integer n = luaL_checkinteger(L, arg); 674 | if (size < SZINT) /* need overflow check? */ 675 | luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), 676 | arg, "unsigned overflow"); 677 | if (!packint(&mem, &i, lb, (lua_Unsigned)n, h.islittle, size, 0)) 678 | return packfailed(L, i, arg); 679 | break; 680 | } 681 | case Kfloat: { /* floating-point options */ 682 | volatile Ftypes u; 683 | lua_Number n; 684 | char *data = getbytes(&mem, &i, lb, size); 685 | if (!data) return packfailed(L, i, arg); 686 | n = luaL_checknumber(L, arg); /* get argument */ 687 | if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ 688 | else if (size == sizeof(u.d)) u.d = (double)n; 689 | else u.n = n; 690 | /* move 'u' to final result, correcting endianness if needed */ 691 | copywithendian(data, u.buff, size, h.islittle); 692 | break; 693 | } 694 | case Kchar: { /* fixed-size string */ 695 | size_t len; 696 | const char *s = luamem_checkarray(L, arg, &len); 697 | luaL_argcheck(L, len == (size_t)size, arg, "wrong length"); 698 | if (!packstream(&mem, &i, lb, s, size)) 699 | return packfailed(L, i, arg); 700 | break; 701 | } 702 | case Kstring: { /* strings with length count */ 703 | size_t len; 704 | const char *s = luamem_checkarray(L, arg, &len); 705 | luaL_argcheck(L, size >= (int)sizeof(size_t) || 706 | len < ((size_t)1 << (size * NB)), 707 | arg, "string length does not fit in given size"); 708 | if (!packint(&mem, &i, lb, (lua_Unsigned)len, h.islittle, size, 0) || /* pack length */ 709 | !packstream(&mem, &i, lb, s, len)) 710 | return packfailed(L, i, arg); 711 | break; 712 | } 713 | case Kzstr: { /* zero-terminated string */ 714 | size_t len; 715 | const char *s = luamem_checkarray(L, arg, &len); 716 | luaL_argcheck(L, memchr(s, '\0', len) == NULL, arg, 717 | "string contains zeros"); 718 | if (!packstream(&mem, &i, lb, s, len) || !packchar(&mem, &i, lb, '\0')) 719 | return packfailed(L, i, arg); 720 | break; 721 | } 722 | case Kpadding: { 723 | if (!getbytes(&mem, &i, lb, 1)) 724 | return packfailed(L, i, arg); 725 | } /* FALLTHROUGH */ 726 | case Kpaddalign: case Knop: 727 | arg--; /* undo increment */ 728 | break; 729 | } 730 | } 731 | lua_pushboolean(L, 1); 732 | lua_pushinteger(L, i+1); 733 | return 2; 734 | } 735 | 736 | 737 | /* 738 | ** Unpack an integer with 'size' bytes and 'islittle' endianness. 739 | ** If size is smaller than the size of a Lua integer and integer 740 | ** is signed, must do sign extension (propagating the sign to the 741 | ** higher bits); if size is larger than the size of a Lua integer, 742 | ** it must check the unread bytes to see whether they do not cause an 743 | ** overflow. 744 | */ 745 | static lua_Integer unpackint (lua_State *L, const char *str, 746 | int islittle, int size, int issigned) { 747 | lua_Unsigned res = 0; 748 | int i; 749 | int limit = (size <= SZINT) ? size : SZINT; 750 | for (i = limit - 1; i >= 0; i--) { 751 | res <<= NB; 752 | res |= (lua_Unsigned)uchar(str[islittle ? i : size - 1 - i]); 753 | } 754 | if (size < SZINT) { /* real size smaller than lua_Integer? */ 755 | if (issigned) { /* needs sign extension? */ 756 | lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); 757 | res = ((res ^ mask) - mask); /* do sign extension */ 758 | } 759 | } 760 | else if (size > SZINT) { /* must check unread bytes */ 761 | int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; 762 | for (i = limit; i < size; i++) { 763 | if (uchar(str[islittle ? i : size - 1 - i]) != mask) 764 | luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); 765 | } 766 | } 767 | return (lua_Integer)res; 768 | } 769 | 770 | 771 | static int mem_unpack (lua_State *L) { 772 | Header h; 773 | size_t ld; 774 | const char *data = luamem_checkmemory(L, 1, &ld); 775 | const char *fmt = luaL_checkstring(L, 2); 776 | size_t pos = posrelatI(luaL_optinteger(L, 3, 1), ld) - 1; 777 | int n = 0; /* number of results */ 778 | luaL_argcheck(L, pos <= ld, 3, "index out of bounds"); 779 | initheader(L, &h); 780 | while (*fmt != '\0') { 781 | int size, ntoalign; 782 | KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); 783 | luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2, 784 | "data string too short"); 785 | pos += ntoalign; /* skip alignment */ 786 | /* stack space for item + next position */ 787 | luaL_checkstack(L, 1, "too many results"); 788 | n++; 789 | switch (opt) { 790 | case Kint: 791 | case Kuint: { 792 | lua_Integer res = unpackint(L, data + pos, h.islittle, size, 793 | (opt == Kint)); 794 | lua_pushinteger(L, res); 795 | break; 796 | } 797 | case Kfloat: { 798 | volatile Ftypes u; 799 | lua_Number num; 800 | copywithendian(u.buff, data + pos, size, h.islittle); 801 | if (size == sizeof(u.f)) num = (lua_Number)u.f; 802 | else if (size == sizeof(u.d)) num = (lua_Number)u.d; 803 | else num = u.n; 804 | lua_pushnumber(L, num); 805 | break; 806 | } 807 | case Kchar: { 808 | lua_pushlstring(L, data + pos, size); 809 | break; 810 | } 811 | case Kstring: { 812 | size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); 813 | luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); 814 | lua_pushlstring(L, data + pos + size, len); 815 | pos += len; /* skip string */ 816 | break; 817 | } 818 | case Kzstr: { 819 | size_t len; 820 | const char *z = (const char *)memchr(data + pos, '\0', ld - pos); 821 | luaL_argcheck(L, z, 2, "unfinished string for format 'z'"); 822 | len = (size_t)(z - data - pos); 823 | lua_pushlstring(L, data + pos, len); 824 | pos += len + 1; /* skip string plus final '\0' */ 825 | break; 826 | } 827 | case Kpaddalign: case Kpadding: case Knop: 828 | n--; /* undo increment */ 829 | break; 830 | } 831 | pos += size; 832 | } 833 | lua_pushinteger(L, pos + 1); /* next position */ 834 | return n + 1; 835 | } 836 | 837 | /* }====================================================== */ 838 | -------------------------------------------------------------------------------- /src/luamem.c: -------------------------------------------------------------------------------- 1 | #define luamem_c 2 | #define LUA_LIB 3 | #define LUAMEMLIB_API 4 | 5 | #include "luamem.h" 6 | 7 | #include 8 | 9 | 10 | LUAMEMLIB_API char *luamem_newalloc (lua_State *L, size_t l) { 11 | char *mem = (char *)lua_newuserdatauv(L, l * sizeof(char), 0); 12 | luaL_newmetatable(L, LUAMEM_ALLOC); 13 | lua_setmetatable(L, -2); 14 | return mem; 15 | } 16 | 17 | typedef struct luamem_Ref { 18 | char *mem; 19 | size_t len; 20 | luamem_Unref unref; 21 | } luamem_Ref; 22 | 23 | #define unrefmem(L,r) if (r->unref) ref->unref(L, r->mem, r->len) 24 | 25 | static int refgc (lua_State *L) { 26 | luamem_Ref *ref = (luamem_Ref *)lua_touserdata(L, 1); 27 | if (ref && ref->len) { 28 | unrefmem(L, ref); 29 | ref->mem = NULL; 30 | ref->len = 0; 31 | ref->unref = NULL; 32 | } 33 | return 0; 34 | } 35 | 36 | static const luaL_Reg refmt[] = { /* metamethods */ 37 | {"__gc", refgc}, 38 | {"__close", refgc}, 39 | {NULL, NULL} 40 | }; 41 | 42 | LUAMEMLIB_API void luamem_newref (lua_State *L) { 43 | luamem_Ref *ref = (luamem_Ref *)lua_newuserdatauv(L, sizeof(luamem_Ref), 0); 44 | ref->mem = NULL; 45 | ref->len = 0; 46 | ref->unref = NULL; 47 | if (luaL_newmetatable(L, LUAMEM_REF)) luaL_setfuncs(L, refmt, 0); 48 | lua_setmetatable(L, -2); 49 | } 50 | 51 | LUAMEMLIB_API int luamem_resetref (lua_State *L, int idx, 52 | char *mem, size_t len, luamem_Unref unref, 53 | int cleanup) { 54 | luamem_Ref *ref = (luamem_Ref *)luaL_testudata(L, idx, LUAMEM_REF); 55 | if (ref) { 56 | if (mem != ref->mem) { 57 | if (cleanup) unrefmem(L, ref); 58 | ref->mem = mem; 59 | } 60 | ref->len = len; 61 | ref->unref = unref; 62 | return 1; 63 | } 64 | return 0; 65 | } 66 | 67 | LUAMEMLIB_API int luamem_type (lua_State *L, int idx) { 68 | int type = LUAMEM_TNONE; 69 | if (lua_type(L, idx) == LUA_TUSERDATA) { 70 | if (lua_getmetatable(L, idx)) { /* does it have a metatable? */ 71 | luaL_getmetatable(L, LUAMEM_ALLOC); /* get allocated memory metatable */ 72 | if (lua_rawequal(L, -1, -2)) type = LUAMEM_TALLOC; 73 | else { 74 | lua_pop(L, 1); /* remove allocated memory metatable */ 75 | luaL_getmetatable(L, LUAMEM_REF); /* get referenced memory metatable */ 76 | if (lua_rawequal(L, -1, -2)) type = LUAMEM_TREF; 77 | } 78 | lua_pop(L, 2); /* remove both metatables */ 79 | } 80 | } 81 | return type; 82 | } 83 | 84 | LUAMEMLIB_API char *luamem_tomemoryx (lua_State *L, int idx, 85 | size_t *len, luamem_Unref *unref, 86 | int *type) { 87 | int typemem; 88 | if (!type) type = &typemem; 89 | *type = luamem_type(L, idx); 90 | switch (*type) { 91 | case LUAMEM_TALLOC: 92 | if (len) *len = lua_rawlen(L, idx); 93 | if (unref) *unref = NULL; 94 | return (char *)lua_touserdata(L, idx); 95 | case LUAMEM_TREF: { 96 | luamem_Ref *ref = (luamem_Ref *)lua_touserdata(L, idx); 97 | if (len) *len = ref->len; 98 | if (unref) *unref = ref->unref; 99 | return ref->mem; 100 | } 101 | } 102 | if (len) *len = 0; 103 | if (unref) *unref = NULL; 104 | return NULL; 105 | } 106 | 107 | LUAMEMLIB_API char *luamem_checkmemory (lua_State *L, int arg, size_t *len) { 108 | int type; 109 | char *mem = luamem_tomemoryx(L, arg, len, NULL, &type); 110 | if (type == LUAMEM_TNONE) luaL_typeerror(L, arg, "memory"); 111 | return mem; 112 | } 113 | 114 | 115 | LUAMEMLIB_API int luamem_isarray (lua_State *L, int idx) { 116 | return (lua_isstring(L, idx) || luamem_ismemory(L, idx)); 117 | } 118 | 119 | LUAMEMLIB_API const char *luamem_toarray (lua_State *L, int idx, size_t *len) { 120 | int type; 121 | const char *s = luamem_tomemoryx(L, idx, len, NULL, &type); 122 | if (type == LUAMEM_TNONE) return lua_tolstring(L, idx, len); 123 | return s; 124 | } 125 | 126 | LUAMEMLIB_API const char *luamem_asarray (lua_State *L, int idx, size_t *len) { 127 | int type; 128 | const char *s = luamem_tomemoryx(L, idx, len, NULL, &type); 129 | if (type == LUAMEM_TNONE) return luaL_tolstring(L, idx, len); 130 | return s; 131 | } 132 | 133 | LUAMEMLIB_API const char *luamem_checkarray (lua_State *L, 134 | int arg, 135 | size_t *len) { 136 | int type; 137 | const char *s = luamem_tomemoryx(L, arg, len, NULL, &type); 138 | if (type == LUAMEM_TNONE) { 139 | s = lua_tolstring(L, arg, len); 140 | if (!s) luaL_typeerror(L, arg, "string or memory"); 141 | } 142 | return s; 143 | } 144 | 145 | LUAMEMLIB_API const char *luamem_optarray (lua_State *L, 146 | int arg, 147 | const char *def, 148 | size_t *len) { 149 | if (lua_isnoneornil(L, arg)) { 150 | if (len) 151 | *len = (def ? strlen(def) : 0); 152 | return def; 153 | } 154 | else return luamem_checkarray(L, arg, len); 155 | } 156 | 157 | 158 | LUAMEMLIB_API void *luamem_realloc(lua_State *L, void *mem, size_t osize, 159 | size_t nsize) { 160 | void *userdata; 161 | lua_Alloc alloc = lua_getallocf(L, &userdata); 162 | return alloc(userdata, mem, osize, nsize); 163 | } 164 | 165 | LUAMEMLIB_API void luamem_free(lua_State *L, void *mem, size_t size) { 166 | luamem_realloc(L, mem, size, 0); 167 | } 168 | 169 | LUAMEMLIB_API size_t luamem_checklenarg (lua_State *L, int idx) { 170 | lua_Integer sz = luaL_checkinteger(L, idx); 171 | luaL_argcheck(L, 0 <= sz && sz < (lua_Integer)LUAMEM_MAXSIZE, 172 | idx, "invalid size"); 173 | return (size_t)sz; 174 | } 175 | 176 | /* 177 | * NOTE: most of the code below is copied from the source of Lua 5.4.3 by 178 | * R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. 179 | * 180 | * Copyright (C) 1994-2020 Lua.org, PUC-Rio. 181 | * 182 | * Permission is hereby granted, free of charge, to any person obtaining 183 | * a copy of this software and associated documentation files (the 184 | * "Software"), to deal in the Software without restriction, including 185 | * without limitation the rights to use, copy, modify, merge, publish, 186 | * distribute, sublicense, and/or sell copies of the Software, and to 187 | * permit persons to whom the Software is furnished to do so, subject to 188 | * the following conditions: 189 | * 190 | * The above copyright notice and this permission notice shall be 191 | * included in all copies or substantial portions of the Software. 192 | * 193 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 194 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 195 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 196 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 197 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 198 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 199 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 200 | */ 201 | 202 | /* 203 | ** {====================================================== 204 | ** Generic Buffer manipulation 205 | ** ======================================================= 206 | */ 207 | 208 | 209 | /* userdata to box arbitrary data */ 210 | typedef struct UBox { 211 | void *box; 212 | size_t bsize; 213 | } UBox; 214 | 215 | 216 | static void *resizebox (lua_State *L, int idx, size_t newsize) { 217 | void *ud; 218 | lua_Alloc allocf = lua_getallocf(L, &ud); 219 | UBox *box = (UBox *)lua_touserdata(L, idx); 220 | void *temp = allocf(ud, box->box, box->bsize, newsize); 221 | if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */ 222 | lua_pushliteral(L, "not enough memory"); 223 | lua_error(L); /* raise a memory error */ 224 | } 225 | box->box = temp; 226 | box->bsize = newsize; 227 | return temp; 228 | } 229 | 230 | 231 | static int boxgc (lua_State *L) { 232 | resizebox(L, 1, 0); 233 | return 0; 234 | } 235 | 236 | 237 | static const luaL_Reg boxmt[] = { /* box metamethods */ 238 | {"__gc", boxgc}, 239 | {"__close", boxgc}, 240 | {NULL, NULL} 241 | }; 242 | 243 | 244 | static void newbox (lua_State *L) { 245 | UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0); 246 | box->box = NULL; 247 | box->bsize = 0; 248 | if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */ 249 | luaL_setfuncs(L, boxmt, 0); /* set its metamethods */ 250 | lua_setmetatable(L, -2); 251 | } 252 | 253 | 254 | /* 255 | ** check whether buffer is using a userdata on the stack as a temporary 256 | ** buffer 257 | */ 258 | #define buffonstack(B) ((B)->b != (B)->init.b) 259 | 260 | 261 | /* 262 | ** Whenever buffer is accessed, slot 'idx' must either be a box (which 263 | ** cannot be NULL) or it is a placeholder for the buffer. 264 | */ 265 | #define checkbufferlevel(B,idx) \ 266 | lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \ 267 | : lua_touserdata(B->L, idx) == (void*)B) 268 | 269 | 270 | /* 271 | ** Compute new size for buffer 'B', enough to accommodate extra 'sz' 272 | ** bytes. 273 | */ 274 | static size_t newbuffsize (luaL_Buffer *B, size_t sz) { 275 | size_t newsize = B->size * 2; /* double buffer size */ 276 | if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ 277 | return luaL_error(B->L, "buffer too large"); 278 | if (newsize < B->n + sz) /* double is not big enough? */ 279 | newsize = B->n + sz; 280 | return newsize; 281 | } 282 | 283 | 284 | /* 285 | ** Returns a pointer to a free area with at least 'sz' bytes in buffer 286 | ** 'B'. 'boxidx' is the relative position in the stack where is the 287 | ** buffer's box or its placeholder. 288 | */ 289 | static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { 290 | checkbufferlevel(B, boxidx); 291 | if (B->size - B->n >= sz) /* enough space? */ 292 | return B->b + B->n; 293 | else { 294 | lua_State *L = B->L; 295 | char *newbuff; 296 | size_t newsize = newbuffsize(B, sz); 297 | /* create larger buffer */ 298 | if (buffonstack(B)) /* buffer already has a box? */ 299 | newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ 300 | else { /* no box yet */ 301 | lua_remove(L, boxidx); /* remove placeholder */ 302 | newbox(L); /* create a new box */ 303 | lua_insert(L, boxidx); /* move box to its intended position */ 304 | lua_toclose(L, boxidx); 305 | newbuff = (char *)resizebox(L, boxidx, newsize); 306 | memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ 307 | } 308 | B->b = newbuff; 309 | B->size = newsize; 310 | return newbuff + B->n; 311 | } 312 | } 313 | 314 | 315 | LUAMEMLIB_API void luamem_addvalue (luaL_Buffer *B) { 316 | lua_State *L = B->L; 317 | size_t len; 318 | const char *s = luamem_toarray(L, -1, &len); 319 | char *b = prepbuffsize(B, len, -2); 320 | memcpy(b, s, len * sizeof(char)); 321 | luaL_addsize(B, len); 322 | lua_pop(L, 1); /* pop string */ 323 | } 324 | 325 | /* }====================================================== */ 326 | -------------------------------------------------------------------------------- /src/luamem.h: -------------------------------------------------------------------------------- 1 | #ifndef lstraux_h 2 | #define lstraux_h 3 | 4 | 5 | #include 6 | #include 7 | 8 | 9 | 10 | #ifndef LUAMEMLIB_API 11 | #define LUAMEMLIB_API LUALIB_API 12 | #endif 13 | 14 | #ifndef LUAMEMMOD_API 15 | #define LUAMEMMOD_API LUAMOD_API 16 | #endif 17 | 18 | 19 | #define LUAMEM_TNONE 0 20 | #define LUAMEM_TALLOC 1 21 | #define LUAMEM_TREF 2 22 | 23 | #define LUAMEM_ALLOC "char[]" 24 | #define LUAMEM_REF "luamem_Ref" 25 | 26 | 27 | LUAMEMLIB_API char *(luamem_newalloc) (lua_State *L, size_t len); 28 | 29 | typedef void (*luamem_Unref) (lua_State *L, void *mem, size_t len); 30 | 31 | LUAMEMLIB_API void (luamem_newref) (lua_State *L); 32 | LUAMEMLIB_API int (luamem_resetref) (lua_State *L, int idx, 33 | char *mem, size_t len, luamem_Unref unref, 34 | int cleanup); 35 | 36 | #define luamem_setref(L,I,M,S,F) luamem_resetref(L,I,M,S,F,1) 37 | 38 | LUAMEMLIB_API int (luamem_type) (lua_State *L, int idx); 39 | 40 | #define luamem_ismemory(L,I) (luamem_type(L,I) != LUAMEM_TNONE) 41 | #define luamem_tomemory(L,I,S) (luamem_tomemoryx(L,I,S,NULL,NULL)) 42 | 43 | LUAMEMLIB_API char *(luamem_tomemoryx) (lua_State *L, int idx, 44 | size_t *len, luamem_Unref *unref, 45 | int *type); 46 | LUAMEMLIB_API char *(luamem_checkmemory) (lua_State *L, int idx, size_t *len); 47 | 48 | 49 | LUAMEMLIB_API int (luamem_isarray) (lua_State *L, int idx); 50 | LUAMEMLIB_API const char *(luamem_toarray) (lua_State *L, int idx, size_t *len); 51 | LUAMEMLIB_API const char *(luamem_asarray) (lua_State *L, int idx, size_t *len); 52 | LUAMEMLIB_API const char *(luamem_checkarray) (lua_State *L, int idx, size_t *len); 53 | LUAMEMLIB_API const char *(luamem_optarray) (lua_State *L, int arg, const char *def, size_t *len); 54 | 55 | 56 | LUAMEMLIB_API void *(luamem_realloc) (lua_State *L, void *mem, size_t osize, 57 | size_t nsize); 58 | LUAMEMLIB_API void (luamem_free) (lua_State *L, void *memo, size_t size); 59 | LUAMEMLIB_API size_t (luamem_checklenarg) (lua_State *L, int idx); 60 | 61 | 62 | #if !defined(MAX_SIZET) 63 | /* maximum value for size_t */ 64 | #define MAX_SIZET ((size_t)(~(size_t)0)) 65 | #endif 66 | 67 | /* 68 | ** Some sizes are better limited to fit in 'int', but must also fit in 69 | ** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) 70 | */ 71 | #define LUAMEM_MAXSIZE \ 72 | (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) 73 | 74 | 75 | /* 76 | ** {====================================================== 77 | ** Lua stack's buffer support 78 | ** ======================================================= 79 | */ 80 | 81 | LUAMEMLIB_API void (luamem_addvalue) (luaL_Buffer *B); 82 | 83 | /* }====================================================== */ 84 | 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /test/testall.lua: -------------------------------------------------------------------------------- 1 | local memory = require "memory" 2 | 3 | local maxi, mini = math.maxinteger, math.mininteger 4 | 5 | local NB = 16 -- maximum size for integers 6 | local sizeLI = string.packsize("j") 7 | 8 | local function asserterr(msg, f, ...) 9 | local ok, err = pcall(f, ...) 10 | assert(not ok) 11 | assert(string.find(err, msg, 1, true) ~= nil) 12 | end 13 | 14 | local function assertret(expected, ...) 15 | for i, v in ipairs(expected) do 16 | assert(v == select(i, ...), string.format("%s ~= %s", tostring(v), tostring(select(i,...)))) 17 | end 18 | assert(#expected == select("#", ...)) 19 | end 20 | 21 | local function assertunpack(expected, ...) 22 | for i, v in ipairs(expected) do 23 | assert(v == select(i, ...), string.format("%s ~= %s", tostring(v), tostring(select(i,...)))) 24 | end 25 | assert(#expected+1 == select("#", ...)) 26 | return select(#expected+1, ...) 27 | end 28 | 29 | local function testpack(case, ...) 30 | local packed = string.pack(case, ...) 31 | local size = #packed 32 | for _, extra in ipairs{ 0, 8, 16 } do 33 | local pad = string.rep("\0", extra) 34 | for _, spec in ipairs{ 35 | { prefix = "", suffix = pad }, 36 | { prefix = pad, suffix = "" }, 37 | { prefix = pad, suffix = pad } 38 | } do 39 | local index = 1+#spec.prefix 40 | local expected = spec.prefix..packed..spec.suffix 41 | local mem = memory.create(#expected) 42 | 43 | local options = string.match(case, "^%S*") 44 | local ok, pos, i = nil, index, 1 45 | for format in string.gmatch(case, "%s(%S+)") do 46 | ok, pos = memory.pack(mem, options..format, pos, select(i, ...)) 47 | assert(ok, pos) 48 | -- TODO: test attempt to pack with not enough space. 49 | i = i+1 50 | end 51 | assert(pos == index+size) 52 | assert(tostring(mem) == expected) 53 | pos, i = index, 1 54 | local sequence = {nil} 55 | for format in string.gmatch(case, "%s%S+") do 56 | sequence[1] = select(i, ...) 57 | pos = assertunpack(sequence, memory.unpack(mem, options..format, pos)) 58 | i = i+1 59 | end 60 | 61 | local format, replaces = case, 1 62 | while replaces > 0 do 63 | memory.fill(mem, 0) 64 | ok, pos = memory.pack(mem, format, index, ...) 65 | assert(ok == true) 66 | assert(pos == index+size) 67 | assert(tostring(mem) == expected) 68 | pos = assertunpack({...}, memory.unpack(mem, format, index)) 69 | assert(pos == index+size) 70 | format, replaces = string.gsub(format, " ", "") 71 | end 72 | 73 | if extra == 0 then break end 74 | end 75 | end 76 | end 77 | 78 | -- memory.type(string), memory:set(i, d), memory:get(i) 79 | local checkmodifiable do 80 | local allchars = {} 81 | for i = 255, 0, -1 do 82 | allchars[#allchars+1] = string.char(i) 83 | end 84 | allchars = table.concat(allchars) 85 | local types = { 86 | fixed = true, 87 | resizable = true, 88 | } 89 | function checkmodifiable(b, size) 90 | assert(types[memory.type(b)] ~= nil) 91 | assert(memory.len(b) == size) 92 | assert(#b == size) 93 | for i = 1, size do 94 | memory.set(b, i, math.max(0, 256-i)) 95 | end 96 | for i = 1, size do 97 | assert(memory.get(b, i) == math.max(0, 256-i)) 98 | end 99 | local expected = (size > #allchars) 100 | and allchars..string.rep("\0", size - #allchars) 101 | or allchars:sub(1, size) 102 | assert(memory.diff(b, expected) == nil) 103 | end 104 | end 105 | 106 | local function newresizable(s, i, j) 107 | local m = memory.create() 108 | if type(s) == "number" then 109 | memory.resize(m, s) 110 | elseif s ~= nil then 111 | s = string.sub(tostring(s), i or 1, j) 112 | memory.resize(m, #s, s) 113 | end 114 | return m 115 | end 116 | 117 | do print("memory.type(value)") 118 | assert(memory.type(nil) == nil) 119 | assert(memory.type(true) == nil) 120 | assert(memory.type(false) == nil) 121 | assert(memory.type(0) == nil) 122 | assert(memory.type(2^70) == nil) 123 | assert(memory.type('123') == nil) 124 | assert(memory.type({}) == nil) 125 | assert(memory.type(function () end) == nil) 126 | assert(memory.type(print) == nil) 127 | assert(memory.type(coroutine.running()) == nil) 128 | assert(memory.type(io.stdout) == nil) 129 | assert(memory.type(memory.create()) == "resizable") 130 | assert(memory.type(memory.create(10)) == "fixed") 131 | assert(memory.type(memory.create("abc")) == "fixed") 132 | assert(memory.type(memory.create("Lua Memory 1.0", 5, -5)) == "fixed") 133 | end 134 | 135 | do print("memory") 136 | local b = memory.create() 137 | memory.resize(b, 10) 138 | assert(memory.len(b) == 10) 139 | do local closeable = b end 140 | assert(memory.type(b) == "other") 141 | assert(memory.len(b) == 0) 142 | assert(memory.tostring(b) == "") 143 | assert(memory.get(b, 1) == nil) 144 | asserterr("resizable memory expected", memory.resize, b, 10) 145 | asserterr("index out of bounds", memory.set, b, 1, 255) 146 | end 147 | 148 | do print("memory..memory") 149 | local b1 = memory.create("abc") 150 | local b2 = memory.create("def") 151 | assert(b1..b2 == "abcdef") 152 | assert(b2..b1 == "defabc") 153 | assert(b2.."abc" == "defabc") 154 | assert("abc"..b2 == "abcdef") 155 | assert(b1..(123) == "abc123") 156 | assert((123)..b1 == "123abc") 157 | asserterr("attempt to concat", function () return b1..{} end) 158 | asserterr("attempt to concat", function () return {}..b2 end) 159 | local mt = {} 160 | function mt:__tostring() 161 | return self[1] 162 | end 163 | function mt.__concat(v1, v2) 164 | return tostring(v1)..tostring(v2) 165 | end 166 | local t = setmetatable({ "table" }, mt) 167 | assert(b1..t == "abctable") 168 | assert(t..b1 == "tableabc") 169 | end 170 | 171 | for kind, newmem in pairs{fixedsize=memory.create, resizable=newresizable} do 172 | local memory = setmetatable({ create = newmem }, { __index = memory }) 173 | 174 | do print(kind, "memory:get(i, j)") 175 | local b = memory.create("\1\2\3\4\5\6\7\8\9\10") 176 | 177 | asserterr("number expected", memory.get, b) 178 | asserterr("number has no integer representation", memory.get, b, 1.256) 179 | 180 | for i = 1, 10 do 181 | assertret({i}, memory.get(b, i)) 182 | assertret({11-i}, memory.get(b, -i)) 183 | end 184 | 185 | assertret({1,2,3,4,5,6,7,8,9,10}, memory.get(b, 1, -1)) 186 | assertret({2,3,4,5,6,7,8,9,10}, memory.get(b, 2, -1)) 187 | assertret({3,4,5,6,7,8}, memory.get(b, 3, -3)) 188 | assertret({3,4,5,6,7,8}, memory.get(b, 3, 8)) 189 | assertret({}, memory.get(b, 2, 1)) 190 | assertret({}, memory.get(b, 11, 20)) 191 | assertret({}, memory.get(b, -20, -11)) 192 | assertret({}, memory.get(b, 11, math.maxinteger)) 193 | assertret({}, memory.get(b, math.mininteger, 0)) 194 | assertret({1,2,3,4,5,6,7,8,9,10}, memory.get(b, math.mininteger, math.maxinteger)) 195 | end 196 | 197 | do print(kind, "memory:set(i, ...)") 198 | local b = memory.create("0123456789") 199 | 200 | memory.set(b, 0) 201 | assert(memory.tostring(b) == "0123456789") 202 | memory.set(b, -11, 0,1,2) 203 | assert(memory.tostring(b) == "\000\001\0023456789") 204 | memory.set(b, math.mininteger, string.byte("012", 1, 3)) 205 | assert(memory.tostring(b) == "0123456789") 206 | 207 | asserterr("value out of range", memory.set, b, 1, 256) 208 | asserterr("value out of range", memory.set, b, 1, 511) 209 | asserterr("value out of range", memory.set, b, 1, -1) 210 | asserterr("value out of range", memory.set, b, 1, math.maxinteger) 211 | asserterr("value out of range", memory.set, b, 1, math.mininteger) 212 | 213 | asserterr("index out of bounds", memory.set, b, 11, 255) 214 | asserterr("index out of bounds", memory.set, b, math.maxinteger, 255) 215 | 216 | memory.set(b, 3, 0,255,0) 217 | assert(memory.tostring(b) == "01\000\255\00056789") 218 | memory.set(b, 7, 0,0xe4,0) 219 | assert(memory.tostring(b) == "01\000\255\0005\000\xe4\0009") 220 | memory.set(b, 0, string.byte("\xe4l\0uó", 1, -1)) 221 | assert(memory.tostring(b) == "\xe4l\0uó\000\xe4\0009") 222 | memory.set(b, 7, string.byte("\xe4l\0uó", 1, -1)) 223 | assert(memory.tostring(b) == "\xe4l\0uó\xe4l\0u") 224 | end 225 | 226 | do print(kind, "memory.create(string), memory.diff, memory.len, #memory") 227 | local function check(data, str, expi, explt) 228 | local b = memory.create(data) 229 | local i, lt = memory.diff(b, str) 230 | assert(i == expi) 231 | assert(lt == explt) 232 | local i, lt = memory.diff(b, memory.create(str)) 233 | assert(i == expi) 234 | assert(lt == explt) 235 | checkmodifiable(b, #data) 236 | end 237 | check('alo', 'alo1', 4, true) 238 | check('', 'a', 1, true) 239 | check('alo\0alo', 'alo\0b', 5, true) 240 | check('alo\0alo\0\0', 'alo\0alo\0', 9, false) 241 | check('alo', 'alo\0', 4, true) 242 | check('alo\0', 'alo', 4, false) 243 | check('\0', '\1', 1, true) 244 | check('\0\0', '\0\1', 2, true) 245 | check('\1\0a\0a', '\1\0a\0a', nil, false) 246 | check('\1\0a\0b', '\1\0a\0a', 5, false) 247 | check('\0\0\0', '\0\0\0\0', 4, true) 248 | check('\0\0\0\0', '\0\0\0', 4, false) 249 | check('\0\0\0', '\0\0\0\0', 4, true) 250 | check('\0\0\0', '\0\0\0', nil, false) 251 | check('\0\0b', '\0\0a\0', 3, false) 252 | end 253 | 254 | do print(kind, "memory.create(size)") 255 | local function check(size) 256 | checkmodifiable(memory.create(size), size) 257 | end 258 | check(0) 259 | check(1) 260 | check(2) 261 | check(100) 262 | check(8192) 263 | end 264 | 265 | do print(kind, "memory.create(memory|string [, i [, j]]), memory.tostring(memory, [, i [, j]])") 266 | local function check(expected, data, ...) 267 | local b = memory.create(data, ...) 268 | assert(memory.diff(b, expected) == nil) 269 | checkmodifiable(b, #expected) 270 | local b = memory.create(memory.create(data), ...) 271 | assert(memory.diff(b, expected) == nil) 272 | checkmodifiable(b, #expected) 273 | assert(memory.tostring(memory.create(data), ...) == expected) 274 | end 275 | check("" , "") 276 | check("" , "", 1) 277 | check("" , "", 1, -1) 278 | check("" , "", mini, maxi) 279 | check("234" , "123456789",2,4) 280 | check("789" , "123456789",7) 281 | check("" , "123456789",7,6) 282 | check("7" , "123456789",7,7) 283 | check("" , "123456789",0,0) 284 | check("123456789", "123456789",-10,10) 285 | check("123456789", "123456789",1,9) 286 | check("" , "123456789",-10,-20) 287 | check("9" , "123456789",-1) 288 | check("6789" , "123456789",-4) 289 | check("456" , "123456789",-6, -4) 290 | check("123456" , "123456789", mini, -4) 291 | check("123456789", "123456789", mini, maxi) 292 | check("" , "123456789", mini, mini) 293 | check("234" , "\000123456789",3,5) 294 | end 295 | 296 | do print(kind, "memory:find(string [, i [, j [, o]]])") 297 | for _, C1 in ipairs({tostring, memory.create}) do 298 | for _, C2 in ipairs({tostring, memory.create}) do 299 | local m = C1"1234567890123456789" 300 | local e = C1"" 301 | local s = C2"345" 302 | assert(memory.find(m, s) == 3) 303 | local a, b = memory.find(m, s) 304 | assert(a == 3) 305 | assert(b == 5, b) 306 | assert(memory.find(m, s, 3) == 3) 307 | assert(memory.find(m, s, 4) == 13) 308 | assert(memory.find(m, C2"346", 4) == nil) 309 | assert(memory.find(m, s, -9) == 13) 310 | assert(memory.find(m, s, 5, 1) == nil) 311 | assert(memory.find(m, C2"\0", 5) == nil) 312 | assert(memory.find(m, s, 1, -1, 4) == nil) 313 | assert(memory.find(m, s, 20, 30) == nil) 314 | assert(memory.find(m, s, -30, -20) == nil) 315 | assert(memory.find(e, s, 1, -1, 4) == nil) 316 | assert(memory.find(e, C2"") == nil) 317 | assert(memory.find(e, C2"", 1) == nil) 318 | assert(memory.find(e, C2"", 2) == nil) 319 | assert(memory.find(e, C2"aaa", 1) == nil) 320 | end 321 | end 322 | end 323 | 324 | do print(kind, "memory:fill(string [, i [, j]])") 325 | local data = string.rep(" ", 10) 326 | local full = "1234567890ABCDEF" 327 | local function fillup(space) 328 | return full:sub(1, #space) 329 | end 330 | local function check(expected, i, j) 331 | for _, S in ipairs({tostring, memory.create}) do 332 | local b = memory.create(data) 333 | memory.fill(b, S"", i, j) 334 | assert(memory.diff(b, data) == nil) 335 | memory.fill(b, S"xuxu", i, j, 5) 336 | assert(memory.diff(b, data) == nil) 337 | memory.fill(b, S"abc", i, j) 338 | assert(memory.diff(b, expected) == nil, tostring(b)) 339 | memory.fill(b, 0x55, i, j) 340 | assert(memory.diff(b, expected:gsub("%S", "\x55")) == nil) 341 | memory.fill(b, S"XYZ", i, j, 3) 342 | assert(memory.diff(b, expected:gsub("%S", "Z")) == nil) 343 | memory.fill(b, S"XYZ", i, j, -1) 344 | assert(memory.diff(b, expected:gsub("%S", "Z")) == nil) 345 | memory.fill(b, S(full), i, j) 346 | assert(memory.diff(b, expected:gsub("%S+", fillup)) == nil) 347 | end 348 | end 349 | check("abcabcabca") 350 | check("abcabcabca", 1) 351 | check("abcabcabca", 1, -1) 352 | check(" abc ", 2, 4) 353 | check(" abca", 7) 354 | check(" ", 7, 6) 355 | check(" a ", 7, 7) 356 | check("abcabcabca",-10, 10) 357 | check(" ",-10,-20) 358 | check("abcabcabc ", 1, 9) 359 | check(" a",-1) 360 | check(" abca",-4) 361 | check(" abc ",-6, -4) 362 | check(" ", 0, 0) 363 | check("abcabcabca", mini, maxi) 364 | check(" ", mini, mini) 365 | check("abcabca ", mini, -4) 366 | check(" abcabcab", 3, maxi) 367 | do 368 | local b = memory.create(full) 369 | memory.fill(b, b) 370 | assert(memory.diff(b, full) == nil) 371 | end 372 | do 373 | local b = memory.create(full) 374 | memory.fill(b, 0) 375 | assert(memory.diff(b, string.rep("\0", #full)) == nil) 376 | end 377 | do 378 | local b = memory.create(full) 379 | memory.fill(b, b, 11, -1) 380 | assert(memory.diff(b, "1234567890123456") == nil) 381 | end 382 | do 383 | local b = memory.create(full) 384 | memory.fill(b, b, 1, 6, 11) 385 | assert(memory.diff(b, "ABCDEF7890ABCDEF") == nil) 386 | end 387 | do 388 | local b = memory.create(full) 389 | memory.fill(b, b, 1, 10, 7) 390 | assert(memory.diff(b, "7890ABCDEFABCDEF") == nil) 391 | end 392 | do 393 | local b = memory.create(full) 394 | memory.fill(b, b, 7, -1) 395 | assert(memory.diff(b, "1234561234567890") == nil) 396 | end 397 | end 398 | 399 | --[[ 400 | NOTE: most of the test cases below are adapted from the tests of Lua 5.3.1 by 401 | R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. 402 | 403 | Copyright (C) 1994-2015 Lua.org, PUC-Rio. 404 | 405 | Permission is hereby granted, free of charge, to any person obtaining 406 | a copy of this software and associated documentation files (the 407 | "Software"), to deal in the Software without restriction, including 408 | without limitation the rights to use, copy, modify, merge, publish, 409 | distribute, sublicense, and/or sell copies of the Software, and to 410 | permit persons to whom the Software is furnished to do so, subject to 411 | the following conditions: 412 | 413 | The above copyright notice and this permission notice shall be 414 | included in all copies or substantial portions of the Software. 415 | 416 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 417 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 418 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 419 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 420 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 421 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 422 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 423 | --]] 424 | 425 | testpack(" B", 0xff) 426 | testpack(" b", 0x7f) 427 | testpack(" b", -0x80) 428 | testpack(" H", 0xffff) 429 | testpack(" h", 0x7fff) 430 | testpack(" h", -0x8000) 431 | testpack(" L", 0xffffffff) 432 | testpack(" l", 0x7fffffff) 433 | testpack(" l", -0x80000000) 434 | 435 | for i = 1, NB do 436 | -- small numbers with signal extension ("\xFF...") 437 | testpack(" i"..i, -1) 438 | -- small unsigned number ("\0...\xAA") 439 | testpack("< I"..i, 0xAA) 440 | testpack("> I"..i, 0xAA) 441 | end 442 | 443 | do print(kind, "memory.pack/unpack: large integers") 444 | local lnum = 0x13121110090807060504030201 445 | testpack("< j", lnum) 446 | testpack("< j", -lnum) 447 | testpack("< i"..sizeLI+1, lnum) 448 | 449 | for i = sizeLI + 1, NB do 450 | -- strings with (correct) extra bytes 451 | testpack("< I"..i, -lnum) 452 | testpack("< i"..i, -lnum) 453 | testpack("> i"..i, -lnum) 454 | 455 | --TODO: move this to an error section about: overflows 456 | local mem = memory.create(i) 457 | memory.set(mem, i, 1) 458 | asserterr("does not fit", memory.unpack, mem, "i"..i) 462 | end 463 | 464 | for i = 1, sizeLI do 465 | local n = lnum&(~(-1<<(i*8))) 466 | testpack("< i"..i, n) 467 | testpack("> i"..i, n) 468 | end 469 | end 470 | 471 | do print(kind, "memory.pack/unpack: sign extension") 472 | local u = 0xf0 473 | for i = 1, sizeLI - 1 do 474 | testpack("< i"..i, -16) 475 | testpack("> I"..i, u) 476 | u = u<<8|0xff 477 | end 478 | end 479 | 480 | do print(kind, "memory.pack/unpack: mixed endianness") 481 | testpack(" >i2 > 1 503 | local min = ~max 504 | asserterr("overflow", memory.pack, mem, "I"..i, 1, umax+1) 507 | asserterr("overflow", memory.pack, mem, ">i"..i, 1, umax) 508 | asserterr("overflow", memory.pack, mem, ">i"..i, 1, max+1) 509 | asserterr("overflow", memory.pack, mem, " i"..i, max) 512 | testpack("< i"..i, min) 513 | testpack("> I"..i, umax) 514 | end 515 | end 516 | 517 | 518 | do print(kind, "memory.pack/unpack: Lua integer size") 519 | local b = memory.create(sizeLI) 520 | testpack("> j", math.maxinteger) 521 | testpack("< j", math.mininteger) 522 | testpack("< j", -1) -- maximum unsigned integer 523 | end 524 | 525 | do print(kind, "memory.pack/unpack: floating-point numbers") 526 | for _, n in ipairs{-1.1, 1.9, 1e20, -1e20, 0.1, 2000.7} do 527 | testpack(" n", n) 528 | testpack("< n", n) 529 | testpack("> n", n) 530 | end 531 | -- for non-native precisions, test only with "round" numbers 532 | for _, n in ipairs{0, -1.5, 1/0, -1/0, 1e10, -1e9, 0.5, 2000.25} do 533 | testpack(" n f d", n, n, n) 534 | testpack("< n f d", n, n, n) 535 | testpack("> n f d", n, n, n) 536 | end 537 | end 538 | 539 | 540 | do print(kind, "memory.pack/unpack: strings") 541 | local s = string.rep("abc", 1000) 542 | testpack(" z B", s, 247) 543 | testpack(" z B", s, 249) 544 | testpack(" s", s) 545 | 546 | local mem = memory.create(#s+1) 547 | asserterr("does not fit", memory.pack, mem, "s1", 1, s) 548 | asserterr("contains zeros", memory.pack, mem, "z", 1, "alo\0"); 549 | 550 | -- create memory with no '\0' after its end 551 | local nozero = memory.create() 552 | memory.resize(nozero, 3003, "abc") 553 | memory.resize(nozero, 3000) 554 | local ok, pos = memory.pack(mem, "z", 1, nozero) 555 | assert(ok == true) 556 | assert(pos == 3002) 557 | assert(tostring(mem) == s.."\0"); 558 | 559 | asserterr("unfinished string for format 'z'", memory.unpack, nozero, "z"); 560 | 561 | for i = 2, NB do 562 | testpack(" s"..i, s) 563 | end 564 | 565 | local x = string.pack("s", "alo") 566 | asserterr("too short", memory.unpack, memory.create(x:sub(1, -2)), "s") 567 | asserterr("too short", memory.unpack, memory.create("abcd"), "c5") 568 | asserterr("unfinished string for format 'z'", memory.unpack, memory.create(), "z") 569 | asserterr("out of limits", memory.pack, memory.create(103), "s100", 1, "alo") 570 | end 571 | 572 | do 573 | testpack(" c0", "") 574 | testpack("!4 c6", "abcdef") 576 | testpack("!4 z c3", "abcdefghi", "xyz") 577 | 578 | asserterr("wrong length", memory.pack, memory.create(2), "c3", 1, "ab") 579 | asserterr("wrong length", memory.pack, memory.create(6), "c5", 1, "123456") 580 | end 581 | 582 | do print(kind, "memory.pack/unpack: multiple types and sequence") 583 | testpack("< b h b f d f n i", 1, 2, 3, 4, 5, 6, 7, 8) 584 | end 585 | 586 | do print(kind, "memory.pack/unpack: alignment") 587 | testpack("< i1 i2 ", 2, 3) 588 | 589 | testpack(">!8 bXh i4 i8 c1Xi8", -12, 100, 200, "\xEC") 590 | testpack(">!8 c1Xh i4 i8 bXi8XIXH", "\xF4", 100, 200, -20) 591 | testpack(">!4 c3 c4 c2 z i4 c5 c2 Xi4", 592 | "abc", "abcd", "xz", "hello", 5, "world", "xy") 593 | testpack(">!4 c3 c4 c2 z i4 c5 c2XhXi4", 594 | "abc", "abcd", "xz", "hello", 5, "world", "xy") 595 | testpack(" b bXd bXbx", 1, 2, 3) 596 | testpack(" b bXd b", 1, 2, 3) 597 | 598 | local mem = memory.create("0123456701234567") 599 | assert(assertunpack({}, memory.unpack(mem, "!8 xXi8")) == 9) 600 | assert(assertunpack({}, memory.unpack(mem, "!8 xXi2")) == 3) 601 | assert(assertunpack({}, memory.unpack(mem, "!2 xXi2")) == 3) 602 | assert(assertunpack({}, memory.unpack(mem, "!2 xXi8")) == 3) 603 | assert(assertunpack({}, memory.unpack(mem, "!16 xXi16")) == 17) 604 | 605 | asserterr("invalid next option", memory.pack, mem, "X", 1) 606 | asserterr("invalid next option", memory.pack, mem, "Xc1", 1) 607 | asserterr("invalid next option", memory.unpack, mem, "XXi") 608 | asserterr("invalid next option", memory.unpack, mem, "X i") 609 | end 610 | 611 | -- TODO: review the cases below to apply then to 'unpack'. 612 | do print(kind, "memory.pack/unpack: initial position") 613 | local mem = memory.create(string.pack("i4i4i4i4", 1, 2, 3, 4)) 614 | 615 | -- with alignment 616 | for pos = 0, 12 do -- will always round position to power of 2 617 | local i, p = memory.unpack(mem, "!4 i4", pos+1) 618 | assert(i == (pos+3)//4+1 and p == i*4+1) 619 | end 620 | 621 | -- negative indices 622 | local i, p = memory.unpack(mem, "!4 i4", -4) 623 | assert(i == 4 and p == 17) 624 | local i, p = memory.unpack(mem, "!4 i4", -7) 625 | assert(i == 4 and p == 17) 626 | local i, p = memory.unpack(mem, "!4 i4", -#mem) 627 | assert(i == 1 and p == 5) 628 | 629 | -- limits 630 | for i = -(#mem+1), #mem+1 do 631 | assert(memory.unpack(mem, "c0", i) == "") 632 | end 633 | asserterr("out of bounds", memory.unpack, mem, "c0", #mem+2) 634 | end 635 | end 636 | 637 | do print "memory.resize(m, size [, s])" 638 | local m = memory.create(3) 639 | asserterr("resizable memory expected", memory.resize, m, 10) 640 | 641 | local m = memory.create() 642 | assert(memory.len(m) == 0) 643 | assert(memory.tostring(m) == "") 644 | 645 | memory.resize(m, 10) 646 | assert(tostring(m) == string.rep('\0', 10)) 647 | 648 | memory.fill(m, "abcde") 649 | memory.resize(m, 15) 650 | assert(tostring(m) == "abcdeabcde\0\0\0\0\0") 651 | 652 | memory.resize(m, 5) 653 | assert(tostring(m) == "abcde") 654 | 655 | memory.resize(m, 0) 656 | assert(tostring(m) == "") 657 | 658 | memory.resize(m, 5, "abcde") 659 | assert(tostring(m) == "abcde") 660 | 661 | memory.resize(m, 10, "xyz") 662 | assert(tostring(m) == "abcdexyzxy") 663 | 664 | memory.resize(m, 5, "123") 665 | assert(tostring(m) == "abcde") 666 | 667 | memory.resize(m, 10, "") 668 | assert(tostring(m) == "abcde\0\0\0\0\0") 669 | 670 | asserterr("string or memory expected", memory.resize, m, 15, table) 671 | assert(tostring(m) == "abcde\0\0\0\0\0") 672 | 673 | asserterr("string or memory expected", memory.resize, m, 0, table) 674 | assert(tostring(m) == "abcde\0\0\0\0\0") 675 | end 676 | 677 | print "OK" 678 | --------------------------------------------------------------------------------