diff --git a/LICENSE b/LICENSE index 3aed164..525216a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2015 Tecgraf, PUC-Rio. +Copyright (C) 2015-2018 Renato Maia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index b509c55..78061bd 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,12 @@ -LuaBuffer -========= +Lua Memory +========== -Seamless Support for Buffers in Lua +The purpose of this project is to support manipulation of memory areas in Lua. +These memory areas are much like Lua strings, but their contents can be modified in place and have an identity (selfness) independent from their contents. +The library provides the following functionalities: -The main purpose of this project is to support for buffers that provide a fixed-size memory area (userdata) to be manipulated in Lua. The library introduces the concept of "stream" which is either a buffer or an ordinary Lua string. The library provides the following functionalities: - -- Support for writable memory buffers. -- C API to manipulate Lua streams (string|buffer) in a unified way. -- Alternative implementations of Lua libraries that manipulate generic Lua streams (string|buffer). - -TODO ----- - -- Make buffers provide `buffer.*` functions as methods as before. -- Assure buffers always have a `\0` at the end. -- Write the manual +- Support for writable memory areas. +- C API to manipulate strings or memory areas in a unified way. Documentation ------------- @@ -22,6 +14,12 @@ Documentation - [Manual](doc/manual.md) - [License](LICENSE) +TODO +---- + +- Finish adaptation of `string.pack` tests to test `memory.pack`. +- Add support for bitwise operations on the memory contents. + History ------- diff --git a/demo/buffer.lua b/demo/buffer.lua deleted file mode 100644 index 41fe8c9..0000000 --- a/demo/buffer.lua +++ /dev/null @@ -1,24 +0,0 @@ --- create a buffer -local b = buffer.create(10) - --- fill buffer with zeros -buffer.fill(b, 0) - --- iterate over buffer -for i = 1, buffer.size(b) do - print(i, buffer.get(b, i)) -end - --- iteractively fill the buffer -for i = 1, buffer.size(b) do - print(i, buffer.set(b, i, 2*i)) -end - --- sets 4th, 5th and 6th bytes in the buffer -buffer.set(b, 4, 0xff, 0xff, 0xff) - --- move 3 bytes from position 4 to position 1 -buffer.fill(b, b, 1, 3, 4) - --- clear the positions after the 3 first bytes -buffer.fill(b, 0, 4) diff --git a/demo/fill.lua b/demo/fill.lua new file mode 100644 index 0000000..911acb4 --- /dev/null +++ b/demo/fill.lua @@ -0,0 +1,24 @@ +-- create a memory +local m = memory.create(10) + +-- fill memory with zeros +memory.fill(m, 0) + +-- iterate over memory +for i = 1, memory.len(m) do + print(i, memory.get(m, i)) +end + +-- iterate to fill the memory +for i = 1, memory.len(m) do + memory.set(m, i, 2*i) +end + +-- sets 4th, 5th and 6th bytes in the memory +memory.set(m, 4, 0xff, 0xff, 0xff) + +-- copy 3 bytes from position 4 to position 1 +memory.fill(m, m, 1, 3, 4) + +-- clear the positions after the 3 first bytes +memory.fill(m, 0, 4) diff --git a/demo/layout.lua b/demo/layout.lua new file mode 100644 index 0000000..ca32ab6 --- /dev/null +++ b/demo/layout.lua @@ -0,0 +1,27 @@ +local ipv = { + [4] = layout.struct{ + { key = "version" , bits = 4 }, + { key = "ihl" , bits = 4 }, + { key = "tos" , bits = 6 }, + { key = "ecn" , bits = 2 }, + { key = "tot_len" , bits = 16 }, + { key = "id" , bits = 16 }, + { key = "flags" , bits = 3 }, + { key = "frag_off", bits = 13 }, + { key = "ttl" , bits = 8 }, + { key = "protocol", bits = 8 }, + { key = "check" , bits = 16 }, + { key = "src" , bits = 32, type = "string" }, + { key = "dst" , bits = 32, type = "string" }, + }, + [6] = layout.struct{ + { key = "version" , bits = 4 }, + { key = "tc" , bits = 8 }, + { key = "fl" , bits = 20 }, + { key = "tot_len" , bits = 16 }, + { key = "nh" , bits = 8 }, + { key = "hl" , bits = 8 }, + { key = "src" , bits = 128, type = "string" }, + { key = "dst" , bits = 128, type = "string" }, + }, +} diff --git a/doc/manual.md b/doc/manual.md index eda8c28..4459623 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -1,92 +1,88 @@ -Module `buffer` -=============== - --- buffer support -[ok] b = buffer.create (b|s|sz [, i [, j]]) -[ok] b:set (pos, ...) -[ok] b:fill (b|s [, i [, j [, o]]]) -[ok] sz = #b -- ~ stream.len -[ok] str = tostring (b) -- ~ stream.tostring -[ok] ... = b:get ([i [, j]]) -- ~ stream.byte -[??] fmt_i, arg_i = b:pack (i, fmt, ...) -- padding shall not change buffer -[??] ... = buffer:unpack (i, fmt [, pos]) - -Module `buffer.stream` -====================== - --- inspect streams -[ ] "string"|"buffer" = stream.type (b|s) -[ok] index, lesser = stream.diff (b|s, b|s) -[ok] sz = stream.len (b|s) -[ok] str = stream.tostring (b|s [, i [, j]]) -- ~ string.sub -[ok] ... = stream.byte (b|s [, i [, j]]) --- pattern matching -[??] i, j = stream.find (b|s, pattern [, init [, plain]]) -[??] for ... in stream.gmatch (b|s, pattern) do -[??] ... = stream.match (b|s, pattern [, init]) --- structure packing -[??] ... = stream.unpack (fmt, b|s [, pos]) -[??] size = stream.packsize (fmt, ...) --- stream factories (out="string"|"buffer") -[ok] b|s = stream.char (out, ...) -[??] b|s = stream.dump (out, f [, strip]) -[??] b|s = stream.format (out, fmt, ...) -[??] b|s = stream.gsub (out, b|s, pattern, repl [, n]) -[??] b|s = stream.pack (out, fmt, ...) -[??] b|s = stream.rep (out, b|s, n [, sep]) -[??] b|s = stream.lower (out, b|s) -- out="string"|"buffer"|"inplace" -[??] b|s = stream.upper (out, b|s) -- out="string"|"buffer"|"inplace" -[??] b|s = stream.reverse (out, b|s) -- out="string"|"buffer"|"inplace" -[ ] b|s = stream.concat (out, list [, sep [, i [, j]]]) - - -Legend ------- -[ok] - implementation and tests -[??] - implementation only -[ ] - no implementation yet - +Index +===== + +[Lua functions](#writable-byte-sequences) | [C API](#c-library-api) | [C API](#c-library-api) +---------------------------------------------|---------------------------------------------|---------------------------------------------------- +[`memory.create`](#memorycreate-s--i--j) | [`luamem_Unref`](#luamem_unref) | [`luamem_pushresultfsize`](#luamem_pushresultfsize) +[`memory.resize`](#memoryresize-m-l) | [`luamem_addvalue`](#luamem_addvalue) | [`luamem_realloc`](#luamem_realloc) +[`memory.type`](#memorytype-m) | [`luamem_checklenarg`](#luamem_checklenarg) | [`luamem_setref`](#luamem_setref) +[`memory.len`](#memorylen-m) | [`luamem_checkmemory`](#luamem_checkmemory) | [`luamem_tomemory`](#luamem_tomemory) +[`memory.diff`](#memorydiff-m1-m2) | [`luamem_checkstring`](#luamem_checkstring) | [`luamem_tomemoryx`](#luamem_tomemoryx) +[`memory.get`](#memoryget-m--i--j) | [`luamem_free`](#luamem_free) | [`luamem_tostring`](#luamem_tostring) +[`memory.set`](#memoryset-m-i-) | [`luamem_ismemory`](#luamem_ismemory) | +[`memory.fill`](#memoryfill-m-s--i--j--o) | [`luamem_isref`](#luamem_isref) | [`LUAMEM_ALLOC`](#luamem_newalloc) +[`memory.find`](#memoryfind-m-s--i--j--o) | [`luamem_isstring`](#luamem_isstring) | [`LUAMEM_REF`](#luamem_newref) +[`memory.pack`](#memorypack-m-fmt-i-v) | [`luamem_newalloc`](#luamem_newalloc) | [`LUAMEM_TALLOC`](#luamem_tomemoryx) +[`memory.unpack`](#memoryunpack-m-fmt--i) | [`luamem_newref`](#luamem_newref) | [`LUAMEM_TNONE`](#luamem_tomemoryx) +[`memory.tostring`](#memorytostring-m--i--j) | [`luamem_pushresult`](#luamem_pushresult) | [`LUAMEM_TREF`](#luamem_tomemoryx) Contents ======== -Writable Fixed-Size Buffers ---------------------------- +Writable Byte Sequences +----------------------- -This library provides generic functions for manipulation of fixed-size memory buffers. -When indexing a buffer, the first byte is at position 1 (not at 0, as in C). -Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the buffer. +This library provides generic functions for manipulation of writable memory areas. +A memory can have a fixed size or be resizable. +When indexing a memory, the first byte is at position 1 (not at 0, as in C). +Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the memory. Thus, the last byte is at position -1, and so on. -In this manual we will refer to byte in position `i` as `b[i]`. -This library provides all its functions inside the table `buffer`. -It also sets a metatable for the buffers where the __index field points to the `buffer` table. +This library provides all its functions inside the table `memory`. +It also sets a metatable for the memory where the `__index` field points to the `memory` table. Therefore, you can use the library functions in object-oriented style. -For instance, `buffer.get(b,i)` can be written as `b:get(i)`, where `b` is a buffer. +For instance, `memory.get(m,i)` can be written as `m:get(i)`, where `m` is a memory. -### `buffer.create (s [, i [, j]])` +### `memory.create ([s [, i [, j]]])` -Creates a new buffer of `s` bytes when `s` is a number. +If `s` is a number, creates a new fixed-size memory of `s` bytes with value zero. -If `s` is a string or a buffer, then the new buffer will have the same size and contents of `s` from position `i` until position `j`; +If `s` is a string or a memory, then the new memory will have the same size and contents of `s` from position `i` until position `j`; `i` and `j` can be negative. The default value for `i` is 1; the default value for `j` is -1 (which is the same as the size of `s`). -These indices are corrected following the same rules of function [`buffer.get`](#bufferget-b-i-j). +These indices are corrected following the same rules of function [`memory.get`](#memoryget-m-i-j). + +If `s` is not provided, a resizable memory of zero bytes is created. + +Returns the new memory. + +### `memory.resize (m, l)` + +Changes resizable memory `m` to contain `l` bytes. +All the initial bytes that fit in the new size are preserved. +Any extra bytes have value zero. + +### `memory.type (m)` + +Returns `"fixed"` if `m` is a fixed-size memory, or `"resizable"` if it is a resizable memory, or `other` if it is an external memory created using the C API. +Otherwise it returns `nil`. + +### `memory.len (m)` + +Returns the size of memory `m`. + +### `memory.diff (m1, m2)` + +Returns the index of the first byte which values differ in `m1` and `m2`, or `nil` if both contain the same bytes. +It also returns the result of a `m1 < m2` as if they were strings. -Returns the new buffer. +Both `m1` and `m2` shall be memory or string. -### `buffer.len (b)` +### `memory.find (m, s [, i [, j [, o]]])` -Returns the size of buffer `b`. +Searches in memory or string `m` from position `i` until `j` for the contents of the memory or string `s` from position `o` of `s`; +`i`, `j` and `o` can be negative. -### `buffer.tostring (b)` +If, after the translation of negative indices, `o` is less than 1, it is corrected to 1. +After the translation of negative indices, `i` and `j` must refer to valid positions of `m`. -Returns a string with the contents of buffer `b`. +If `i` is greater and `j` (empty range), or `o` refers to a position beyond the size of `s` (no contents), or the bytes from `s` are not found in `m` this function returns `nil`. +Otherwise, it return the position of the first byte found in `m`. -### `buffer.get (b [, i [, j]])` +### `memory.get (m [, i [, j]])` -Returns the values of bytes in buffer `b` from `i` until `j`; +Returns the values of bytes in memory `m` from `i` until `j`; `i` and `j` can be negative. The default value for `i` is 1; the default value for `j` is `i`. @@ -95,32 +91,226 @@ If, after the translation of negative indices, `i` is less than 1, it is correct If `j` is greater than the size of `s`, it is corrected to that size. If, after these corrections, `i` is greater than `j`, the range is empty and no values are returned. -### `buffer.set (b, i, ...)` +### `memory.set (m, i, ...)` -Sets the values of bytes in buffer `b` from position `i` with values indicated by numbers received as arguments `...`; +Sets the values of bytes in memory `m` from position `i` with values indicated by numbers received as arguments `...`; `i` can be negative. -If there are more arguments than bytes in the range from `i` to the end of buffer `b`, the extra arguments are ignored. +If there are more arguments than bytes in the range from `i` to the end of memory `m`, the extra arguments are ignored. -### `buffer.fill (b, s [, i [, j [, o]]])` +### `memory.fill (m, s [, i [, j [, o]]])` -Sets the values of all bytes in buffer `b` in the range from position `i` until `j` with the contents from position `o` of the string or buffer `s`; +Sets the values of all bytes in memory `m` from position `i` until `j` with the contents of the memory or string `s` from position `o` of `s`; `i`, `j` and `o` can be negative. -If, after the translation of negative indices, `o` is less than 1, it is corrected to 1. -After the translation of negative indices, `i` and `j` must refer to valid positions of `b`. +These indices are corrected following the same rules of function [`memory.find`](#memoryfind-m-s-i-j-o-). -If `i` is greater and `j` (empty range), or `o` refers to a position beyond the size of `b` (no contents) this function has no effect. -Otherwise, the specified contents from `s` (from `o`) are copied repeatedly until they fill all bytes in the specified range of `b` (from `i` to `j`). +If `i` is greater and `j` (empty range), or `o` refers to a position beyond the size of `s` (no contents) this function has no effect. +Otherwise, the specified contents from `s` (from `o`) are copied repeatedly until they fill all bytes in the specified range of `m` (from `i` to `j`). -If `s` is a number then all bytes in the specified range of `b` are set with the value of `s`. +If `s` is a number then all bytes in the specified range of `m` are set with the value of `s`. The value of `o` is ignored in this case. -### `buffer.pack (b, i, fmt, v...)` +### `memory.tostring (m [, i [, j]])` + +Returns a string with the contents of memory or string `m` from `i` until `j`; +`i` and `j` can be negative. +The default value for `i` is 1; +the default value for `j` is `i`. + +### `memory.pack (m, fmt, i, v...)` + +Serializes in memory `m`, from position `i`, the values `v...` in binary form according to the format `fmt` (see the [Lua manual](http://www.lua.org/manual/5.3/manual.html#6.4.2)). +Returns a boolean indicating whether all values were packed in memory `m`, followed by the index of the first unwritten byte in `m` and all the values `v...` that were not packed. + +### `memory.unpack (m, fmt [, i])` + +Returns the values encoded in position `i` of memory or string `m`, according to the format `fmt`, as in function [memory.pack](#memorypack-m-i-fmt-v-); +The default value for `i` is 1. +After the read values, this function also returns the index of the first unread byte in `m`. + +C Library API +------------- + +This section describes the C API provided as a separate library to create and manipulate memory areas from C. +There are two distinct types of memory areas in the C API: + +- __allocated__: points to a constant block address with fixed size, which is automatically released when the memory is garbage collected (see [`luamem_newalloc`](#luamem_newalloc)). +- __referenced__: points to a memory area with block address and size provided by the application, which can provide a unrefering function to be used to free the memory area when it is not pointed by the Lua memory object anymore (see [`luamem_newref`](#luamem_newref)). + +### `luamem_newalloc` + +```C +char *luamem_newalloc (lua_State *L, size_t len); +``` + +Creates and pushes onto the stack a new allocated memory with the given size, and returns its block address. + +Allocated memory areas uses metatable created with name given by constant `LUAMEM_ALLOC` (see [`luaL_newmetatable`](http://www.lua.org/manual/5.3/manual.html#luaL_newmetatable)). + +### `luamem_Unref` + +```C +typedef void (*luamem_Unref) (lua_State *L, void *mem, size_t len); +``` + +Type for memory unrefering functions. + +These functions are called whenever a referenced memory ceases to pointo to block address `mem` which have size of `len` bytes. (see [`luamem_setref`](#luamem_setref)). + +### `luamem_newref` + +```C +void luamem_newref (lua_State *L); +``` + +Creates and pushes onto the stack a new reference memory pointing to NULL, with length zero, and no unrefering function (see [`luamem_Unref`](#luamem_Unref)). + +Referenced memory areas uses metatable created with name given by constant `LUAMEM_REF` (see [`luaL_newmetatable`](http://www.lua.org/manual/5.3/manual.html#luaL_newmetatable)). + +### `luamem_setref` + +```C +int luamem_setref (lua_State *L, int idx, char *mem, size_t len, luamem_Unref unref); +``` + +Defines the block address (`mem`), size (`len`), and unrefering function (`unref`) of the referenced memory at index `idx`, and returns 1. +If `idx` does not contain a referenced memory it returns 0; + +If `unref` is not `NULL`, it will be called when the memory ceases to point to this block address, either by being garbage collected or if it is updated to point to another block address (by a future call of `luamem_setref`). + +If `mem` points to the same block address currently pointed by referenced memory at index `idx` then the unrefering function previously registered is not invoked. +Therefore, to avoid the call of the current unrefering function of memory at index `idx` you can do: + +```C +size_t len; +char *mem = luamem_tomemory(L, idx, &len); +luamem_setref(L, idx, mem, len, NULL); /* only update `unref` to NULL */ +``` + +### `luamem_isref` + +```C +int luamem_isref(lua_State *L, int idx); +``` + +Returns 1 if the value at the given index is a referenced memory, and 0 otherwise. + +### `luamem_ismemory` + +```C +int luamem_ismemory (lua_State *L, int idx); +``` + +Returns 1 if the value at the given index is a memory (allocated or referenced), and 0 otherwise. + +### `luamem_tomemory` + +```C +void *luamem_tomemory (lua_State *L, int idx, size_t *len); +``` + +Equivalent to `luamem_tomemoryx(L, idx, len, NULL, NULL)`. + +### `luamem_tomemoryx` + +```C +char *luamem_tomemoryx (lua_State *L, int idx, size_t *len, luamem_Unref *unref, int *type); +``` + +Return the block address of memory at the given index, or `NULL` if the value is not a memory. + +If `len` is not `NULL`, it sets `*len` with the memory size. +If `unref` is not `NULL`, it sets `*unref` with the unrefering function if the value is a referenced memory, or `NULL` otherwise. +If `type` is not `NULL`, it sets `*type` with `LUAMEM_TREF` if the value is referenced memory, or `LUAMEM_TALLOC` in case of an allocated memory, or `LUAMEM_TNONE` otherwise. + +Because Lua has garbage collection, there is no guarantee that the pointer returned by `luamem_tomemory` will be valid after the corresponding Lua value is removed from the stack. + +### `luamem_checkmemory` + +```C +char *luamem_checkmemory (lua_State *L, int arg, size_t *len); +``` + +Checks whether the function argument `arg` is a memory (allocated or referenced) and returns a pointer to its contents; +if `len` is not `NULL` fills `*len` with the memory's length. + +### `luamem_isstring` + +```C +int luamem_isstring (lua_State *L, int idx); +``` + +Returns 1 if the value at the given index is a memory or string, and 0 otherwise. + +### `luamem_tostring` + +```C +const char *luamem_tostring (lua_State *L, int idx, size_t *len); +``` + +If the value at the given index is a memory it behaves like [`luamem_tomemory`](#luamem_tomemory), but retuning a pointer to constant bytes. +Otherwise, it is equivalent to [`lua_tolstring`](http://www.lua.org/manual/5.3/manual.html#lua_tolstring). + +__Note__: Unlike Lua strings, memory areas are not followed by a null byte (`'\0'`). + +### `luamem_checkstring` + +```C +const char *luamem_checkstring (lua_State *L, int arg, size_t *len); +``` + +Checks whether the function argument `arg` is a memory or string and returns a pointer to its contents; +if `len` is not `NULL` fills `*len` with the contents' length. + +This function might use `lua_tolstring` to get its result, so all conversions and caveats of that function apply here. + +### `luamem_checklenarg` + +```C +size_t luamem_checklenarg (lua_State *L, int arg); +``` + +Checks whether the function argument `arg` is an integer (or can be converted to an integer) of a valid memory size and returns this integer cast to a `size_t`. + +### `luamem_realloc` + +```C +void *luamem_realloc (lua_State *L, void *mem, size_t old, size_t new); +``` + +Reallocates memory pointed by `mem` of size `old` with new size `new` using the allocation function registered by the Lua state (see [`lua_getallocf`](http://www.lua.org/manual/5.3/manual.html#lua_getallocf)). +Returns the reallocated memory. + +### `luamem_free` + +```C +void luamem_free (lua_State *L, void *mem, size_t sz); +``` + +Equivalent to `luamem_realloc(L, mem, sz, 0)`. + +__Note__: Any referenced memory which uses this function as the unrefering function is considered a resizable memory by the `memory` module. + +### `luamem_addvalue` + +```C +void luamem_addvalue (luaL_Buffer *B); +``` + +Similar to [`luaL_addvalue`](http://www.lua.org/manual/5.3/manual.html#luaL_addvalue), but if the value on top of the stack is a memory, it adds its contents to the buffer without converting it to a Lua string. + +### `luamem_pushresult` + +```C +void luamem_pushresult (luaL_Buffer *B); +``` + +Similar to [`luamem_pushresult`](http://www.lua.org/manual/5.3/manual.html#luaL_pushresult), but leaves a memory with the buffer contents on the top of the stack instead of a string. -Serializes in buffer `b`, from position `i`, the values `v...` in binary form according to the format `fmt` (see the [Lua manual](http://www.lua.org/manual/5.3/manual.html#6.4.2)). -Returns the index of the first unwritten byte in `b`. +### `luamem_pushresultfsize` -### `buffer.unpack (b, i, fmt)` +```C +void luamem_pushresbufsize (luaL_Buffer *B, size_t sz); +``` -Returns the values encoded in position `i` of buffer or string `b`, according to the format `fmt`, as in function [buffer.pack](#bufferpack-b-i-fmt-v-). -After the read values, this function also returns the index of the first unread byte in `b`. \ No newline at end of file +Equivalent to the sequence [`luaL_addsize`](http://www.lua.org/manual/5.3/manual.html#luaL_addsize), [`luamem_pushresult`](#luamem_pushresult). diff --git a/lua/memory/layout.lua b/lua/memory/layout.lua new file mode 100644 index 0000000..745cb0a --- /dev/null +++ b/lua/memory/layout.lua @@ -0,0 +1,228 @@ +local _G = require "_G" +local assert = _G.assert +local ipairs = _G.ipairs +local rawset = _G.rawset +local setmetatable = _G.setmetatable + +local memory = require "memory" +local memnew = memory.create +local memcopy = memory.fill +local memget = memory.get +local mempack = memory.pack +local memunpack = memory.unpack +local memtostring = memory.tostring + +local Pointer = {} + +function Pointer:__index(key) + local field = self.struct.fields[key] + if field ~= nil then + return field.read(self.parent) + end +end + +function Pointer:__newindex(key, value) + local field = self.struct.fields[key] + if field ~= nil then + field.write(self.parent, value) + end +end + +function Pointer:__tostring() + assert(self.struct.bitoff == 0 and self.struct.bits%8 == 0, 'unsupported') + local startpos = self.parent.pos+self.struct.pos-1 + return memtostring(self.parent.buffer, startpos, startpos + self.struct.bytes-1) +end + +local LuaIntBits = 64 + +local EndianFlag = { + native = "", + little = "<", + big = ">", +} + +local LittleEndian do + local bytes = memnew(2) + mempack(bytes, "I2", 1, 1) + LittleEndian = { + [">"] = false, + ["<"] = true, + [""] = memget(bytes, 1) == 1, + } +end + +local function calcsizes(field, byteidx, bitoff) + local bitpart + local bytes + local bits = field.bits + if bits == nil then + bytes = field.bytes + if bitoff > 0 then -- byte align + byteidx = byteidx+1 + bitoff = 0 + end + bits = bytes*8 + bitpart = 0 + else + local bitused = bitoff+bits + bitpart = bitused%8 + bytes = bitused//8 + end + local spec = { + pos = byteidx, + bitoff = bitoff, + bits = bits, + bytes = bytes, + } + return spec, byteidx, bitoff, bytes, bits, bitpart +end + +local layout = {} + +function layout.string(field, ...) + local spec, byteidx, bitoff, bytes, bits, bitpart = calcsizes(field, ...) + + assert(bitoff == 0 and bitpart == 0, "unsupported type") + local format = "c"..bytes + function spec.read(self) + return memunpack(self.buffer, format, byteidx+self.pos-1) + end + function spec.write(self, value) + mempack(self.buffer, format, byteidx+self.pos-1, value) + end + + return spec, byteidx+bytes, bitoff +end + +function layout.number(field, ...) + local spec, byteidx, bitoff, bytes, bits, bitpart = calcsizes(field, ...) + assert(bits <= LuaIntBits, "size is too big") + + local endian = field.endian + if endian == nil then endian = "native" end + endian = assert(EndianFlag[endian], "illegal endianess") + + if bitoff == 0 and bitpart == 0 then + local format = endian.."I"..bytes + function spec.read(self) + return memunpack(self.buffer, format, byteidx+self.pos-1) + end + function spec.write(self, value) + if not mempack(self.buffer, format, byteidx+self.pos-1, value) then + error("out of bounds") + end + end + else + local mask = (~0>>(LuaIntBits-bits)) + local shift = LittleEndian[endian] and bitoff or (8-bitpart)%8 + if bitpart > 0 then + spec.bytes = bytes+1 + end + local format = endian.."I"..spec.bytes + function spec.read(self) + return (memunpack(self.buffer, format, byteidx+self.pos-1)>>shift)&mask + end + function spec.write(self, value) + assert(value <= mask, "unsigned overflow") + local buffer = self.buffer + local pos = byteidx+self.pos-1 + local current = memunpack(buffer, format, pos) + value = (current&~(mask< 0, "invalid type") + local spec = { + pos = byteidx, + bitoff = bitoff, + bits = 0, + bytes = 0, + } + + local specs = {} + for _, field in ipairs(fields) do + local key = field.key + local type = field.type + if type == nil then type = "number" end + local build = assert(layout[type], "unsupported type") + field, byteidx, bitoff = build(field, byteidx, bitoff) + + spec.bytes = byteidx-spec.pos + spec.bits = 8*(spec.bytes)+bitoff-spec.bitoff + + if key ~= nil then + specs[key] = field + end + end + spec.fields = specs + return spec, byteidx, bitoff +end + +function layout.struct(field, ...) + local spec, byteidx, bitoff = layoutstruct(field, ...) + function spec.read(self) + local pointer = self[spec] + if pointer == nil then + pointer = setmetatable({ struct = spec, parent = self }, Pointer) + self[spec] = pointer + end + return pointer + end + function spec.write(self, value, ...) + if getmetatable(value) == Pointer then + local src, dst = value.struct, spec + assert(src.bitoff == 0 and src.bits%8 == 0 + and dst.bitoff == 0 and dst.bits%8 == 0, "unsupported") + assert(dst.bytes == src.bytes, "size mismatch") + memcopy(self.parent.buffer, value.parent.buffer, + dst.pos, dst.pos+dst.bytes-1, src.pos) + else + error("unsupported") + end + end + return spec, byteidx, bitoff +end + + +local module = {} + +function module.newstruct(fields) + return layoutstruct(fields, 1, 0) +end + +local empty = memnew(0) +function module.newpointer(struct) + local pointer = setmetatable({ + struct = struct, + buffer = empty, + pos = 1, + }, Pointer) + rawset(pointer, "parent", pointer) + return pointer +end + +function module.setpointer(pointer, buffer, pos) + rawset(pointer, "buffer", buffer) + rawset(pointer, "pos", pos or 1) +end + +return module diff --git a/src/Makefile b/src/Makefile index 448ee0d..8af8cd6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,62 +8,77 @@ LUA_BIN= $(LUA_HOME)/bin # Your platform. See PLATS for possible values. PLAT= none -CC= gcc -CFLAGS= -O2 -Wall -I$(LUA_INC) $(MYCFLAGS) -LDFLAGS= -L$(LUA_LIB) $(MYLDFLAGS) -LIBS= $(MYLIBS) +CC= gcc -std=gnu99 +CFLAGS= -O2 -Wall -I$(LUA_INC) $(SYSCFLAGS) $(MYCFLAGS) +LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS) +LIBS= $(SYSLIBS) $(MYLIBS) AR= ar rcu RANLIB= ranlib RM= rm -f +SYSCFLAGS= +SYSLDFLAGS= +SYSLIBS= + MYCFLAGS= MYLDFLAGS= MYLIBS= # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= -PLATS= linux solaris macosx +PLATS= linux macosx + +MEM_O= lmemmod.o +API_O= lmemlib.o -LIBNAME= luabuffer +MEM_M= memory.so +API_S= libluamemlib.so +LIB_A= libluamemory.a -OBJ= lbuflib.o lstrlib.o lstrops.o lstraux.o -LIB= lib$(LIBNAME).a -SO= lib$(LIBNAME).so +ALL_O= $(MEM_O) $(API_O) +ALL_A= $(LIB_A) +ALL_S= $(API_S) +ALL_M= $(MEM_M) +ALL_T= $(ALL_A) $(ALL_S) $(ALL_M) default: $(PLAT) -all: a so +all: $(ALL_T) -o: $(OBJ) +o: $(ALL_O) -a: $(LIB) +a: $(ALL_A) -so: $(SO) +so: $(ALL_S) -# Static Libraies +m: $(ALL_M) -$(LIB): $(OBJ) - $(AR) $@ $^ - $(RANLIB) $@ +$(API_S): $(API_O) + $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) -# Dynamic Libraies +$(MEM_M): $(MEM_O) $(API_S) + $(LD) -o $@ $(LDFLAGS) $^ $(LIBS) -$(SO): $(OBJ) - $(LD) $(LDFLAGS) -o $@ $^ +$(LIB_A): $(ALL_O) + $(AR) $@ $^ + $(RANLIB) $@ clean: - $(RM) $(OBJ) $(LIB) $(SO) + $(RM) $(ALL_T) $(ALL_O) + +depend: + @$(CC) $(CFLAGS) -MM l*.c echo: - @echo "CC = $(CC)" - @echo "CFLAGS = $(CFLAGS)" - @echo "AR = $(AR)" - @echo "RANLIB = $(RANLIB)" - @echo "RM = $(RM)" - @echo "MYCFLAGS = $(MYCFLAGS)" - @echo "MYLDFLAGS = $(MYLDFLAGS)" - @echo "MYLIBS = $(MYLIBS)" + @echo "PLAT= $(PLAT)" + @echo "CC= $(CC)" + @echo "CFLAGS= $(CFLAGS)" + @echo "LDFLAGS= $(SYSLDFLAGS)" + @echo "LIBS= $(LIBS)" + @echo "AR= $(AR)" + @echo "RANLIB= $(RANLIB)" + @echo "RM= $(RM)" # Convenience targets for usual platforms ALL= all @@ -76,10 +91,6 @@ linux: $(MAKE) $(ALL) LD="gcc" MYCFLAGS="-fpic $(MYCFLAGS)" \ MYLDFLAGS="-Wl,-E -O -shared $(MYLDFLAGS)" -solaris: - $(MAKE) $(ALL) LD="gcc" MYCFLAGS="-fpic $(MYCFLAGS)" \ - MYLDFLAGS="-O -shared $(MYLDFLAGS)" - macosx: $(MAKE) $(ALL) MYCFLAGS="-fno-common $(MYCFLAGS)" \ MYLDFLAGS="-bundle -undefined dynamic_lookup $(MYLDFLAGS)" \ diff --git a/src/lmemlib.c b/src/lmemlib.c new file mode 100644 index 0000000..ddb849d --- /dev/null +++ b/src/lmemlib.c @@ -0,0 +1,219 @@ +#define lmemlib_c + +#define LUAMEMLIB_API + +#include "lmemlib.h" + +#include + + +static int typeerror (lua_State *L, int arg, const char *tname); + + +LUAMEMLIB_API char *luamem_newalloc (lua_State *L, size_t l) { + char *mem = lua_newuserdata(L, l * sizeof(char)); + luaL_newmetatable(L, LUAMEM_ALLOC); + lua_setmetatable(L, -2); + return mem; +} + + +#define LUAMEM_REFREGISTRY "luamem_ReferenceRegistry" + +typedef struct luamem_Ref { + char *mem; + size_t len; + luamem_Unref unref; +} luamem_Ref; + +#define unref(L,r) if (r->unref) ref->unref(L, r->mem, r->len) + +static int luaunref (lua_State *L) { + luamem_Ref *ref = luaL_testudata(L, 1, LUAMEM_REF); + if (ref) unref(L, ref); + return 0; +} + +LUAMEMLIB_API void luamem_newref (lua_State *L) { + luamem_Ref *ref = (luamem_Ref *)lua_newuserdata(L, sizeof(luamem_Ref)); + ref->mem = NULL; + ref->len = 0; + ref->unref = NULL; + if (luaL_newmetatable(L, LUAMEM_REF)) { + lua_pushcfunction(L, luaunref); + lua_setfield(L, -2, "__gc"); + } + lua_setmetatable(L, -2); +} + +LUAMEMLIB_API int luamem_setref (lua_State *L, int idx, + char *mem, size_t len, luamem_Unref unref) { + luamem_Ref *ref = luaL_testudata(L, 1, LUAMEM_REF); + if (ref) { + if (mem != ref->mem) unref(L, ref); + ref->mem = mem; + ref->len = len; + ref->unref = unref; + return 1; + } + return 0; +} + + +LUAMEMLIB_API char *luamem_tomemoryx (lua_State *L, int idx, + size_t *len, luamem_Unref *unref, + int *type) { + char *mem = NULL; + void *p = lua_touserdata(L, idx); + if (len) *len = 0; + if (unref) *unref = NULL; + if (type) *type = LUAMEM_TNONE; + if (p) { /* value is a userdata? */ + if (lua_getmetatable(L, idx)) { /* does it have a metatable? */ + luaL_getmetatable(L, LUAMEM_ALLOC); /* get allocated memory metatable */ + if (lua_rawequal(L, -1, -2)) { /* is the same? */ + mem = (char *)p; + if (len) *len = lua_rawlen(L, idx); + if (type) *type = LUAMEM_TALLOC; + } else { + lua_pop(L, 1); /* remove allocated memory metatable */ + luaL_getmetatable(L, LUAMEM_REF); /* get referenced memory metatable */ + if (lua_rawequal(L, -1, -2)) { + luamem_Ref *ref = (luamem_Ref *)p; + mem = ref->mem; + if (len) *len = ref->len; + if (unref) *unref = ref->unref; + if (type) *type = LUAMEM_TREF; + } + } + lua_pop(L, 2); /* remove both metatables */ + } + } + return mem; +} + +LUAMEMLIB_API char *luamem_checkmemory (lua_State *L, int arg, size_t *len) { + char *mem = luamem_tomemory(L, arg, len); + if (!mem) typeerror(L, arg, "memory"); + return mem; +} + + +LUAMEMLIB_API int luamem_isstring (lua_State *L, int idx) { + return (lua_isstring(L, idx) || luamem_ismemory(L, idx)); +} + +LUAMEMLIB_API const char *luamem_tostring (lua_State *L, int idx, size_t *len) { + const char *s = luamem_tomemory(L, idx, len); + if (s) return s; + return lua_tolstring(L, idx, len); +} + +LUAMEMLIB_API const char *luamem_checkstring (lua_State *L, + int arg, + size_t *len) { + const char *s = luamem_tostring(L, arg, len); + if (!s) typeerror(L, arg, "string or memory"); + return s; +} + + +LUAMEMLIB_API void *luamem_realloc(lua_State *L, void *mem, size_t old, + size_t new) { + void *userdata; + lua_Alloc alloc = lua_getallocf(L, &userdata); + return alloc(userdata, mem, old, new); +} + +LUAMEMLIB_API void luamem_free(lua_State *L, void *mem, size_t size) { + luamem_realloc(L, mem, size, 0); +} + +LUAMEMLIB_API size_t luamem_checklenarg (lua_State *L, int idx) { + lua_Integer sz = luaL_checkinteger(L, idx); + luaL_argcheck(L, 0 <= sz && sz < (lua_Integer)LUAMEM_MAXALLOC, + idx, "invalid size"); + return (size_t)sz; +} + +/* +* NOTE: most of the code below is copied from the source of Lua 5.3.1 by +* R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. +* +* Copyright (C) 1994-2015 Lua.org, PUC-Rio. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +static int typeerror (lua_State *L, int arg, const char *tname) { + const char *msg; + const char *typearg; /* name for the type of the actual argument */ + if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING) + typearg = lua_tostring(L, -1); /* use the given type name */ + else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA) + typearg = "light userdata"; /* special name for messages */ + else + typearg = luaL_typename(L, arg); /* standard name */ + msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg); + return luaL_argerror(L, arg, msg); +} + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +/* +** check whether buffer is using a userdata on the stack as a temporary +** buffer +*/ +#define buffonstack(B) ((B)->b != (B)->initb) + + +LUAMEMLIB_API void luamem_pushresult (luaL_Buffer *B) { + lua_State *L = B->L; + if (!buffonstack(B) || B->n < B->size) { + char *p = (char *)luamem_newalloc(L, B->n * sizeof(char)); + /* move content to new buffer */ + memcpy(p, B->b, B->n * sizeof(char)); + if (buffonstack(B)) + lua_remove(L, -2); /* remove old buffer */ + } +} + + +LUAMEMLIB_API void luamem_pushresultsize (luaL_Buffer *B, size_t sz) { + luaL_addsize(B, sz); + luamem_pushresult(B); +} + + +LUAMEMLIB_API void luamem_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t l; + const char *s = luamem_tostring(L, -1, &l); + if (buffonstack(B)) + lua_insert(L, -2); /* put value below buffer */ + luaL_addlstring(B, s, l); + lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */ +} + +/* }====================================================== */ diff --git a/src/lmemlib.h b/src/lmemlib.h new file mode 100644 index 0000000..a6af52a --- /dev/null +++ b/src/lmemlib.h @@ -0,0 +1,80 @@ +#ifndef lstraux_h +#define lstraux_h + + +#include +#include + + + +#ifndef LUAMEMLIB_API +#define LUAMEMLIB_API LUALIB_API +#endif + +#ifndef LUAMEMMOD_API +#define LUAMEMMOD_API LUAMOD_API +#endif + + +#define LUAMEM_TNONE 0 +#define LUAMEM_TALLOC 1 +#define LUAMEM_TREF 2 + +#define LUAMEM_ALLOC "char[]" +#define LUAMEM_REF "luamem_Ref" + + +LUAMEMLIB_API char *(luamem_newalloc) (lua_State *L, size_t len); + + +typedef void (*luamem_Unref) (lua_State *L, void *mem, size_t len); + +#define luamem_isref(L,I) (luaL_testudata(L,I,LUAMEM_REF) != NULL) + +LUAMEMLIB_API void (luamem_newref) (lua_State *L); +LUAMEMLIB_API int (luamem_setref) (lua_State *L, int idx, + char *mem, size_t len, luamem_Unref unref); + + +#define luamem_ismemory(L,I) (luamem_tomemory(L,I,NULL) != NULL) +#define luamem_tomemory(L,I,S) (luamem_tomemoryx(L,I,S,NULL,NULL)) + +LUAMEMLIB_API char *(luamem_tomemoryx) (lua_State *L, int idx, + size_t *len, luamem_Unref *unref, + int *type); +LUAMEMLIB_API char *(luamem_checkmemory) (lua_State *L, int idx, size_t *len); + + +LUAMEMLIB_API int (luamem_isstring) (lua_State *L, int idx); +LUAMEMLIB_API const char *(luamem_tostring) (lua_State *L, int idx, size_t *len); +LUAMEMLIB_API const char *(luamem_checkstring) (lua_State *L, int idx, size_t *len); + + +LUAMEMLIB_API void *(luamem_realloc) (lua_State *L, void *mem, size_t old, + size_t new); +LUAMEMLIB_API void (luamem_free) (lua_State *L, void *memo, size_t size); +LUAMEMLIB_API size_t (luamem_checklenarg) (lua_State *L, int idx); + + +/* +** Some sizes are better limited to fit in 'int', but must also fit in +** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) +*/ +#define LUAMEM_MAXALLOC \ + (sizeof(size_t) < sizeof(int) ? (~(size_t)0) : (size_t)(INT_MAX)) + + +/* +** {====================================================== +** Lua stack's buffer support +** ======================================================= +*/ + +LUAMEMLIB_API void (luamem_addvalue) (luaL_Buffer *B); +LUAMEMLIB_API void (luamem_pushresbuf) (luaL_Buffer *B); +LUAMEMLIB_API void (luamem_pushresbufsize) (luaL_Buffer *B, size_t sz); + +/* }====================================================== */ + + +#endif diff --git a/src/lbuflib.c b/src/lmemmod.c similarity index 55% rename from src/lbuflib.c rename to src/lmemmod.c index b78f1a0..9c2e356 100644 --- a/src/lbuflib.c +++ b/src/lmemmod.c @@ -1,86 +1,168 @@ -/* -** $Id$ -** Stream support for the Lua language -** See Copyright Notice in lstraux.h -*/ - -#define lbuflib_c +#define lmemmod_c -#include "lstraux.h" -#include "lstrops.h" +#include "lmemlib.h" #include +static lua_Integer posrelat (lua_Integer pos, size_t len); +static int str2byte (lua_State *L, const char *s, size_t l); +static void code2char (lua_State *L, int idx, char *p, int n); +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2); - -static int buf_create (lua_State *L) { - char *p; - size_t lb; - const char *s = NULL; - if (lua_type(L, 1) == LUA_TNUMBER) { - lua_Integer sz = luaL_checkinteger(L, 1); - luaL_argcheck(L, 0 <= sz && sz < (lua_Integer)LUABUF_MAXSIZE, 1, - "invalid size"); - lb = (size_t)sz; +static int mem_create (lua_State *L) { + if (lua_gettop(L) == 0) { + luamem_newref(L); + luamem_setref(L, 1, NULL, 0, luamem_free); } else { - lua_Integer posi, pose; - s = luabuf_checkstream(L, 1, &lb); - posi = luastreamI_posrelat(luaL_optinteger(L, 2, 1), lb); - pose = luastreamI_posrelat(luaL_optinteger(L, 3, -1), lb); - if (posi < 1) posi = 1; - if (pose > (lua_Integer)lb) pose = lb; - if (posi > pose) { - lb = 0; - s = NULL; + char *p; + size_t len; + const char *s = NULL; + if (lua_type(L, 1) == LUA_TNUMBER) { + len = luamem_checklenarg(L, 1); } else { - lb = (int)(pose - posi + 1); - if (posi + lb <= pose) /* arithmetic overflow? */ - return luaL_error(L, "string slice too long"); - s += posi-1; + lua_Integer posi, pose; + s = luamem_checkstring(L, 1, &len); + posi = posrelat(luaL_optinteger(L, 2, 1), len); + pose = posrelat(luaL_optinteger(L, 3, -1), len); + if (posi < 1) posi = 1; + if (pose > (lua_Integer)len) pose = len; + if (posi > pose) { + len = 0; + s = NULL; + } else { + len = (int)(pose - posi + 1); + if (posi + len <= pose) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + s += posi-1; + } } + p = luamem_newalloc(L, len); + if (s) memcpy(p, s, len * sizeof(char)); + else memset(p, 0, len * sizeof(char)); } - p = luabuf_newbuffer(L, lb); - if (s) memcpy(p, s, lb * sizeof(char)); return 1; } -static int buf_len (lua_State *L) { - size_t lb; - luabuf_checkbuffer(L, 1, &lb); - lua_pushinteger(L, (lua_Integer)lb); +static int mem_resize (lua_State *L) { + size_t len; + luamem_Unref unref; + char *mem = luamem_tomemoryx(L, 1, &len, &unref, NULL); + size_t size = luamem_checklenarg(L, 2); + luaL_argcheck(L, unref == luamem_free, 1, "resizable memory expected"); + if (len != size) { + char *new = luamem_realloc(L, mem, len, size); + if (!new) luaL_error(L, "out of memory"); + luamem_setref(L, 1, mem, len, NULL); /* don't free `mem` again */ + if (len < size) memset(new+len, 0, (size-len)*sizeof(char)); + luamem_setref(L, 1, new, size, luamem_free); + } + return 0; +} + +static int mem_type (lua_State *L) { + luamem_Unref unref; + int type; + luamem_tomemoryx(L, 1, NULL, &unref, &type); + if (type == LUAMEM_TALLOC) { + lua_pushliteral(L, "fixed"); + } else if (type == LUAMEM_TREF) { + if (unref == luamem_free) lua_pushliteral(L, "resizable"); + else lua_pushliteral(L, "other"); + } else { + lua_pushnil(L); + } return 1; } -static int buf_tostring (lua_State *L) { - size_t lb; - const char *s = luabuf_checkbuffer(L, 1, &lb); - if (lb>0) lua_pushlstring(L, s, lb); +static int mem_len (lua_State *L) { + size_t len; + luamem_checkmemory(L, 1, &len); + lua_pushinteger(L, (lua_Integer)len); + return 1; +} + +static int mem_tostring (lua_State *L) { + size_t len; + const char *s = luamem_checkstring(L, 1, &len); + lua_Integer posi = posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer pose = posrelat(luaL_optinteger(L, 3, -1), len); + int n; + if (posi < 1) posi = 1; + if (pose > (lua_Integer)len) pose = len; + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + if (n > 0) lua_pushlstring(L, s + posi -1, n); else lua_pushliteral(L, ""); return 1; } -static int buf_get (lua_State *L) { - size_t lb; - const char *s = luabuf_checkbuffer(L, 1, &lb); - return luastreamI_str2byte(L, s, lb); +static int mem_diff (lua_State *L) { + size_t l1, l2; + const char *s1 = luamem_checkstring(L, 1, &l1); + const char *s2 = luamem_checkstring(L, 2, &l2); + size_t i, n=(l1= 0) return pos; + else if (0u - (size_t)pos > len) return 0; + else return (lua_Integer)len + pos + 1; +} + +static int str2byte (lua_State *L, const char *s, size_t l) { + lua_Integer posi = posrelat(luaL_optinteger(L, 2, 1), l); + lua_Integer pose = posrelat(luaL_optinteger(L, 3, posi), l); + int n, i; + if (posi < 1) posi = 1; + if (pose > (lua_Integer)l) pose = l; + if (posi > pose) return 0; /* empty interval; return no values */ + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + luaL_checkstack(L, n, "string slice too long"); + for (i=0; i l1) return NULL; /* avoids a negative 'l1' */ + else { + const char *init; /* to search for a '*s2' inside 's1' */ + l2--; /* 1st char will be checked by 'memchr' */ + l1 = l1-l2; /* 's2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct 'l1' and 's1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} /* ** {====================================================== @@ -205,7 +412,7 @@ static int getnum (const char **fmt, int df) { int a = 0; do { a = a*10 + (*((*fmt)++) - '0'); - } while (digit(**fmt) && a <= ((int)LUABUF_MAXSIZE - 9)/10); + } while (digit(**fmt) && a <= ((int)LUAMEM_MAXALLOC - 9)/10); return a; } } @@ -318,9 +525,10 @@ static char *getbytes (char **b, size_t *i, size_t lb, size_t sz) { static int packfailed (lua_State *L, size_t i, size_t arg) { lua_pushboolean(L, 0); + lua_replace(L, arg-2); lua_pushinteger(L, i+1); - lua_pushinteger(L, arg-3); - return 3; + lua_replace(L, arg-1); + return 3+lua_gettop(L)-arg; } static int packchar (char **b, size_t *i, size_t lb, const char c) { @@ -386,20 +594,21 @@ static void copywithendian (volatile char *dest, volatile const char *src, } } -static int buf_pack (lua_State *L) { +static int mem_pack (lua_State *L) { Header h; size_t lb; - char *buff = luabuf_checkbuffer(L, 1, &lb); - size_t i = (size_t)luastreamI_posrelat(luaL_checkinteger(L, 2), lb) - 1; - const char *fmt = luaL_checkstring(L, 3); /* format string */ + char *mem = luamem_checkmemory(L, 1, &lb); + const char *fmt = luaL_checkstring(L, 2); /* format string */ + size_t i = (size_t)posrelat(luaL_checkinteger(L, 3), lb) - 1; int arg = 3; /* current argument to pack */ - luaL_argcheck(L, 0 <= i && i <= (lua_Integer)lb-1, 2, "index out of bounds"); + luaL_argcheck(L, 0 <= i && i <= (lua_Integer)lb-1, 3, "out of bounds"); initheader(L, &h); + mem += i; while (*fmt != '\0') { int size, ntoalign; KOption opt = getdetails(&h, i, &fmt, &size, &ntoalign); arg++; - if (!getbytes(&buff, &i, lb, ntoalign)) /* skip alignment */ + if (!getbytes(&mem, &i, lb, ntoalign)) /* skip alignment */ return packfailed(L, i, arg); switch (opt) { case Kint: { /* signed integers */ @@ -408,7 +617,7 @@ static int buf_pack (lua_State *L) { lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); } - if (!packint(&buff, &i, lb, (lua_Unsigned)n, h.islittle, size, (n < 0))) + if (!packint(&mem, &i, lb, (lua_Unsigned)n, h.islittle, size, (n < 0))) return packfailed(L, i, arg); break; } @@ -417,14 +626,14 @@ static int buf_pack (lua_State *L) { if (size < SZINT) /* need overflow check? */ luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), arg, "unsigned overflow"); - if (!packint(&buff, &i, lb, (lua_Unsigned)n, h.islittle, size, 0)) + if (!packint(&mem, &i, lb, (lua_Unsigned)n, h.islittle, size, 0)) return packfailed(L, i, arg); break; } case Kfloat: { /* floating-point options */ volatile Ftypes u; lua_Number n; - char *data = getbytes(&buff, &i, lb, size); + char *data = getbytes(&mem, &i, lb, size); if (!data) return packfailed(L, i, arg); n = luaL_checknumber(L, arg); /* get argument */ if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ @@ -436,33 +645,33 @@ static int buf_pack (lua_State *L) { } case Kchar: { /* fixed-size string */ size_t len; - const char *s = luabuf_checkstream(L, arg, &len); + const char *s = luamem_checkstring(L, arg, &len); luaL_argcheck(L, len == (size_t)size, arg, "wrong length"); - if (!packstream(&buff, &i, lb, s, size)) + if (!packstream(&mem, &i, lb, s, size)) return packfailed(L, i, arg); break; } case Kstring: { /* strings with length count */ size_t len; - const char *s = luabuf_checkstream(L, arg, &len); + const char *s = luamem_checkstring(L, arg, &len); luaL_argcheck(L, size >= (int)sizeof(size_t) || len < ((size_t)1 << (size * NB)), arg, "string length does not fit in given size"); - if (!packint(&buff, &i, lb, (lua_Unsigned)len, h.islittle, size, 0) || /* pack length */ - !packstream(&buff, &i, lb, s, len)) + if (!packint(&mem, &i, lb, (lua_Unsigned)len, h.islittle, size, 0) || /* pack length */ + !packstream(&mem, &i, lb, s, len)) return packfailed(L, i, arg); break; } case Kzstr: { /* zero-terminated string */ size_t len; - const char *s = luabuf_checkstream(L, arg, &len); + const char *s = luamem_checkstring(L, arg, &len); luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); - if (!packstream(&buff, &i, lb, s, len) || !packchar(&buff, &i, lb, '\0')) + if (!packstream(&mem, &i, lb, s, len) || !packchar(&mem, &i, lb, '\0')) return packfailed(L, i, arg); break; } case Kpadding: { - if (!getbytes(&buff, &i, lb, 1)) + if (!getbytes(&mem, &i, lb, 1)) return packfailed(L, i, arg); /* go through */ } @@ -473,8 +682,7 @@ static int buf_pack (lua_State *L) { } lua_pushboolean(L, 1); lua_pushinteger(L, i+1); - lua_pushinteger(L, arg-2); - return 3; + return 2; } @@ -493,7 +701,7 @@ static lua_Integer unpackint (lua_State *L, const char *str, int limit = (size <= SZINT) ? size : SZINT; for (i = limit - 1; i >= 0; i--) { res <<= NB; - res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; + res |= (lua_Unsigned)uchar(str[islittle ? i : size - 1 - i]); } if (size < SZINT) { /* real size smaller than lua_Integer? */ if (issigned) { /* needs sign extension? */ @@ -504,7 +712,7 @@ static lua_Integer unpackint (lua_State *L, const char *str, else if (size > SZINT) { /* must check unread bytes */ int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; for (i = limit; i < size; i++) { - if ((unsigned char)str[islittle ? i : size - 1 - i] != mask) + if (uchar(str[islittle ? i : size - 1 - i]) != mask) luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); } } @@ -512,20 +720,20 @@ static lua_Integer unpackint (lua_State *L, const char *str, } -static int buf_unpack (lua_State *L) { +static int mem_unpack (lua_State *L) { Header h; size_t ld; - const char *data = luabuf_checkbuffer(L, 1, &ld); - size_t pos = (size_t)luastreamI_posrelat(luaL_checkinteger(L, 2), ld) - 1; - const char *fmt = luaL_checkstring(L, 3); + const char *data = luamem_checkmemory(L, 1, &ld); + const char *fmt = luaL_checkstring(L, 2); + size_t pos = (size_t)posrelat(luaL_optinteger(L, 3, 1), ld) - 1; int n = 0; /* number of results */ - luaL_argcheck(L, pos <= ld, 2, "initial position out of bounds"); + luaL_argcheck(L, pos <= ld, 3, "out of bounds"); initheader(L, &h); while (*fmt != '\0') { int size, ntoalign; KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); if ((size_t)ntoalign + size > ~pos || pos + ntoalign + size > ld) - luaL_argerror(L, 1, "data too short"); + luaL_argerror(L, 1, "out of bounds"); pos += ntoalign; /* skip alignment */ /* stack space for item + next position */ luaL_checkstack(L, 1, "too many results"); @@ -576,39 +784,3 @@ static int buf_unpack (lua_State *L) { } /* }====================================================== */ - - - -static const luaL_Reg buflib[] = { - {"create", buf_create}, - {"fill", buf_fill}, - {"get", buf_get}, - {"len", buf_len}, - {"set", buf_set}, - {"pack", buf_pack}, - {"unpack", buf_unpack}, - {NULL, NULL} -}; - -static const luaL_Reg bufmeta[] = { - {"__len", buf_len}, - {"__tostring", buf_tostring}, - {NULL, NULL} -}; - - -static void createmetatable (lua_State *L) { - if (!luaL_getmetatable(L, LUABUF_BUFFER)) { - lua_pop(L, 1); /* pop 'nil' */ - luaL_newmetatable(L, LUABUF_BUFFER); - } - luaL_setfuncs(L, bufmeta, 0); /* add buffer methods to new metatable */ - lua_pop(L, 1); /* pop new metatable */ -} - - -LUABUFMOD_API int luaopen_buffer (lua_State *L) { - luaL_newlib(L, buflib); - createmetatable(L); - return 1; -} diff --git a/src/lstraux.c b/src/lstraux.c deleted file mode 100644 index b4282e1..0000000 --- a/src/lstraux.c +++ /dev/null @@ -1,100 +0,0 @@ -/* -** $Id$ -** Auxiliary functions for handling generic streams in Lua. -** See Copyright Notice in lstraux.h -*/ - -#define lstraux_c -#define LUABUFLIB_API - -#include "lstraux.h" - -#include - - - -LUABUFLIB_API char *luabuf_newbuffer (lua_State *L, size_t l) { - char *p = lua_newuserdata(L, l * sizeof(char)); - if (!luaL_getmetatable(L, LUABUF_BUFFER)) { - lua_pop(L, 1); /* pop 'nil' */ - luaL_newmetatable(L, LUABUF_BUFFER); - } - lua_setmetatable(L, -2); - return p; -} - - -LUABUFLIB_API char *luabuf_tobuffer (lua_State *L, int idx, size_t *len) { - char *b = luaL_testudata(L, idx, LUABUF_BUFFER); - if (b && len) *len = lua_rawlen(L, idx); - return b; -} - - -LUABUFLIB_API int luabuf_isstream (lua_State *L, int idx) { - return (lua_isstring(L, idx) || luabuf_isbuffer(L, idx)); -} - - -LUABUFLIB_API const char *luabuf_tostream (lua_State *L, int idx, size_t *len) { - if (lua_isstring(L, idx)) return lua_tolstring(L, idx, len); - return luabuf_tobuffer(L, idx, len); -} - - -LUABUFLIB_API char *luabuf_checkbuffer (lua_State *L, int idx, size_t *len) { - char *b = luaL_checkudata(L, idx, LUABUF_BUFFER); - if (len) *len = lua_rawlen(L, idx); - return b; -} - - -LUABUFLIB_API const char *luabuf_checkstream (lua_State *L, int arg, size_t *len) { - const char *s = luabuf_tostream(L, arg, len); - if (!s) luaL_argerror(L, arg, "stream expected"); - return s; -} - - -/* -** {====================================================== -** Generic Buffer manipulation -** ======================================================= -*/ - -/* -** check whether buffer is using a userdata on the stack as a temporary -** buffer -*/ -#define buffonstack(B) ((B)->b != (B)->initb) - - -LUABUFLIB_API void luabuf_pushresbuf (luabuf_Buffer *B) { - lua_State *L = B->L; - if (!buffonstack(B) || B->n < B->size) { - char *p = (char *)luabuf_newbuffer(L, B->n * sizeof(char)); - /* move content to new buffer */ - memcpy(p, B->b, B->n * sizeof(char)); - if (buffonstack(B)) - lua_remove(L, -2); /* remove old buffer */ - } -} - - -LUABUFLIB_API void luabuf_pushresbufsize (luabuf_Buffer *B, size_t sz) { - luaL_addsize(B, sz); - luabuf_pushresbuf(B); -} - - -LUABUFLIB_API void luabuf_addvalue (luabuf_Buffer *B) { - lua_State *L = B->L; - size_t l; - const char *s = luabuf_tostream(L, -1, &l); - if (buffonstack(B)) - lua_insert(L, -2); /* put value below buffer */ - luabuf_addstream(B, s, l); - lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */ -} - -/* }====================================================== */ diff --git a/src/lstraux.h b/src/lstraux.h deleted file mode 100644 index 2a8c83c..0000000 --- a/src/lstraux.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -** $Id$ -** Lua Stream - Generic stream support for the Lua language -** Renato Maia, Tecgraf/PUC-Rio (http://www.tecgraf.puc-rio.br/~maia) -** See Copyright Notice at the end of this file -*/ - - -#ifndef lstraux_h -#define lstraux_h - - -#include -#include - - - -#ifndef LUABUFLIB_API -#define LUABUFLIB_API LUALIB_API -#endif - -#ifndef LUABUFMOD_API -#define LUABUFMOD_API LUAMOD_API -#endif - - - -/* -** A buffer is a userdata with metatable 'LUABUF_BUFFER'. -*/ - -#define LUABUF_BUFFER "char*" - - -#define luabuf_isbuffer(L,I) (luaL_testudata (L,I,LUABUF_BUFFER) != NULL) - - -LUABUFLIB_API char * (luabuf_newbuffer) (lua_State *L, size_t len); -LUABUFLIB_API int (luabuf_isstream) (lua_State *L, int idx); -LUABUFLIB_API char * (luabuf_tobuffer) (lua_State *L, int idx, size_t *len); -LUABUFLIB_API const char *(luabuf_tostream) (lua_State *L, int idx, size_t *len); -LUABUFLIB_API char * (luabuf_checkbuffer) (lua_State *L, int arg, size_t *len); -LUABUFLIB_API const char *(luabuf_checkstream) (lua_State *L, int arg, size_t *len); - -/* -** {====================================================== -** Lua stack's buffer support -** ======================================================= -*/ - -#define luabuf_Buffer luaL_Buffer -#define luabuf_addchar luaL_addchar -#define luabuf_addsize luaL_addsize -#define luabuf_prepbuffsize luaL_prepbuffsize -#define luabuf_prepbuffer luaL_prepbuffer -#define luabuf_addstream luaL_addlstring -#define luabuf_addlstring luaL_addlstring -#define luabuf_addstring luaL_addstring -#define luabuf_pushresult luaL_pushresult -#define luabuf_pushresultsize luaL_pushresultsize -#define luabuf_buffinit luaL_buffinit -#define luabuf_buffinitsize luaL_buffinitsize - - -LUABUFLIB_API void (luabuf_addvalue) (luabuf_Buffer *B); -LUABUFLIB_API void (luabuf_pushresbuf) (luabuf_Buffer *B); -LUABUFLIB_API void (luabuf_pushresbufsize) (luabuf_Buffer *B, size_t sz); - -/* }====================================================== */ - - -/****************************************************************************** -* Copyright (C) 1994-2015 Lua.org, PUC-Rio. -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -******************************************************************************/ - - -#endif diff --git a/src/lstrlib.c b/src/lstrlib.c deleted file mode 100644 index 66ed6ba..0000000 --- a/src/lstrlib.c +++ /dev/null @@ -1,1509 +0,0 @@ -/* -** $Id$ -** NOTE: most of the code in here is copied from the source of Lua 5.3.1 by -** R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. -** -** Stream support for the Lua language -** See Copyright Notice in lstraux.h -*/ - -#define lstrlib_c - -#include "lstraux.h" -#include "lstrops.h" - -#include -#include - - - -#if !defined(lua_assert) -#define lua_assert(x) ((void)0) -#endif - - - -/* -** maximum number of captures that a pattern can do during -** pattern-matching. This limit is arbitrary. -*/ -#if !defined(LUA_MAXCAPTURES) -#define LUA_MAXCAPTURES 32 -#endif - - -/* macro to 'unsign' a character */ -#define uchar(c) ((unsigned char)(c)) - - -static int str_len (lua_State *L) { - size_t l; - luabuf_checkstream(L, 1, &l); - lua_pushinteger(L, (lua_Integer)l); - return 1; -} - - - -static int str_isbuffer (lua_State *L) { - lua_pushboolean(L, luabuf_isbuffer(L, 1)); - return 1; -} - - -static int str_diff (lua_State *L) { - size_t l1, l2; - const char *s1 = luabuf_checkstream(L, 1, &l1); - const char *s2 = luabuf_checkstream(L, 2, &l2); - size_t i, n=(l1 (lua_Integer)l) end = l; - if (start == 1 && end == l && lua_type(L, 1) == LUA_TSTRING) - lua_settop(L, 1); - else if (start <= end) - lua_pushlstring(L, s + start - 1, (size_t)(end - start + 1)); - else lua_pushliteral(L, ""); - return 1; -} - - -static const char *const outops[] = {"string", "buffer", NULL}; -static const char *const inoutops[] = {"string", "buffer", "inplace", NULL}; - - -static void copyreverse (char *p, const char *s, size_t l) { - size_t i; - for (i=0; i 1) { /* first n-1 copies (followed by separator) */ - memcpy(p, s, l * sizeof(char)); p += l; - if (lsep > 0) { /* empty 'memcpy' is not that cheap */ - memcpy(p, sep, lsep * sizeof(char)); - p += lsep; - } - } - memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ -} - - -static int str_rep (lua_State *L) { - size_t l, lsep; - int op = luaL_checkoption(L, 1, NULL, outops); - const char *s = luabuf_checkstream(L, 2, &l); - lua_Integer n = luaL_checkinteger(L, 3); - const char *sep = luaL_optlstring(L, 4, "", &lsep); - if (n <= 0) { - if (op == 0) lua_pushliteral(L, ""); - else luabuf_newbuffer(L, 0); - } else if (l + lsep < l || l + lsep > LUABUF_MAXSIZE / n) /* may overflow? */ - return luaL_error(L, "resulting string too large"); - else { - size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; - if (op == 0) { /* string */ - luabuf_Buffer b; - char *p = luabuf_buffinitsize(L, &b, totallen); - copyrepeat(p, s, l, sep, lsep, n); - luabuf_pushresultsize(&b, totallen); - } else { - char *p = luabuf_newbuffer(L, totallen); - copyrepeat(p, s, l, sep, lsep, n); - } - } - return 1; -} - - -static int str_byte (lua_State *L) { - size_t l; - const char *s = luabuf_checkstream(L, 1, &l); - return luastreamI_str2byte(L, s, l); -} - - -static int str_char (lua_State *L) { - int n = lua_gettop(L)-1; /* number of bytes */ - int op = luaL_checkoption(L, 1, NULL, outops); - if (op == 0) { - luabuf_Buffer b; - char *p = luabuf_buffinitsize(L, &b, n); - luastreamI_code2char(L, 2, p, n); - luabuf_pushresultsize(&b, n); - } else { - char *p = luabuf_newbuffer(L, n); - luastreamI_code2char(L, 2, p, n); - } - return 1; -} - - -static int writer (lua_State *L, const void *b, size_t size, void *B) { - (void)L; - luabuf_addstream((luabuf_Buffer *) B, (const char *)b, size); - return 0; -} - - -static int str_dump (lua_State *L) { - luabuf_Buffer b; - int strip = lua_toboolean(L, 3); - int op = luaL_checkoption(L, 1, NULL, outops); - luaL_checktype(L, 2, LUA_TFUNCTION); - lua_settop(L, 1); - luabuf_buffinit(L,&b); - if (lua_dump(L, writer, &b, strip) != 0) - return luaL_error(L, "unable to dump given function"); - if (op == 0) luabuf_pushresult(&b); - else luabuf_pushresbuf(&b); - return 1; -} - - - -/* -** {====================================================== -** PATTERN MATCHING -** ======================================================= -*/ - - -#define CAP_UNFINISHED (-1) -#define CAP_POSITION (-2) - - -typedef struct MatchState { - int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ - const char *src_init; /* init of source string */ - const char *src_end; /* end ('\0') of source string */ - const char *p_end; /* end ('\0') of pattern */ - lua_State *L; - int level; /* total number of captures (finished or unfinished) */ - struct { - const char *init; - ptrdiff_t len; - } capture[LUA_MAXCAPTURES]; -} MatchState; - - -/* recursive function */ -static const char *match (MatchState *ms, const char *s, const char *p); - - -/* maximum recursion depth for 'match' */ -#if !defined(MAXCCALLS) -#define MAXCCALLS 200 -#endif - - -#define L_ESC '%' -#define SPECIALS "^$*+?.([%-" - - -static int check_capture (MatchState *ms, int l) { - l -= '1'; - if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) - return luaL_error(ms->L, "invalid capture index %%%d", l + 1); - return l; -} - - -static int capture_to_close (MatchState *ms) { - int level = ms->level; - for (level--; level>=0; level--) - if (ms->capture[level].len == CAP_UNFINISHED) return level; - return luaL_error(ms->L, "invalid pattern capture"); -} - - -static const char *classend (MatchState *ms, const char *p) { - switch (*p++) { - case L_ESC: { - if (p == ms->p_end) - luaL_error(ms->L, "malformed pattern (ends with '%%')"); - return p+1; - } - case '[': { - if (*p == '^') p++; - do { /* look for a ']' */ - if (p == ms->p_end) - luaL_error(ms->L, "malformed pattern (missing ']')"); - if (*(p++) == L_ESC && p < ms->p_end) - p++; /* skip escapes (e.g. '%]') */ - } while (*p != ']'); - return p+1; - } - default: { - return p; - } - } -} - - -static int match_class (int c, int cl) { - int res; - switch (tolower(cl)) { - case 'a' : res = isalpha(c); break; - case 'c' : res = iscntrl(c); break; - case 'd' : res = isdigit(c); break; - case 'g' : res = isgraph(c); break; - case 'l' : res = islower(c); break; - case 'p' : res = ispunct(c); break; - case 's' : res = isspace(c); break; - case 'u' : res = isupper(c); break; - case 'w' : res = isalnum(c); break; - case 'x' : res = isxdigit(c); break; - case 'z' : res = (c == 0); break; /* deprecated option */ - default: return (cl == c); - } - return (islower(cl) ? res : !res); -} - - -static int matchbracketclass (int c, const char *p, const char *ec) { - int sig = 1; - if (*(p+1) == '^') { - sig = 0; - p++; /* skip the '^' */ - } - while (++p < ec) { - if (*p == L_ESC) { - p++; - if (match_class(c, uchar(*p))) - return sig; - } - else if ((*(p+1) == '-') && (p+2 < ec)) { - p+=2; - if (uchar(*(p-2)) <= c && c <= uchar(*p)) - return sig; - } - else if (uchar(*p) == c) return sig; - } - return !sig; -} - - -static int singlematch (MatchState *ms, const char *s, const char *p, - const char *ep) { - if (s >= ms->src_end) - return 0; - else { - int c = uchar(*s); - switch (*p) { - case '.': return 1; /* matches any char */ - case L_ESC: return match_class(c, uchar(*(p+1))); - case '[': return matchbracketclass(c, p, ep-1); - default: return (uchar(*p) == c); - } - } -} - - -static const char *matchbalance (MatchState *ms, const char *s, - const char *p) { - if (p >= ms->p_end - 1) - luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); - if (*s != *p) return NULL; - else { - int b = *p; - int e = *(p+1); - int cont = 1; - while (++s < ms->src_end) { - if (*s == e) { - if (--cont == 0) return s+1; - } - else if (*s == b) cont++; - } - } - return NULL; /* string ends out of balance */ -} - - -static const char *max_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - ptrdiff_t i = 0; /* counts maximum expand for item */ - while (singlematch(ms, s + i, p, ep)) - i++; - /* keeps trying to match with the maximum repetitions */ - while (i>=0) { - const char *res = match(ms, (s+i), ep+1); - if (res) return res; - i--; /* else didn't match; reduce 1 repetition to try again */ - } - return NULL; -} - - -static const char *min_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - for (;;) { - const char *res = match(ms, s, ep+1); - if (res != NULL) - return res; - else if (singlematch(ms, s, p, ep)) - s++; /* try with one more repetition */ - else return NULL; - } -} - - -static const char *start_capture (MatchState *ms, const char *s, - const char *p, int what) { - const char *res; - int level = ms->level; - if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); - ms->capture[level].init = s; - ms->capture[level].len = what; - ms->level = level+1; - if ((res=match(ms, s, p)) == NULL) /* match failed? */ - ms->level--; /* undo capture */ - return res; -} - - -static const char *end_capture (MatchState *ms, const char *s, - const char *p) { - int l = capture_to_close(ms); - const char *res; - ms->capture[l].len = s - ms->capture[l].init; /* close capture */ - if ((res = match(ms, s, p)) == NULL) /* match failed? */ - ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ - return res; -} - - -static const char *match_capture (MatchState *ms, const char *s, int l) { - size_t len; - l = check_capture(ms, l); - len = ms->capture[l].len; - if ((size_t)(ms->src_end-s) >= len && - memcmp(ms->capture[l].init, s, len) == 0) - return s+len; - else return NULL; -} - - -static const char *match (MatchState *ms, const char *s, const char *p) { - if (ms->matchdepth-- == 0) - luaL_error(ms->L, "pattern too complex"); - init: /* using goto's to optimize tail recursion */ - if (p != ms->p_end) { /* end of pattern? */ - switch (*p) { - case '(': { /* start capture */ - if (*(p + 1) == ')') /* position capture? */ - s = start_capture(ms, s, p + 2, CAP_POSITION); - else - s = start_capture(ms, s, p + 1, CAP_UNFINISHED); - break; - } - case ')': { /* end capture */ - s = end_capture(ms, s, p + 1); - break; - } - case '$': { - if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */ - goto dflt; /* no; go to default */ - s = (s == ms->src_end) ? s : NULL; /* check end of string */ - break; - } - case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ - switch (*(p + 1)) { - case 'b': { /* balanced string? */ - s = matchbalance(ms, s, p + 2); - if (s != NULL) { - p += 4; goto init; /* return match(ms, s, p + 4); */ - } /* else fail (s == NULL) */ - break; - } - case 'f': { /* frontier? */ - const char *ep; char previous; - p += 2; - if (*p != '[') - luaL_error(ms->L, "missing '[' after '%%f' in pattern"); - ep = classend(ms, p); /* points to what is next */ - previous = (s == ms->src_init) ? '\0' : *(s - 1); - if (!matchbracketclass(uchar(previous), p, ep - 1) && - matchbracketclass(uchar(*s), p, ep - 1)) { - p = ep; goto init; /* return match(ms, s, ep); */ - } - s = NULL; /* match failed */ - break; - } - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - case '8': case '9': { /* capture results (%0-%9)? */ - s = match_capture(ms, s, uchar(*(p + 1))); - if (s != NULL) { - p += 2; goto init; /* return match(ms, s, p + 2) */ - } - break; - } - default: goto dflt; - } - break; - } - default: dflt: { /* pattern class plus optional suffix */ - const char *ep = classend(ms, p); /* points to optional suffix */ - /* does not match at least once? */ - if (!singlematch(ms, s, p, ep)) { - if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ - p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ - } - else /* '+' or no suffix */ - s = NULL; /* fail */ - } - else { /* matched once */ - switch (*ep) { /* handle optional suffix */ - case '?': { /* optional */ - const char *res; - if ((res = match(ms, s + 1, ep + 1)) != NULL) - s = res; - else { - p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ - } - break; - } - case '+': /* 1 or more repetitions */ - s++; /* 1 match already done */ - /* go through */ - case '*': /* 0 or more repetitions */ - s = max_expand(ms, s, p, ep); - break; - case '-': /* 0 or more repetitions (minimum) */ - s = min_expand(ms, s, p, ep); - break; - default: /* no suffix */ - s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ - } - } - break; - } - } - } - ms->matchdepth++; - return s; -} - - - -static const char *lmemfind (const char *s1, size_t l1, - const char *s2, size_t l2) { - if (l2 == 0) return s1; /* empty strings are everywhere */ - else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ - else { - const char *init; /* to search for a '*s2' inside 's1' */ - l2--; /* 1st char will be checked by 'memchr' */ - l1 = l1-l2; /* 's2' cannot be found after that */ - while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { - init++; /* 1st char is already checked */ - if (memcmp(init, s2+1, l2) == 0) - return init-1; - else { /* correct 'l1' and 's1' to try again */ - l1 -= init-s1; - s1 = init; - } - } - return NULL; /* not found */ - } -} - - -static void push_onecapture (MatchState *ms, int i, const char *s, - const char *e) { - if (i >= ms->level) { - if (i == 0) /* ms->level == 0, too */ - lua_pushlstring(ms->L, s, e - s); /* add whole match */ - else - luaL_error(ms->L, "invalid capture index %%%d", i + 1); - } - else { - ptrdiff_t l = ms->capture[i].len; - if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); - if (l == CAP_POSITION) - lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); - else - lua_pushlstring(ms->L, ms->capture[i].init, l); - } -} - - -static int push_captures (MatchState *ms, const char *s, const char *e) { - int i; - int nlevels = (ms->level == 0 && s) ? 1 : ms->level; - luaL_checkstack(ms->L, nlevels, "too many captures"); - for (i = 0; i < nlevels; i++) - push_onecapture(ms, i, s, e); - return nlevels; /* number of strings pushed */ -} - - -/* check whether pattern has no special characters */ -static int nospecials (const char *p, size_t l) { - size_t upto = 0; - do { - if (strpbrk(p + upto, SPECIALS)) - return 0; /* pattern has a special character */ - upto += strlen(p + upto) + 1; /* may have more after \0 */ - } while (upto <= l); - return 1; /* no special chars found */ -} - - -static int str_find_aux (lua_State *L, int find) { - size_t ls, lp; - const char *s = luabuf_checkstream(L, 1, &ls); - const char *p = luabuf_checkstream(L, 2, &lp); - lua_Integer init = luastreamI_posrelat(luaL_optinteger(L, 3, 1), ls); - if (init < 1) init = 1; - else if (init > (lua_Integer)ls + 1) { /* start after string's end? */ - lua_pushnil(L); /* cannot find anything */ - return 1; - } - /* explicit request or no special characters? */ - if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { - /* do a plain search */ - const char *s2 = lmemfind(s + init - 1, ls - (size_t)init + 1, p, lp); - if (s2) { - lua_pushinteger(L, s2 - s + 1); - lua_pushinteger(L, s2 - s + lp); - return 2; - } - } - else { - MatchState ms; - const char *s1 = s + init - 1; - int anchor = (*p == '^'); - if (anchor) { - p++; lp--; /* skip anchor character */ - } - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s + ls; - ms.p_end = p + lp; - do { - const char *res; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - if ((res=match(&ms, s1, p)) != NULL) { - if (find) { - lua_pushinteger(L, s1 - s + 1); /* start */ - lua_pushinteger(L, res - s); /* end */ - return push_captures(&ms, NULL, 0) + 2; - } - else - return push_captures(&ms, s1, res); - } - } while (s1++ < ms.src_end && !anchor); - } - lua_pushnil(L); /* not found */ - return 1; -} - - -static int str_find (lua_State *L) { - return str_find_aux(L, 1); -} - - -static int str_match (lua_State *L) { - return str_find_aux(L, 0); -} - - -static int gmatch_aux (lua_State *L) { - MatchState ms; - size_t ls, lp; - const char *s = luabuf_tostream(L, lua_upvalueindex(1), &ls); - const char *p = luabuf_tostream(L, lua_upvalueindex(2), &lp); - const char *src; - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s+ls; - ms.p_end = p + lp; - for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); - src <= ms.src_end; - src++) { - const char *e; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - if ((e = match(&ms, src, p)) != NULL) { - lua_Integer newstart = e-s; - if (e == src) newstart++; /* empty match? go at least one position */ - lua_pushinteger(L, newstart); - lua_replace(L, lua_upvalueindex(3)); - return push_captures(&ms, src, e); - } - } - return 0; /* not found */ -} - - -static int str_gmatch (lua_State *L) { - luabuf_checkstream(L, 1, NULL); - luabuf_checkstream(L, 2, NULL); - lua_settop(L, 2); - lua_pushinteger(L, 0); - lua_pushcclosure(L, gmatch_aux, 3); - return 1; -} - - -static void add_s (MatchState *ms, luabuf_Buffer *b, const char *s, - const char *e) { - size_t l, i; - lua_State *L = ms->L; - const char *news = luabuf_tostream(L, 3, &l); - for (i = 0; i < l; i++) { - if (news[i] != L_ESC) - luabuf_addchar(b, news[i]); - else { - i++; /* skip ESC */ - if (!isdigit(uchar(news[i]))) { - if (news[i] != L_ESC) - luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); - luabuf_addchar(b, news[i]); - } - else if (news[i] == '0') - luabuf_addstream(b, s, e - s); - else { - push_onecapture(ms, news[i] - '1', s, e); - luaL_tolstring(L, -1, NULL); /* if number, convert it to string */ - lua_remove(L, -2); /* remove original value */ - luaL_addvalue(b); /* add capture to accumulated result */ - } - } - } -} - - -static void add_value (MatchState *ms, - luabuf_Buffer *b, - const char *s, - const char *e, - int tr) { - lua_State *L = ms->L; - switch (tr) { - case LUA_TFUNCTION: { - int n; - lua_pushvalue(L, 3); - n = push_captures(ms, s, e); - lua_call(L, n, 1); - break; - } - case LUA_TTABLE: { - push_onecapture(ms, 0, s, e); - lua_gettable(L, 3); - break; - } - default: { /* LUA_TNUMBER or LUA_TSTRING */ - add_s(ms, b, s, e); - return; - } - } - if (!lua_toboolean(L, -1)) { /* nil or false? */ - lua_pop(L, 1); - lua_pushlstring(L, s, e - s); /* keep original text */ - } - else if (!lua_isstring(L, -1)) - luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); - luaL_addvalue(b); /* add result to accumulator */ -} - - -static int str_gsub (lua_State *L) { - size_t srcl, lp; - int op = luaL_checkoption(L, 1, NULL, outops); - const char *src = luabuf_checkstream(L, 2, &srcl); - const char *p = luabuf_checkstream(L, 3, &lp); - int tr = lua_type(L, 4); - lua_Integer max_s = luaL_optinteger(L, 5, srcl + 1); - int anchor = (*p == '^'); - lua_Integer n = 0; - MatchState ms; - luabuf_Buffer b; - luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || - tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, - "string/function/table expected"); - luabuf_buffinit(L, &b); - if (anchor) { - p++; lp--; /* skip anchor character */ - } - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = src; - ms.src_end = src+srcl; - ms.p_end = p + lp; - while (n < max_s) { - const char *e; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - e = match(&ms, src, p); - if (e) { - n++; - add_value(&ms, &b, src, e, tr); - } - if (e && e>src) /* non empty match? */ - src = e; /* skip it */ - else if (src < ms.src_end) - luabuf_addchar(&b, *src++); - else break; - if (anchor) break; - } - luabuf_addstream(&b, src, ms.src_end-src); - if (op == 0) luabuf_pushresult(&b); - else luabuf_pushresbuf(&b); - lua_pushinteger(L, n); /* number of substitutions */ - return 2; -} - -/* }====================================================== */ - - - -/* -** {====================================================== -** STRING FORMAT -** ======================================================= -*/ - -/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ -#define MAX_ITEM 512 - -/* valid flags in a format specification */ -#define FLAGS "-+ #0" - -/* -** maximum size of each format specification (such as "%-099.99d") -** (+2 for length modifiers; +10 accounts for %99.99x plus margin of error) -*/ -#define MAX_FORMAT (sizeof(FLAGS) + 2 + 10) - - -static void addquoted (lua_State *L, luabuf_Buffer *b, int arg) { - size_t l; - const char *s = luabuf_checkstream(L, arg, &l); - luabuf_addchar(b, '"'); - while (l--) { - if (*s == '"' || *s == '\\' || *s == '\n') { - luabuf_addchar(b, '\\'); - luabuf_addchar(b, *s); - } - else if (*s == '\0' || iscntrl(uchar(*s))) { - char buff[10]; - if (!isdigit(uchar(*(s+1)))) - sprintf(buff, "\\%d", (int)uchar(*s)); - else - sprintf(buff, "\\%03d", (int)uchar(*s)); - luabuf_addstring(b, buff); - } - else - luabuf_addchar(b, *s); - s++; - } - luabuf_addchar(b, '"'); -} - -static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { - const char *p = strfrmt; - while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ - if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) - luaL_error(L, "invalid format (repeated flags)"); - if (isdigit(uchar(*p))) p++; /* skip width */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - if (*p == '.') { - p++; - if (isdigit(uchar(*p))) p++; /* skip precision */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - } - if (isdigit(uchar(*p))) - luaL_error(L, "invalid format (width or precision too long)"); - *(form++) = '%'; - memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char)); - form += p - strfrmt + 1; - *form = '\0'; - return p; -} - - -/* -** add length modifier into formats -*/ -static void addlenmod (char *form, const char *lenmod) { - size_t l = strlen(form); - size_t lm = strlen(lenmod); - char spec = form[l - 1]; - strcpy(form + l - 1, lenmod); - form[l + lm - 1] = spec; - form[l + lm] = '\0'; -} - - -static int str_format (lua_State *L) { - int top = lua_gettop(L); - int arg = 1; - size_t sfl; - int op = luaL_checkoption(L, arg++, NULL, outops); - const char *strfrmt = luaL_checklstring(L, arg, &sfl); - const char *strfrmt_end = strfrmt+sfl; - luabuf_Buffer b; - luabuf_buffinit(L, &b); - while (strfrmt < strfrmt_end) { - if (*strfrmt != L_ESC) - luabuf_addchar(&b, *strfrmt++); - else if (*++strfrmt == L_ESC) - luabuf_addchar(&b, *strfrmt++); /* %% */ - else { /* format item */ - char form[MAX_FORMAT]; /* to store the format ('%...') */ - char *buff = luabuf_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ - int nb = 0; /* number of bytes in added item */ - if (++arg > top) - luaL_argerror(L, arg, "no value"); - strfrmt = scanformat(L, strfrmt, form); - switch (*strfrmt++) { - case 'c': { - nb = sprintf(buff, form, (int)luaL_checkinteger(L, arg)); - break; - } - case 'd': case 'i': - case 'o': case 'u': case 'x': case 'X': { - lua_Integer n = luaL_checkinteger(L, arg); - addlenmod(form, LUA_INTEGER_FRMLEN); - nb = sprintf(buff, form, n); - break; - } -#if defined(LUA_USE_AFORMAT) - case 'a': case 'A': -#endif - case 'e': case 'E': case 'f': - case 'g': case 'G': { - addlenmod(form, LUA_NUMBER_FRMLEN); - nb = sprintf(buff, form, luaL_checknumber(L, arg)); - break; - } - case 'q': { - addquoted(L, &b, arg); - break; - } - case 's': { - size_t l; - const char *s = luaL_tolstring(L, arg, &l); - if (!strchr(form, '.') && l >= 100) { - /* no precision and string is too long to be formatted; - keep original string */ - luaL_addvalue(&b); - break; - } - else { - nb = sprintf(buff, form, s); - lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ - break; - } - } - default: { /* also treat cases 'pnLlh' */ - return luaL_error(L, "invalid option '%%%c' to 'format'", - *(strfrmt - 1)); - } - } - luaL_addsize(&b, nb); - } - } - if (op == 0) luabuf_pushresult(&b); - else luabuf_pushresbuf(&b); - return 1; -} - -/* }====================================================== */ - - -/* -** {====================================================== -** PACK/UNPACK -** ======================================================= -*/ - - -/* value used for padding */ -#if !defined(LUA_PACKPADBYTE) -#define LUA_PACKPADBYTE 0x00 -#endif - -/* maximum size for the binary representation of an integer */ -#define MAXINTSIZE 16 - -/* number of bits in a character */ -#define NB CHAR_BIT - -/* mask for one character (NB 1's) */ -#define MC ((1 << NB) - 1) - -/* size of a lua_Integer */ -#define SZINT ((int)sizeof(lua_Integer)) - - -/* dummy union to get native endianness */ -static const union { - int dummy; - char little; /* true iff machine is little endian */ -} nativeendian = {1}; - - -/* dummy structure to get native alignment requirements */ -struct cD { - char c; - union { double d; void *p; lua_Integer i; lua_Number n; } u; -}; - -#define MAXALIGN (offsetof(struct cD, u)) - - -/* -** Union for serializing floats -*/ -typedef union Ftypes { - float f; - double d; - lua_Number n; - char buff[5 * sizeof(lua_Number)]; /* enough for any float type */ -} Ftypes; - - -/* -** information to pack/unpack stuff -*/ -typedef struct Header { - lua_State *L; - int islittle; - int maxalign; -} Header; - - -/* -** options for pack/unpack -*/ -typedef enum KOption { - Kint, /* signed integers */ - Kuint, /* unsigned integers */ - Kfloat, /* floating-point numbers */ - Kchar, /* fixed-length strings */ - Kstring, /* strings with prefixed length */ - Kzstr, /* zero-terminated strings */ - Kpadding, /* padding */ - Kpaddalign, /* padding for alignment */ - Knop /* no-op (configuration or spaces) */ -} KOption; - - -/* -** Read an integer numeral from string 'fmt' or return 'df' if -** there is no numeral -*/ -static int digit (int c) { return '0' <= c && c <= '9'; } - -static int getnum (const char **fmt, int df) { - if (!digit(**fmt)) /* no number? */ - return df; /* return default value */ - else { - int a = 0; - do { - a = a*10 + (*((*fmt)++) - '0'); - } while (digit(**fmt) && a <= ((int)LUABUF_MAXSIZE - 9)/10); - return a; - } -} - - -/* -** Read an integer numeral and raises an error if it is larger -** than the maximum size for integers. -*/ -static int getnumlimit (Header *h, const char **fmt, int df) { - int sz = getnum(fmt, df); - if (sz > MAXINTSIZE || sz <= 0) - luaL_error(h->L, "integral size (%d) out of limits [1,%d]", - sz, MAXINTSIZE); - return sz; -} - - -/* -** Initialize Header -*/ -static void initheader (lua_State *L, Header *h) { - h->L = L; - h->islittle = nativeendian.little; - h->maxalign = 1; -} - - -/* -** Read and classify next option. 'size' is filled with option's size. -*/ -static KOption getoption (Header *h, const char **fmt, int *size) { - int opt = *((*fmt)++); - *size = 0; /* default */ - switch (opt) { - case 'b': *size = sizeof(char); return Kint; - case 'B': *size = sizeof(char); return Kuint; - case 'h': *size = sizeof(short); return Kint; - case 'H': *size = sizeof(short); return Kuint; - case 'l': *size = sizeof(long); return Kint; - case 'L': *size = sizeof(long); return Kuint; - case 'j': *size = sizeof(lua_Integer); return Kint; - case 'J': *size = sizeof(lua_Integer); return Kuint; - case 'T': *size = sizeof(size_t); return Kuint; - case 'f': *size = sizeof(float); return Kfloat; - case 'd': *size = sizeof(double); return Kfloat; - case 'n': *size = sizeof(lua_Number); return Kfloat; - case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint; - case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; - case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; - case 'c': - *size = getnum(fmt, -1); - if (*size == -1) - luaL_error(h->L, "missing size for format option 'c'"); - return Kchar; - case 'z': return Kzstr; - case 'x': *size = 1; return Kpadding; - case 'X': return Kpaddalign; - case ' ': break; - case '<': h->islittle = 1; break; - case '>': h->islittle = 0; break; - case '=': h->islittle = nativeendian.little; break; - case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break; - default: luaL_error(h->L, "invalid format option '%c'", opt); - } - return Knop; -} - - -/* -** Read, classify, and fill other details about the next option. -** 'psize' is filled with option's size, 'notoalign' with its -** alignment requirements. -** Local variable 'size' gets the size to be aligned. (Kpadal option -** always gets its full alignment, other options are limited by -** the maximum alignment ('maxalign'). Kchar option needs no alignment -** despite its size. -*/ -static KOption getdetails (Header *h, size_t totalsize, - const char **fmt, int *psize, int *ntoalign) { - KOption opt = getoption(h, fmt, psize); - int align = *psize; /* usually, alignment follows size */ - if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ - if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) - luaL_argerror(h->L, 1, "invalid next option for option 'X'"); - } - if (align <= 1 || opt == Kchar) /* need no alignment? */ - *ntoalign = 0; - else { - if (align > h->maxalign) /* enforce maximum alignment */ - align = h->maxalign; - if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */ - luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); - *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); - } - return opt; -} - - -/* -** Pack integer 'n' with 'size' bytes and 'islittle' endianness. -** The final 'if' handles the case when 'size' is larger than -** the size of a Lua integer, correcting the extra sign-extension -** bytes if necessary (by default they would be zeros). -*/ -static void packint (luabuf_Buffer *b, lua_Unsigned n, - int islittle, int size, int neg) { - char *buff = luabuf_prepbuffsize(b, size); - int i; - buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ - for (i = 1; i < size; i++) { - n >>= NB; - buff[islittle ? i : size - 1 - i] = (char)(n & MC); - } - if (neg && size > SZINT) { /* negative number need sign extension? */ - for (i = SZINT; i < size; i++) /* correct extra bytes */ - buff[islittle ? i : size - 1 - i] = (char)MC; - } - luaL_addsize(b, size); /* add result to buffer */ -} - - -/* -** Copy 'size' bytes from 'src' to 'dest', correcting endianness if -** given 'islittle' is different from native endianness. -*/ -static void copywithendian (volatile char *dest, volatile const char *src, - int size, int islittle) { - if (islittle == nativeendian.little) { - while (size-- != 0) - *(dest++) = *(src++); - } - else { - dest += size - 1; - while (size-- != 0) - *(dest--) = *(src++); - } -} - - -static int str_pack (lua_State *L) { - luabuf_Buffer b; - Header h; - int arg = 1; /* current argument to pack */ - int op = luaL_checkoption(L, arg++, NULL, outops); - const char *fmt = luaL_checkstring(L, arg); /* format string */ - size_t totalsize = 0; /* accumulate total size of result */ - initheader(L, &h); - lua_pushnil(L); /* mark to separate arguments from string buffer */ - luabuf_buffinit(L, &b); - while (*fmt != '\0') { - int size, ntoalign; - KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); - totalsize += ntoalign + size; - while (ntoalign-- > 0) - luabuf_addchar(&b, LUA_PACKPADBYTE); /* fill alignment */ - arg++; - switch (opt) { - case Kint: { /* signed integers */ - lua_Integer n = luaL_checkinteger(L, arg); - if (size < SZINT) { /* need overflow check? */ - lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); - luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); - } - packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0)); - break; - } - case Kuint: { /* unsigned integers */ - lua_Integer n = luaL_checkinteger(L, arg); - if (size < SZINT) /* need overflow check? */ - luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), - arg, "unsigned overflow"); - packint(&b, (lua_Unsigned)n, h.islittle, size, 0); - break; - } - case Kfloat: { /* floating-point options */ - volatile Ftypes u; - char *buff = luabuf_prepbuffsize(&b, size); - lua_Number n = luaL_checknumber(L, arg); /* get argument */ - if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ - else if (size == sizeof(u.d)) u.d = (double)n; - else u.n = n; - /* move 'u' to final result, correcting endianness if needed */ - copywithendian(buff, u.buff, size, h.islittle); - luaL_addsize(&b, size); - break; - } - case Kchar: { /* fixed-size string */ - size_t len; - const char *s = luabuf_checkstream(L, arg, &len); - luaL_argcheck(L, len == (size_t)size, arg, "wrong length"); - luabuf_addstream(&b, s, size); - break; - } - case Kstring: { /* strings with length count */ - size_t len; - const char *s = luabuf_checkstream(L, arg, &len); - luaL_argcheck(L, size >= (int)sizeof(size_t) || - len < ((size_t)1 << (size * NB)), - arg, "string length does not fit in given size"); - packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ - luabuf_addstream(&b, s, len); - totalsize += len; - break; - } - case Kzstr: { /* zero-terminated string */ - size_t len; - const char *s = luabuf_checkstream(L, arg, &len); - luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); - luabuf_addstream(&b, s, len); - luabuf_addchar(&b, '\0'); /* add zero at the end */ - totalsize += len + 1; - break; - } - case Kpadding: luabuf_addchar(&b, LUA_PACKPADBYTE); /* go through */ - case Kpaddalign: case Knop: - arg--; /* undo increment */ - break; - } - } - if (op == 0) luabuf_pushresult(&b); - else luabuf_pushresbuf(&b); - return 1; -} - - -static int str_packsize (lua_State *L) { - Header h; - int arg = 1; /* current argument to pack */ - const char *fmt = luaL_checkstring(L, arg); /* format string */ - size_t totalsize = 0; /* accumulate total size of result */ - initheader(L, &h); - while (*fmt != '\0') { - int size, ntoalign; - KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); - size += ntoalign; /* total space used by option */ - luaL_argcheck(L, totalsize <= LUABUF_MAXSIZE - size, 1, - "format result too large"); - totalsize += size; - arg++; - switch (opt) { - case Kstring: { /* strings with length count */ - size_t len; - luabuf_checkstream(L, arg, &len); - luaL_argcheck(L, size >= (int)sizeof(size_t) || - len < ((size_t)1 << (size * NB)), - arg, "string length does not fit in given size"); - totalsize += len; - break; - } - case Kzstr: { /* zero-terminated string */ - size_t len; - const char *s = luabuf_checkstream(L, arg, &len); - luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); - totalsize += len + 1; - break; - } - default: break; - } - } - lua_pushinteger(L, (lua_Integer)totalsize); - return 1; -} - - -/* -** Unpack an integer with 'size' bytes and 'islittle' endianness. -** If size is smaller than the size of a Lua integer and integer -** is signed, must do sign extension (propagating the sign to the -** higher bits); if size is larger than the size of a Lua integer, -** it must check the unread bytes to see whether they do not cause an -** overflow. -*/ -static lua_Integer unpackint (lua_State *L, const char *str, - int islittle, int size, int issigned) { - lua_Unsigned res = 0; - int i; - int limit = (size <= SZINT) ? size : SZINT; - for (i = limit - 1; i >= 0; i--) { - res <<= NB; - res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; - } - if (size < SZINT) { /* real size smaller than lua_Integer? */ - if (issigned) { /* needs sign extension? */ - lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); - res = ((res ^ mask) - mask); /* do sign extension */ - } - } - else if (size > SZINT) { /* must check unread bytes */ - int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; - for (i = limit; i < size; i++) { - if ((unsigned char)str[islittle ? i : size - 1 - i] != mask) - luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); - } - } - return (lua_Integer)res; -} - - -static int str_unpack (lua_State *L) { - Header h; - const char *fmt = luaL_checkstring(L, 1); - size_t ld; - const char *data = luabuf_checkstream(L, 2, &ld); - size_t pos = (size_t)luastreamI_posrelat(luaL_optinteger(L, 3, 1), ld) - 1; - int n = 0; /* number of results */ - luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); - initheader(L, &h); - while (*fmt != '\0') { - int size, ntoalign; - KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); - if ((size_t)ntoalign + size > ~pos || pos + ntoalign + size > ld) - luaL_argerror(L, 2, "data string too short"); - pos += ntoalign; /* skip alignment */ - /* stack space for item + next position */ - luaL_checkstack(L, 2, "too many results"); - n++; - switch (opt) { - case Kint: - case Kuint: { - lua_Integer res = unpackint(L, data + pos, h.islittle, size, - (opt == Kint)); - lua_pushinteger(L, res); - break; - } - case Kfloat: { - volatile Ftypes u; - lua_Number num; - copywithendian(u.buff, data + pos, size, h.islittle); - if (size == sizeof(u.f)) num = (lua_Number)u.f; - else if (size == sizeof(u.d)) num = (lua_Number)u.d; - else num = u.n; - lua_pushnumber(L, num); - break; - } - case Kchar: { - lua_pushlstring(L, data + pos, size); - break; - } - case Kstring: { - size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); - luaL_argcheck(L, pos + len + size <= ld, 2, "data string too short"); - lua_pushlstring(L, data + pos + size, len); - pos += len; /* skip string */ - break; - } - case Kzstr: { - size_t len = (int)strlen(data + pos); - lua_pushlstring(L, data + pos, len); - pos += len + 1; /* skip string plus final '\0' */ - break; - } - case Kpaddalign: case Kpadding: case Knop: - n--; /* undo increment */ - break; - } - pos += size; - } - lua_pushinteger(L, pos + 1); /* next position */ - return n + 1; -} - -/* }====================================================== */ - - -static const luaL_Reg strlib[] = { - {"byte", str_byte}, - {"char", str_char}, - {"diff", str_diff}, - {"dump", str_dump}, - {"find", str_find}, - {"format", str_format}, - {"gmatch", str_gmatch}, - {"gsub", str_gsub}, - {"isbuffer", str_isbuffer}, - {"len", str_len}, - {"lower", str_lower}, - {"match", str_match}, - {"pack", str_pack}, - {"packsize", str_packsize}, - {"rep", str_rep}, - {"reverse", str_reverse}, - {"tostring", str_tostring}, - {"upper", str_upper}, - {"unpack", str_unpack}, - {NULL, NULL} -}; - - -LUABUFMOD_API int luaopen_buffer_stream (lua_State *L) { - luaL_newlib(L, strlib); - return 1; -} diff --git a/src/lstrops.c b/src/lstrops.c deleted file mode 100644 index b4cf53d..0000000 --- a/src/lstrops.c +++ /dev/null @@ -1,50 +0,0 @@ -/* -** $Id$ -** NOTE: most of the code in here is copied from the source of Lua 5.3.1 by -** R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. -** -** Stream support for the Lua language -** See Copyright Notice in lstraux.h -*/ - -#define lstrops_c - -#include "lstraux.h" -#include "lstrops.h" - - -/* macro to 'unsign' a character */ -#define uchar(c) ((unsigned char)(c)) - - -/* translate a relative string position: negative means back from end */ -LUABUF_FUNC lua_Integer luastreamI_posrelat (lua_Integer pos, size_t len) { - if (pos >= 0) return pos; - else if (0u - (size_t)pos > len) return 0; - else return (lua_Integer)len + pos + 1; -} - -LUABUF_FUNC int luastreamI_str2byte (lua_State *L, const char *s, size_t l) { - lua_Integer posi = luastreamI_posrelat(luaL_optinteger(L, 2, 1), l); - lua_Integer pose = luastreamI_posrelat(luaL_optinteger(L, 3, posi), l); - int n, i; - if (posi < 1) posi = 1; - if (pose > (lua_Integer)l) pose = l; - if (posi > pose) return 0; /* empty interval; return no values */ - n = (int)(pose - posi + 1); - if (posi + n <= pose) /* arithmetic overflow? */ - return luaL_error(L, "string slice too long"); - luaL_checkstack(L, n, "string slice too long"); - for (i=0; i - - - -#ifndef LUABUF_FUNC -#define LUABUF_FUNC LUAI_FUNC -#endif - - - -/* -** Some sizes are better limited to fit in 'int', but must also fit in -** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) -*/ -#define LUABUF_MAXSIZE \ - (sizeof(size_t) < sizeof(int) ? (~(size_t)0) : (size_t)(INT_MAX)) - - - - -LUABUF_FUNC lua_Integer luastreamI_posrelat (lua_Integer pos, size_t len); -LUABUF_FUNC int luastreamI_str2byte (lua_State *L, const char *s, size_t l); -LUABUF_FUNC void luastreamI_code2char (lua_State *L, int idx, char *p, int n); - - -#endif diff --git a/test/testall.lua b/test/testall.lua index 28a233c..ccf4f3c 100644 --- a/test/testall.lua +++ b/test/testall.lua @@ -1,8 +1,4 @@ --- $Id$ -local buffer = require "buffer" -local stream = require "buffer.stream" - -print('testing buffers and buffer functions') +local memory = require "memory" local maxi, mini = math.maxinteger, math.mininteger @@ -12,546 +8,266 @@ local function checkerror(msg, f, ...) assert(not s and string.find(err, msg)) end --- testing stream.isbuffer(string) -assert(stream.isbuffer(nil) == false) -assert(stream.isbuffer(true) == false) -assert(stream.isbuffer(false) == false) -assert(stream.isbuffer(0) == false) -assert(stream.isbuffer(2^70) == false) -assert(stream.isbuffer('123') == false) -assert(stream.isbuffer({}) == false) -assert(stream.isbuffer(function () end) == false) -assert(stream.isbuffer(print) == false) -assert(stream.isbuffer(coroutine.running()) == false) -assert(stream.isbuffer(io.stdout) == false) - --- testing stream.isbuffer(string), buffer:set(i, d), buffer:get(i) +-- memory.type(string), memory:set(i, d), memory:get(i) local checkmodifiable do local allchars = {} for i = 255, 0, -1 do allchars[#allchars+1] = string.char(i) end allchars = table.concat(allchars) + local types = { + fixed = true, + resizable = true, + } function checkmodifiable(b, size) - assert(stream.isbuffer(b) == true) - assert(stream.len(b) == size) + assert(types[memory.type(b)] ~= nil) + assert(memory.len(b) == size) assert(#b == size) for i = 1, size do - buffer.set(b, i, math.max(0, 256-i)) + memory.set(b, i, math.max(0, 256-i)) end for i = 1, size do - assert(buffer.get(b, i) == math.max(0, 256-i)) + assert(memory.get(b, i) == math.max(0, 256-i)) end local expected = (size > #allchars) and allchars..string.rep("\0", size - #allchars) or allchars:sub(1, size) - assert(stream.diff(b, expected) == nil) + assert(memory.diff(b, expected) == nil) end end --- testing buffer:set(i, d) -do - local b = buffer.create(10) - checkerror("value out of range", buffer.set, b, 1, 256) - checkerror("value out of range", buffer.set, b, 1, 511) - checkerror("value out of range", buffer.set, b, 1, -1) -end - -do -- testing buffer.create(string), stream.diff, stream.len, #buffer - local function check(data, str, expi, explt) - local b = buffer.create(data) - local i, lt = stream.diff(b, str) - assert(i == expi) - assert(lt == explt) - local i, lt = stream.diff(b, buffer.create(str)) - assert(i == expi) - assert(lt == explt) - checkmodifiable(b, #data) +local function newresizable(s, i, j) + local m = memory.create() + if type(s) == "number" then + memory.resize(m, s) + else + memory.resize(m, #s) + memory.fill(m, s, i, j) end - check('alo', 'alo1', 4, true) - check('', 'a', 1, true) - check('alo\0alo', 'alo\0b', 5, true) - check('alo\0alo\0\0', 'alo\0alo\0', 9, false) - check('alo', 'alo\0', 4, true) - check('alo\0', 'alo', 4, false) - check('\0', '\1', 1, true) - check('\0\0', '\0\1', 2, true) - check('\1\0a\0a', '\1\0a\0a', nil, false) - check('\1\0a\0b', '\1\0a\0a', 5, false) - check('\0\0\0', '\0\0\0\0', 4, true) - check('\0\0\0\0', '\0\0\0', 4, false) - check('\0\0\0', '\0\0\0\0', 4, true) - check('\0\0\0', '\0\0\0', nil, false) - check('\0\0b', '\0\0a\0', 3, false) + return m end -do -- testing buffer.create(size) - local function check(size) - checkmodifiable(buffer.create(size), size) +for kind, newmem in pairs{fixedsize=memory.create, resizable=newresizable} do + + do print(kind, "memory.type(value)") + assert(memory.type(nil) == nil) + assert(memory.type(true) == nil) + assert(memory.type(false) == nil) + assert(memory.type(0) == nil) + assert(memory.type(2^70) == nil) + assert(memory.type('123') == nil) + assert(memory.type({}) == nil) + assert(memory.type(function () end) == nil) + assert(memory.type(print) == nil) + assert(memory.type(coroutine.running()) == nil) + assert(memory.type(io.stdout) == nil) + assert(memory.type(memory.create()) == "resizable") + assert(memory.type(memory.create(10)) == "fixed") + assert(memory.type(memory.create("abc")) == "fixed") + assert(memory.type(memory.create("Lua Memory 1.0", 5, -5)) == "fixed") end - check(0) - check(1) - check(2) - check(100) - check(8192) -end -do -- testing buffer.create(buffer|string [, i [, j]]) - local function check(expected, data, ...) - local b = buffer.create(data, ...) - assert(stream.diff(b, expected) == nil) - checkmodifiable(b, #expected) - local b = buffer.create(buffer.create(data), ...) - assert(stream.diff(b, expected) == nil) - checkmodifiable(b, #expected) + do print(kind, "memory:set(i, d)") + local b = memory.create(10) + checkerror("value out of range", memory.set, b, 1, 256) + checkerror("value out of range", memory.set, b, 1, 511) + checkerror("value out of range", memory.set, b, 1, -1) end - check("" , "") - check("" , "", 1) - check("" , "", 1, -1) - check("" , "", mini, maxi) - check("234" , "123456789",2,4) - check("789" , "123456789",7) - check("" , "123456789",7,6) - check("7" , "123456789",7,7) - check("" , "123456789",0,0) - check("123456789", "123456789",-10,10) - check("123456789", "123456789",1,9) - check("" , "123456789",-10,-20) - check("9" , "123456789",-1) - check("6789" , "123456789",-4) - check("456" , "123456789",-6, -4) - check("123456" , "123456789", mini, -4) - check("123456789", "123456789", mini, maxi) - check("" , "123456789", mini, mini) - check("234" , "\000123456789",3,5) -end -do -- testing buffer:fill(stream [, i [, j]]) - local data = string.rep(" ", 10) - local full = "1234567890ABCDEF" - local function fillup(space) - return full:sub(1, #space) - end - local function check(expected, i, j) - for _, S in ipairs({tostring, buffer.create}) do - local b = buffer.create(data) - buffer.fill(b, S"", i, j) - assert(stream.diff(b, data) == nil) - buffer.fill(b, S"xuxu", i, j, 5) - assert(stream.diff(b, data) == nil) - buffer.fill(b, S"abc", i, j) - assert(stream.diff(b, expected) == nil) - buffer.fill(b, 0x55, i, j) - assert(stream.diff(b, expected:gsub("%S", "\x55")) == nil) - buffer.fill(b, S"XYZ", i, j, 3) - assert(stream.diff(b, expected:gsub("%S", "Z")) == nil) - buffer.fill(b, S"XYZ", i, j, -1) - assert(stream.diff(b, expected:gsub("%S", "Z")) == nil) - buffer.fill(b, S(full), i, j) - assert(stream.diff(b, expected:gsub("%S+", fillup)) == nil) + do print(kind, "memory.create(string), memory.diff, memory.len, #memory") + local function check(data, str, expi, explt) + local b = memory.create(data) + local i, lt = memory.diff(b, str) + assert(i == expi) + assert(lt == explt) + local i, lt = memory.diff(b, memory.create(str)) + assert(i == expi) + assert(lt == explt) + checkmodifiable(b, #data) end + check('alo', 'alo1', 4, true) + check('', 'a', 1, true) + check('alo\0alo', 'alo\0b', 5, true) + check('alo\0alo\0\0', 'alo\0alo\0', 9, false) + check('alo', 'alo\0', 4, true) + check('alo\0', 'alo', 4, false) + check('\0', '\1', 1, true) + check('\0\0', '\0\1', 2, true) + check('\1\0a\0a', '\1\0a\0a', nil, false) + check('\1\0a\0b', '\1\0a\0a', 5, false) + check('\0\0\0', '\0\0\0\0', 4, true) + check('\0\0\0\0', '\0\0\0', 4, false) + check('\0\0\0', '\0\0\0\0', 4, true) + check('\0\0\0', '\0\0\0', nil, false) + check('\0\0b', '\0\0a\0', 3, false) end - check("abcabcabca") - check("abcabcabca", 1) - check("abcabcabca", 1, -1) - check(" abc ", 2, 4) - check(" abca", 7) - check(" ", 7, 6) - check(" a ", 7, 7) - check("abcabcabca",-10, 10) - check("abcabcabc ", 1, 9) - check(" a",-1) - check(" abca",-4) - check(" abc ",-6, -4) - local function check(...) - local b = buffer.create(data) - checkerror("index out of bounds", buffer.fill, b, "xuxu", ...) - end - check( mini, maxi) - check( mini, mini) - check( mini, maxi) - check( 0, 0) - check(-10,-20) - check( mini, -4) - check( 3, maxi) - do - local b = buffer.create(full) - buffer.fill(b, b) - assert(stream.diff(b, full) == nil) - end - do - local b = buffer.create(full) - buffer.fill(b, 0) - assert(stream.diff(b, string.rep("\0", #full)) == nil) - end - do - local b = buffer.create(full) - buffer.fill(b, b, 11, -1) - assert(stream.diff(b, "1234567890123456") == nil) - end - do - local b = buffer.create(full) - buffer.fill(b, b, 1, 6, 11) - assert(stream.diff(b, "ABCDEF7890ABCDEF") == nil) - end - do - local b = buffer.create(full) - buffer.fill(b, b, 1, 10, 7) - assert(stream.diff(b, "7890ABCDEFABCDEF") == nil) - end - do - local b = buffer.create(full) - buffer.fill(b, b, 7, -1) - assert(stream.diff(b, "1234561234567890") == nil) - end -end -do -- testing stream.tostring(b|s [, i [, j]]), stream.byte(b|s [, i [, j]]) - local function check(data, ...) - local i,j = ... - local expected = string.sub(data, i or 1, j) - local s = data - assert(stream.tostring(s, ...) == expected) - if select("#", ...) >= 2 then - assert(string.char(stream.byte(s, ...)) == expected) - end - local s = buffer.create(data) - assert(stream.tostring(s, ...) == expected) - if select("#", ...) >= 2 then - assert(string.char(stream.byte(s, ...)) == expected) + do print(kind, "memory.create(size)") + local function check(size) + checkmodifiable(memory.create(size), size) end + check(0) + check(1) + check(2) + check(100) + check(8192) end - check("a") - check("\xe4") - check("\255") - check("\0") - check("\0\0alo\0x", -1) - check("ba", 2) - check("\n\n", 2, -1) - check("\n\n", 2, 2) - check("") - check("hi", -3) - check("hi", 3) - check("hi", 9, 10) - check("hi", 2, 1) - check("\0\255\0") - check("\0\xe4\0") - check("\xe4l\0óu", 1, -1) - check("\xe4l\0óu", 1, 0) - check("\xe4l\0óu", -10, 100) -end - - - - ---[======[ -do -- testing stream.concat(out, list [, sep [, i [, j]]]) - local function makelists(list) - local buffers = {} - local mixed = {} - for index, str in pairs(list) do - local b = buffer.create(str) - buffer[index] = b - mixed[index] = index%2==0 and str or b + do print(kind, "memory.create(memory|string [, i [, j]]), memory.tostring(memory, [, i [, j]])") + local function check(expected, data, ...) + local b = memory.create(data, ...) + assert(memory.diff(b, expected) == nil) + checkmodifiable(b, #expected) + local b = memory.create(memory.create(data), ...) + assert(memory.diff(b, expected) == nil) + checkmodifiable(b, #expected) + assert(memory.tostring(memory.create(data), ...) == expected) end - return { - strings = list, - buffers = buffers, - mixed = mixed, - } + check("" , "") + check("" , "", 1) + check("" , "", 1, -1) + check("" , "", mini, maxi) + check("234" , "123456789",2,4) + check("789" , "123456789",7) + check("" , "123456789",7,6) + check("7" , "123456789",7,7) + check("" , "123456789",0,0) + check("123456789", "123456789",-10,10) + check("123456789", "123456789",1,9) + check("" , "123456789",-10,-20) + check("9" , "123456789",-1) + check("6789" , "123456789",-4) + check("456" , "123456789",-6, -4) + check("123456" , "123456789", mini, -4) + check("123456789", "123456789", mini, maxi) + check("" , "123456789", mini, mini) + check("234" , "\000123456789",3,5) end - local function check(expected, lists, ...) - for _, list in pairs(lists) do - local str = stream.concat("string", list, ...) - assert(str == expected) - local buf = stream.concat("buffer", list, ...) - assert(stream.diff(buf, expected) == expected) - checkmodifiable(buf) + + do print(kind, "memory:find(string [, i [, j [, o]]])") + for _, C1 in ipairs({tostring, memory.create}) do + for _, C2 in ipairs({tostring, memory.create}) do + local m = C1"1234567890123456789" + local e = C1"" + local s = C2"345" + assert(memory.find(m, s) == 3) + local a, b = memory.find(m, s) + assert(a == 3) + assert(b == 5, b) + assert(memory.find(m, s, 3) == 3) + assert(memory.find(m, s, 4) == 13) + assert(memory.find(m, C2"346", 4) == nil) + assert(memory.find(m, s, -9) == 13) + assert(memory.find(m, s, 5, 1) == nil) + assert(memory.find(m, C2"\0", 5) == nil) + assert(memory.find(m, s, 1, -1, 4) == nil) + assert(memory.find(m, s, 20, 30) == nil) + assert(memory.find(m, s, -30, -20) == nil) + assert(memory.find(e, s, 1, -1, 4) == nil) + assert(memory.find(e, C2"") == nil) + assert(memory.find(e, C2"", 1) == nil) + assert(memory.find(e, C2"", 2) == nil) + assert(memory.find(e, C2"aaa", 1) == nil) + end end end - local empty = makelists{} - check("", empty) - check("", empty, 'x') - check("", empty, "x", maxi, maxi - 1) - check("", empty, "x", mini + 1, mini) - check("", empty, "x", maxi, mini) - check("\0.\0.\0\1.\0.\0\1\2", makelists{'\0', '\0\1', '\0\1\2'}, '.\0.') - local a = {}; for i=1,300 do a[i] = "xuxu" end - a = makelists(a) - check(string.rep("xuxu", 300, "123"), a, "123") - check("xuxu" , a, "b" , 20, 20) - check("xuxuxuxu" , a, "" , 20, 21) - check("" , a, "x" , 22, 21) - check("xuxu3xuxu" , a, "3" , 299) - check("alo" , makelists{[maxi]="alo" }, "x", maxi, maxi) - check("y-alo", makelists{[maxi]="alo",[maxi-1]="y"}, "-", maxi-1, maxi) - - assert(not pcall(stream.concat, {buffer.create"a", "b", {}})) - - a = makelists{"a","b","c"} - check("", a, ",", 1, 0) - check("a", a, ",", 1, 1) - check("a,b", a, ",", 1, 2) - check("b,c", a, ",", 2) - check("c", a, ",", 3) - check("", a, ",", 4) -end - --- testing string.find -assert(string.find("123456789", "345") == 3) -a,b = string.find("123456789", "345") -assert(string.sub("123456789", a, b) == "345") -assert(string.find("1234567890123456789", "345", 3) == 3) -assert(string.find("1234567890123456789", "345", 4) == 13) -assert(string.find("1234567890123456789", "346", 4) == nil) -assert(string.find("1234567890123456789", ".45", -9) == 13) -assert(string.find("abcdefg", "\0", 5, 1) == nil) -assert(string.find("", "") == 1) -assert(string.find("", "", 1) == 1) -assert(not string.find("", "", 2)) -assert(string.find('', 'aaa', 1) == nil) -assert(('alo(.)alo'):find('(.)', 1, 1) == 4) - --- testing string.byte/string.char -assert(string.byte("a") == 97) -assert(string.byte("\xe4") > 127) -assert(string.byte(string.char(255)) == 255) -assert(string.byte(string.char(0)) == 0) -assert(string.byte("\0") == 0) -assert(string.byte("\0\0alo\0x", -1) == string.byte('x')) -assert(string.byte("ba", 2) == 97) -assert(string.byte("\n\n", 2, -1) == 10) -assert(string.byte("\n\n", 2, 2) == 10) -assert(string.byte("") == nil) -assert(string.byte("hi", -3) == nil) -assert(string.byte("hi", 3) == nil) -assert(string.byte("hi", 9, 10) == nil) -assert(string.byte("hi", 2, 1) == nil) -assert(string.char() == "") -assert(string.char(0, 255, 0) == "\0\255\0") -assert(string.char(0, string.byte("\xe4"), 0) == "\0\xe4\0") -assert(string.char(string.byte("\xe4l\0óu", 1, -1)) == "\xe4l\0óu") -assert(string.char(string.byte("\xe4l\0óu", 1, 0)) == "") -assert(string.char(string.byte("\xe4l\0óu", -10, 100)) == "\xe4l\0óu") - -assert(string.upper("ab\0c") == "AB\0C") -assert(string.lower("\0ABCc%$") == "\0abcc%$") -assert(string.rep('teste', 0) == '') -assert(string.rep('tés\00tê', 2) == 'tés\0têtés\000tê') -assert(string.rep('', 10) == '') - -if string.packsize("i") == 4 then - -- result length would be 2^31 (int overflow) - checkerror("too large", string.rep, 'aa', (1 << 30)) - checkerror("too large", string.rep, 'a', (1 << 30), ',') -end - --- repetitions with separator -assert(string.rep('teste', 0, 'xuxu') == '') -assert(string.rep('teste', 1, 'xuxu') == 'teste') -assert(string.rep('\1\0\1', 2, '\0\0') == '\1\0\1\0\0\1\0\1') -assert(string.rep('', 10, '.') == string.rep('.', 9)) -assert(not pcall(string.rep, "aa", maxi // 2)) -assert(not pcall(string.rep, "", maxi // 2, "aa")) - -assert(string.reverse"" == "") -assert(string.reverse"\0\1\2\3" == "\3\2\1\0") -assert(string.reverse"\0001234" == "4321\0") - -for i=0,30 do assert(string.len(string.rep('a', i)) == i) end - -assert(type(tostring(nil)) == 'string') -assert(type(tostring(12)) == 'string') -assert(string.find(tostring{}, 'table:')) -assert(string.find(tostring(print), 'function:')) -assert(#tostring('\0') == 1) -assert(tostring(true) == "true") -assert(tostring(false) == "false") -assert(tostring(-1203) == "-1203") -assert(tostring(1203.125) == "1203.125") -assert(tostring(-0.5) == "-0.5") -assert(tostring(-32767) == "-32767") -if 2147483647 > 0 then -- no overflow? (32 bits) - assert(tostring(-2147483647) == "-2147483647") -end -if 4611686018427387904 > 0 then -- no overflow? (64 bits) - assert(tostring(4611686018427387904) == "4611686018427387904") - assert(tostring(-4611686018427387904) == "-4611686018427387904") -end - -if tostring(0.0) == "0.0" then -- "standard" coercion float->string - assert('' .. 12 == '12' and 12.0 .. '' == '12.0') - assert(tostring(-1203 + 0.0) == "-1203.0") -else -- compatible coercion - assert(tostring(0.0) == "0") - assert('' .. 12 == '12' and 12.0 .. '' == '12') - assert(tostring(-1203 + 0.0) == "-1203") -end + do print(kind, "memory:fill(string [, i [, j]])") + local data = string.rep(" ", 10) + local full = "1234567890ABCDEF" + local function fillup(space) + return full:sub(1, #space) + end + local function check(expected, i, j) + for _, S in ipairs({tostring, memory.create}) do + local b = memory.create(data) + memory.fill(b, S"", i, j) + assert(memory.diff(b, data) == nil) + memory.fill(b, S"xuxu", i, j, 5) + assert(memory.diff(b, data) == nil) + memory.fill(b, S"abc", i, j) + assert(memory.diff(b, expected) == nil) + memory.fill(b, 0x55, i, j) + assert(memory.diff(b, expected:gsub("%S", "\x55")) == nil) + memory.fill(b, S"XYZ", i, j, 3) + assert(memory.diff(b, expected:gsub("%S", "Z")) == nil) + memory.fill(b, S"XYZ", i, j, -1) + assert(memory.diff(b, expected:gsub("%S", "Z")) == nil) + memory.fill(b, S(full), i, j) + assert(memory.diff(b, expected:gsub("%S+", fillup)) == nil) + end + end + check("abcabcabca") + check("abcabcabca", 1) + check("abcabcabca", 1, -1) + check(" abc ", 2, 4) + check(" abca", 7) + check(" ", 7, 6) + check(" a ", 7, 7) + check("abcabcabca",-10, 10) + check("abcabcabc ", 1, 9) + check(" a",-1) + check(" abca",-4) + check(" abc ",-6, -4) + local function check(...) + local b = memory.create(data) + checkerror("index out of bounds", memory.fill, b, "xuxu", ...) + end + check( mini, maxi) + check( mini, mini) + check( mini, maxi) + check( 0, 0) + check(-10,-20) + check( mini, -4) + check( 3, maxi) + do + local b = memory.create(full) + memory.fill(b, b) + assert(memory.diff(b, full) == nil) + end + do + local b = memory.create(full) + memory.fill(b, 0) + assert(memory.diff(b, string.rep("\0", #full)) == nil) + end + do + local b = memory.create(full) + memory.fill(b, b, 11, -1) + assert(memory.diff(b, "1234567890123456") == nil) + end + do + local b = memory.create(full) + memory.fill(b, b, 1, 6, 11) + assert(memory.diff(b, "ABCDEF7890ABCDEF") == nil) + end + do + local b = memory.create(full) + memory.fill(b, b, 1, 10, 7) + assert(memory.diff(b, "7890ABCDEFABCDEF") == nil) + end + do + local b = memory.create(full) + memory.fill(b, b, 7, -1) + assert(memory.diff(b, "1234561234567890") == nil) + end + end -x = '"ílo"\n\\' -assert(string.format('%q%s', x, x) == '"\\"ílo\\"\\\n\\\\""ílo"\n\\') -assert(string.format('%q', "\0") == [["\0"]]) -assert(load(string.format('return %q', x))() == x) -x = "\0\1\0023\5\0009" -assert(load(string.format('return %q', x))() == x) -assert(string.format("\0%c\0%c%x\0", string.byte("\xe4"), string.byte("b"), 140) == - "\0\xe4\0b8c\0") -assert(string.format('') == "") -assert(string.format("%c",34)..string.format("%c",48)..string.format("%c",90)..string.format("%c",100) == - string.format("%c%c%c%c", 34, 48, 90, 100)) -assert(string.format("%s\0 is not \0%s", 'not be', 'be') == 'not be\0 is not \0be') -assert(string.format("%%%d %010d", 10, 23) == "%10 0000000023") -assert(tonumber(string.format("%f", 10.3)) == 10.3) -x = string.format('"%-50s"', 'a') -assert(#x == 52) -assert(string.sub(x, 1, 4) == '"a ') - -assert(string.format("-%.20s.20s", string.rep("%", 2000)) == - "-"..string.rep("%", 20)..".20s") -assert(string.format('"-%20s.20s"', string.rep("%", 2000)) == - string.format("%q", "-"..string.rep("%", 2000)..".20s")) - --- format x tostring -assert(string.format("%s %s", nil, true) == "nil true") -assert(string.format("%s %.4s", false, true) == "false true") -assert(string.format("%.3s %.3s", false, true) == "fal tru") -local m = setmetatable({}, {__tostring = function () return "hello" end}) -assert(string.format("%s %.10s", m, m) == "hello hello") - - -assert(string.format("%x", 0.0) == "0") -assert(string.format("%02x", 0.0) == "00") -assert(string.format("%08X", 4294967295) == "FFFFFFFF") -assert(string.format("%+08d", 31501) == "+0031501") -assert(string.format("%+08d", -30927) == "-0030927") - - -do -- longest number that can be formatted - local i = 1 - local j = 10000 - while i + 1 < j do -- binary search for maximum finite float - local m = (i + j) // 2 - if 10^m < math.huge then i = m else j = m end - end - assert(10^i < math.huge and 10^j == math.huge) - assert(string.len(string.format('%.99f', -(10^i))) > i) -end - - --- testing large numbers for format -do -- assume at least 32 bits - local max, min = 0x7fffffff, -0x80000000 -- "large" for 32 bits - assert(string.sub(string.format("%8x", -1), -8) == "ffffffff") - assert(string.format("%x", max) == "7fffffff") - assert(string.sub(string.format("%x", min), -8) == "80000000") - assert(string.format("%d", max) == "2147483647") - assert(string.format("%d", min) == "-2147483648") - assert(string.format("%u", 0xffffffff) == "4294967295") - assert(string.format("%o", 0xABCD) == "125715") - - max, min = 0x7fffffffffffffff, -0x8000000000000000 - if max > 2.0^53 then -- only for 64 bits - assert(string.format("%x", (2^52 | 0) - 1) == "fffffffffffff") - assert(string.format("0x%8X", 0x8f000003) == "0x8F000003") - assert(string.format("%d", 2^53) == "9007199254740992") - assert(string.format("%i", -2^53) == "-9007199254740992") - assert(string.format("%x", max) == "7fffffffffffffff") - assert(string.format("%x", min) == "8000000000000000") - assert(string.format("%d", max) == "9223372036854775807") - assert(string.format("%d", min) == "-9223372036854775808") - assert(string.format("%u", ~(-1 << 64)) == "18446744073709551615") - assert(tostring(1234567890123) == '1234567890123') - end -end - - -do print("testing 'format %a %A'") - local function matchhexa (n) - local s = string.format("%a", n) - -- result matches ISO C requirements - assert(string.find(s, "^%-?0x[1-9a-f]%.?[0-9a-f]*p[-+]?%d+$")) - assert(tonumber(s) == n) -- and has full precision - s = string.format("%A", n) - assert(string.find(s, "^%-?0X[1-9A-F]%.?[0-9A-F]*P[-+]?%d+$")) - assert(tonumber(s) == n) - end - for _, n in ipairs{0.1, -0.1, 1/3, -1/3, 1e30, -1e30, - -45/247, 1, -1, 2, -2, 3e-20, -3e-20} do - matchhexa(n) - end - - assert(string.find(string.format("%A", 0.0), "^0X0%.?0?P%+?0$")) - assert(string.find(string.format("%a", -0.0), "^%-0x0%.?0?p%+?0$")) - - if not _port then -- test inf, -inf, NaN, and -0.0 - assert(string.find(string.format("%a", 1/0), "^inf")) - assert(string.find(string.format("%A", -1/0), "^%-INF")) - assert(string.find(string.format("%a", 0/0), "^%-?nan")) - assert(string.find(string.format("%a", -0.0), "^%-0x0")) - end - - if not pcall(string.format, "%.3a", 0) then - (Message or print)("\n >>> modifiers for format '%a' not available <<<\n") - else - assert(string.find(string.format("%+.2A", 12), "^%+0X%x%.%x0P%+?%d$")) - assert(string.find(string.format("%.4A", -12), "^%-0X%x%.%x000P%+?%d$")) - end -end - - --- errors in format - -local function check (fmt, msg) - checkerror(msg, string.format, fmt, 10) end -local aux = string.rep('0', 600) -check("%100.3d", "too long") -check("%1"..aux..".3d", "too long") -check("%1.100d", "too long") -check("%10.1"..aux.."004d", "too long") -check("%t", "invalid option") -check("%"..aux.."d", "repeated flags") -check("%d %d", "no value") - - -assert(load("return 1\n--comment without ending EOL")() == 1) - - +do print "memory.resize(m, size)" + local m = memory.create(3) + checkerror("resizable memory expected", memory.resize, m, 10) -if not _port then + local m = memory.create() + memory.resize(m, 10) + assert(tostring(m) == string.rep('\0', 10)) - local locales = { "ptb", "ISO-8859-1", "pt_BR" } - local function trylocale (w) - for i = 1, #locales do - if os.setlocale(locales[i], w) then return true end - end - return false - end - - if not trylocale("collate") then - print("locale not supported") - else - assert("alo" < "álo" and "álo" < "amo") - end - - if not trylocale("ctype") then - print("locale not supported") - else - assert(string.gsub("áéíóú", "%a", "x") == "xxxxx") - assert(string.gsub("áÁéÉ", "%l", "x") == "xÁxÉ") - assert(string.gsub("áÁéÉ", "%u", "x") == "áxéx") - assert(string.upper"áÁé{xuxu}ção" == "ÁÁÉ{XUXU}ÇÃO") - end - - os.setlocale("C") - assert(os.setlocale() == 'C') - assert(os.setlocale(nil, "numeric") == 'C') + memory.fill(m, "abcde") + memory.resize(m, 15) + assert(tostring(m) == "abcdeabcde\0\0\0\0\0") + memory.resize(m, 5) + assert(tostring(m) == "abcde") end ---]======] - -print('OK') +print "OK" diff --git a/test/testlayout.lua b/test/testlayout.lua new file mode 100644 index 0000000..ae46286 --- /dev/null +++ b/test/testlayout.lua @@ -0,0 +1,355 @@ +local memory = require "memory" +local layout = require "memory.layout" + +local function asserterr(msg, f, ...) + local ok, res = xpcall(f, debug.traceback, ...) + assert(ok == false) + assert(string.find(res, msg, 1, true) ~= nil, res) +end + +local function assertbytes(m, ...) + local count = select("#", ...) + for i = 1, count do + assert(memory.get(m, i) == select(i, ...), + string.format("%d: %02x ~= %02x", i, memory.get(m, i), select(i, ...))) + end + assert(memory.len(m) == count) +end + +local native = string.unpack("B", string.pack("I2", 1)) == 0 and "big" or "little" +local function testfields(spec) + for _, endian in ipairs{"big", "little", "native", false} do + for _, field in ipairs(spec.struct) do + field.endian = endian or nil + end + local p = layout.newpointer(layout.newstruct(spec.struct)) + for i, field in ipairs(spec.struct) do + if field.key ~= nil then + asserterr("out of bounds", function () local _ = p[field.key] end) + asserterr("out of bounds", function () p[field.key] = 0 end) + end + end + local m = memory.create(spec.length) + layout.setpointer(p, m) + for _, field in ipairs(spec.struct) do + p[field.key] = spec.values[field.key] + end + assertbytes(m, table.unpack(spec[endian] or spec[native])) + for _, field in ipairs(spec.struct) do + if field.key ~= nil then + asserterr("unsigned overflow", function () + p[field.key] = 1<<(field.bits or field.bytes*8) + end) + end + assert(p[field.key] == spec.values[field.key]) + end + assertbytes(m, table.unpack(spec[endian] or spec[native])) + end +end + +testfields{ + length = 8, + struct = { + { key = "a", bits = 1 }, + { key = "b", bits = 2 }, + { key = "c", bits = 3 }, + { key = "d", bits = 4 }, + { key = "e", bits = 5 }, + { key = "f", bits = 6 }, + { key = "g", bits = 7 }, + { key = "h", bits = 8 }, + { key = "i", bits = 12 }, + { key = "j", bits = 6 }, + { key = "k", bits = 1 }, + { key = "l", bits = 2 }, + { key = "m", bits = 3 }, + { key = "n", bits = 4 }, + }, + values = { + a = 0x01, -- 1 + b = 0x01, -- 01 + c = 0x03, -- 011 + d = 0x0b, -- 1011 + e = 0x13, -- 1 0011 + f = 0x25, -- 10 0101 + g = 0x45, -- 100 0101 + h = 0xc9, -- 1100 1001 + i = 0x821, -- 1000 0010 0001 + j = 0x25, -- 10 0101 + k = 0x00, -- 0 + l = 0x02, -- 10 + m = 0x04, -- 100 + n = 0x0d, -- 1101 + }, + big = { + -- abbc ccdd + 0xae, -- 1010 1110 + -- ddee eeef + 0xe7, -- 1110 0111 + -- ffff fggg + 0x2c, -- 0010 1100 + -- gggg hhhh + 0x5c, -- 0101 1100 + -- hhhh iiii + 0x98, -- 1001 1000 + -- iiii iiii + 0x21, -- 0010 0001 + -- jjjj jjkl + 0x95, -- 1001 0101 + -- lmmm nnnn + 0x4d, -- 0100 1101 + }, + little = { + -- ddcc cbba + 0xdb, -- 1101 1011 + -- feee eedd + 0xce, -- 1100 1110 + -- gggf ffff + 0xb2, -- 1011 0010 + -- hhhh gggg + 0x98, -- 1001 1000 + -- iiii hhhh + 0x1c, -- 0001 1100 + -- iiii iiii + 0x82, -- 1000 0010 + -- lkjj jjjj + 0x25, -- 0010 0101 + -- nnnn mmml + 0xd9, -- 1101 1001 + }, +} + +testfields{ + length = 8, + struct = { + { key = "a", bits = 9 }, + { key = "b", bits = 17 }, + { key = "c", bits = 25 }, + { key = "d", bits = 13 }, + }, + values = { + a = 0x002, -- 0 0000 0010 + b = 0x08d15, -- 0 1000 1101 0001 0101 + c = 0x13c4d5e, -- 1 0011 1100 0100 1101 0101 1110 + d = 0x0def, -- 0 1101 1110 1111 + }, + big = { + -- aaaa aaaa + 0x01, -- 0000 0001 + -- abbb bbbb + 0x23, -- 0010 0011 + -- bbbb bbbb + 0x45, -- 0100 0101 + -- bbcc cccc + 0x67, -- 0110 0111 + -- cccc cccc + 0x89, -- 1000 1001 + -- cccc cccc + 0xab, -- 1010 1011 + -- cccd dddd + 0xcd, -- 1100 1101 + -- dddd dddd + 0xef, -- 1110 1111 + }, + little = { + -- aaaa aaaa + 0x02, -- 0000 0010 + -- bbbb bbba + 0x2a, -- 0010 1010 + -- bbbb bbbb + 0x1a, -- 0001 1010 + -- cccc ccbb + 0x79, -- 0111 1001 + -- cccc cccc + 0x35, -- 0011 0101 + -- cccc cccc + 0xf1, -- 1111 0001 + -- dddd dccc + 0x7c, -- 0111 1100 + -- dddd dddd + 0x6f, -- 0110 1111 + }, +} + +testfields{ + length = 6, + struct = { + { key = "one", bytes = 1 }, + { key = "two", bytes = 2 }, + { key = "three", bytes = 3 }, + }, + values = { + one = 0x12, + two = 0x3456, + three = 0x789abc, + }, + big = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc }, + little = { 0x12, 0x56, 0x34, 0xbc, 0x9a, 0x78 }, +} + +testfields{ + length = 5, + struct = { + { key = "bits", bits = 3 }, + { key = "bytes", bytes = 3 }, + { bits = 3 }, + { key = "morebits", bits = 3 }, + }, + values = { + bits = 0x2, + bytes = 0xaaf00f, + morebits = 0x5, + }, + big = { 0x40, 0xaa, 0xf0, 0x0f, 0x14 }, + little = { 0x02, 0x0f, 0xf0, 0xaa, 0x28 }, +} + +do + local p = layout.newpointer(layout.newstruct{ + { key = "bigbytes", bytes = 2, endian = "big" }, + { key = "littlebytes", bytes = 2, endian = "little" }, + { key = "fewbigbits", bits = 3, endian = "big" }, + { key = "manybigbits", bits = 13, endian = "big" }, + { key = "fewlittlebits", bits = 3, endian = "little" }, + { key = "manylittlebits", bits = 13, endian = "little" }, + }) + local m = memory.create(8) + layout.setpointer(p, m) + + memory.set(m, 1, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef) + assert(p.bigbytes == 0x0123) + assert(p.littlebytes == 0x6745) + assert(p.fewbigbits == 0x4) -- 100 + assert(p.manybigbits == 0x9ab) -- 0 1001 1010 1011 + assert(p.fewlittlebits == 0x5) -- [101] + assert(p.manylittlebits == 0x1df9) --..1100 1] [1110 1111.. + p.bigbytes = 0xfedc ; assertbytes(m, 0xfe, 0xdc, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef) + p.littlebytes = 0x98ba ; assertbytes(m, 0xfe, 0xdc, 0xba, 0x98, 0x89, 0xab, 0xcd, 0xef) + -- 100|0 1001 1010 1011 --> 011|0 1001 1010 1011 + p.fewbigbits = 0x3 ; assertbytes(m, 0xfe, 0xdc, 0xba, 0x98, 0x69, 0xab, 0xcd, 0xef) + -- 011|0 1001 1010 1011 --> 011|1 0110 0101 0100 + p.manybigbits = 0x1654 ; assertbytes(m, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0xcd, 0xef) + --..1100 1][101] [1110 1111.. --> ..1100 1][010] [1110 1111.. + p.fewlittlebits = 0x2 ; assertbytes(m, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0xca, 0xef) + --..1100 1][010] [1110 1111.. --> ..0011 0][010] [0001 0000.. + p.manylittlebits = 0x206; assertbytes(m, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10) +end + +do + local p = layout.newpointer(layout.newstruct{ + { key = "bit", bits = 1, type = "boolean", endian = "little" }, + { key = "bits", bits = 4, type = "boolean", endian = "little" }, + { key = "byte", bytes = 1, type = "boolean", endian = "native" }, + { key = "little", bytes = 2, type = "boolean", endian = "little" }, + { key = "big", bytes = 2, type = "boolean", endian = "big" }, + }) + local m = memory.create(6) + layout.setpointer(p, m) + + memory.fill(m, 0xff) + assert(p.bit == true) + assert(p.bits == true) + assert(p.byte == true) + assert(p.little == true) + assert(p.big == true) + memory.fill(m, 0) + assert(p.bit == false) + assert(p.bits == false) + assert(p.byte == false) + assert(p.little == false) + assert(p.big == false) + p.bit = true ; assert(p.bit == true) ; assertbytes(m, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00) + p.bits = true ; assert(p.bits == true) ; assertbytes(m, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00) + p.byte = true ; assert(p.byte == true) ; assertbytes(m, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00) + p.little = true; assert(p.little == true); assertbytes(m, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00) + p.big = true ; assert(p.big == true) ; assertbytes(m, 0x03, 0x01, 0x01, 0x00, 0x00, 0x01) +end + +do + for _, bits in ipairs{1, 2, 3, 4, 7, 9, 15, 17} do + asserterr("unsupported type", + layout.newstruct, {{ key = "unsupported", bits = bits, type = "string" }}) + for _, size in ipairs{8, 16, 32, 64} do + asserterr("unsupported type", + layout.newstruct, { + { bits = bits }, + { key = "unsupported", bits = size, type = "string" }, + }) + end + end + + local p = layout.newpointer(layout.newstruct{ + { key = "one", bytes = 1, type = "string" }, + { key = "two", bytes = 2, type = "string" }, + { key = "eight", bytes = 8, type = "string" }, + { key = "nine", bytes = 9, type = "string" }, + }) + local m = memory.create(20) + layout.setpointer(p, m) + + memory.fill(m, 0) + assert(p.one == string.rep("\0", 1)) + assert(p.two == string.rep("\0", 2)) + assert(p.eight == string.rep("\0", 8)) + assert(p.nine == string.rep("\0", 9)) + + local function assertmem(f, i, j) + local v = string.rep("\x55", 1+j-i) + p[f] = v + assert(p[f] == v) + for k = 1, i-1 do assert(memory.get(m, k) == 0) end + for k = i, j do assert(memory.get(m, k) == 0x55) end + for k = j+1, memory.len(m) do assert(memory.get(m, k) == 0) end + memory.fill(m, 0) + end + assertmem("one", 1, 1) + assertmem("two", 2, 3) + assertmem("eight", 4, 11) + assertmem("nine", 12, 20) +end + +do + local p = layout.newpointer(layout.newstruct{ + { bits = 2 }, + { key = "half", bits = 4, endian = "little" }, + { key = "double", bytes = 2, endian = "big" }, + { key = "nested", type = "struct", + { bits = 2 }, + { key = "half", bits = 4, endian = "big" }, + { key = "double", bytes = 2, endian = "little" }, + { key = "single", bytes = 1 }, + }, + { key = "single", bytes = 1 }, + }) + local m = memory.create(8) + layout.setpointer(p, m) + + memory.set(m, 1, 0x10, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef) + assert(p.half == 0x4) + assert(p.double == 0x2345) + assert(p.nested.half == 0x9) + assert(p.nested.double == 0xab89) + assert(p.nested.single == 0xcd) + assert(p.single == 0xef) + + memory.fill(m, 0x55) + p.half = 0xa ; assertbytes(m, 0x69, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55) + p.double = 0xaaf0 ; assertbytes(m, 0x69, 0xaa, 0xf0, 0x55, 0x55, 0x55, 0x55, 0x55) + p.nested.half = 0xa ; assertbytes(m, 0x69, 0xaa, 0xf0, 0x69, 0x55, 0x55, 0x55, 0x55) + p.nested.double = 0xaaf0; assertbytes(m, 0x69, 0xaa, 0xf0, 0x69, 0xf0, 0xaa, 0x55, 0x55) + p.nested.single = 0xaa ; assertbytes(m, 0x69, 0xaa, 0xf0, 0x69, 0xf0, 0xaa, 0xaa, 0x55) + p.single = 0xaa ; assertbytes(m, 0x69, 0xaa, 0xf0, 0x69, 0xf0, 0xaa, 0xaa, 0xaa) + + local p2 = layout.newpointer(layout.newstruct{ + { bytes = 1 }, + { key = "nested", type = "struct", { bytes = 4 } }, + }) + local m2 = memory.create(6) + layout.setpointer(p2, m2) + p2.nested = p.nested ; assertbytes(m2, 0x00, 0x69, 0xf0, 0xaa, 0xaa, 0x00) + + memory.fill(m2, 0xff) + p.nested = p2.nested ; assertbytes(m, 0x69, 0xaa, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xaa) +end + +print("Success!") diff --git a/test/tpack.lua b/test/tpack.lua index 2cc8739..d070a3a 100644 --- a/test/tpack.lua +++ b/test/tpack.lua @@ -1,8 +1,61 @@ --- $Id: tpack.lua,v 1.10 2014/12/26 17:20:53 roberto Exp $ -local buffer = require "buffer" -local pack = buffer.pack +local memory = require "memory" +local pack = memory.pack local packsize = string.packsize -local unpack = buffer.unpack +local unpack = memory.unpack + +do + local function failpack(expectpos, expectvals, ok, pos, ...) + assert(ok == false) + assert(pos == expectpos) + for i, value in ipairs(expectvals) do + assert(value == select(i, ...)) + end + assert(select("#", ...) == #expectvals) + end + + local m = memory.create(0) + failpack(1, {123,456,789}, pack(m, "b", 1, 123,456,789)) + + local m = memory.create(10) + memory.fill(m, 0x55) + local values = { + 0x11111111, + 0x22222222, + 0x33333333, + 0x44444444, + } + local expectvals = { + 0x33333333, + 0x44444444, + } + failpack(9, expectvals, pack(m, "i4i4i4i4", 1, table.unpack(values))) +end + +--[[ +NOTE: most of the code below is copied from the tests of Lua 5.3.1 by + R. Ierusalimschy, L. H. de Figueiredo, W. Celes - Lua.org, PUC-Rio. + +Copyright (C) 1994-2015 Lua.org, PUC-Rio. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--]] print "testing pack/unpack" @@ -40,10 +93,10 @@ function checkerror (msg, f, ...) assert(not status and string.find(err, msg)) end -local function assertpack(sz, n, ok, pos, validx) - assert(ok) +local function assertpack(sz, n, ok, pos, ...) + assert(ok == true) assert(pos == sz+1) - assert(validx == n+1) + assert(select("#", ...) == 0) end local function assertunpack(sz, vals, ...) @@ -54,75 +107,77 @@ local function assertunpack(sz, vals, ...) return ... end --- minimum behavior for integer formats +print("minimum behavior for integer formats") do - local function testpack(fmt, val) - local sz = packsize(fmt) - local b = buffer.create(sz) - assertpack(sz, 1, pack(b, 1, fmt, val)) - assertunpack(sz, {val}, unpack(b, 1, fmt)) + local function testpack(fmt, val, pos) + local sz = packsize(fmt)+pos-1 + local b = memory.create(sz) + assertpack(sz, 1, pack(b, fmt, pos, val)) + assertunpack(sz, {val}, unpack(b, fmt, pos)) + end + for i=1, 10 do + testpack("B", 0xff, i) + testpack("b", 0x7f, i) + testpack("b", -0x80, i) + testpack("H", 0xffff, i) + testpack("h", 0x7fff, i) + testpack("h", -0x8000, i) + testpack("L", 0xffffffff, i) + testpack("l", 0x7fffffff, i) + testpack("l", -0x80000000, i) end - testpack("B", 0xff) - testpack("b", 0x7f) - testpack("b", -0x80) - testpack("H", 0xffff) - testpack("h", 0x7fff) - testpack("h", -0x8000) - testpack("L", 0xffffffff) - testpack("l", 0x7fffffff) - testpack("l", -0x80000000) end for i = 1, NB do -- small numbers with signal extension ("\xFF...") local s = string.rep("\xff", i) - local b = buffer.create(i) - assertpack(i, 1, pack(b, 1, "i" .. i, -1)) + local b = memory.create(i) + assertpack(i, 1, pack(b, "i" .. i, 1, -1)) assert(tostring(b) == s) - assertunpack(i, {-1}, unpack(b, 1, "i" .. i)) + assertunpack(i, {-1}, unpack(b, "i" .. i)) -- small unsigned number ("\0...\xAA") s = "\xAA" .. string.rep("\0", i - 1) - assertpack(i, 1, pack(b, 1, "I" .. i, 0xAA)) + assertunpack(i, {0xAA}, unpack(b, "I" .. i, 1, 0xAA)) assert(tostring(b) == s:reverse()) - assertunpack(i, {0xAA}, unpack(b, 1, ">I" .. i)) + assertunpack(i, {0xAA}, unpack(b, ">I" .. i)) end do - local b = buffer.create(sizeLI+1) + local b = memory.create(sizeLI+1) local lnum = 0x13121110090807060504030201 - assertpack(sizeLI, 1, pack(b, 1, "i" .. i)) + assertunpack(i, {-lnum}, unpack(b, ">i" .. i)) -- overflows - buffer.fill(b, 0, 1, i-1) - buffer.set(b, i, 1) - checkerror("does not fit", unpack, b, 1, "i" .. i) + memory.fill(b, 0, 1, i-1) + memory.set(b, i, 1) + checkerror("does not fit", unpack, b, "i" .. i) end end @@ -131,106 +186,104 @@ for i = 1, sizeLI do local lnum = 0x13121110090807060504030201 local n = lnum & (~(-1 << (i * 8))) local s = string.sub(lstr, 1, i) - local b = buffer.create(i) - assertpack(i, 1, pack(b, 1, "i" .. i, n)) + assertpack(i, 1, pack(b, ">i" .. i, 1, n)) assert(tostring(b) == s:reverse()) - assertunpack(i, {n}, unpack(b, 1, ">i" .. i)) + assertunpack(i, {n}, unpack(b, ">i" .. i)) end --- sign extension +print("sign extension") do local u = 0xf0 for i = 1, sizeLI - 1 do - local b = buffer.create(i) - buffer.set(b, 1, 0xf0) + local b = memory.create(i) + memory.set(b, 1, 0xf0) if i>=2 then - buffer.fill(b, 0xff, 2, i) + memory.fill(b, 0xff, 2, i) end - assertunpack(i, {-16}, unpack(b, 1, "I"..i)) + assertunpack(i, {-16}, unpack(b, "I"..i)) u = u * 256 + 0xff end end --- mixed endianness +print("mixed endianness") do - local b = buffer.create(4) - assertpack(4, 2, pack(b, 1, ">i2 i2 i2")) - assertpack(4, 1, pack(b, 1, "=i4", 2001)) + memory.fill(b, "\10\0\0\20") + assertunpack(4, {10, 20}, unpack(b, "i2")) + assertpack(4, 1, pack(b, "=i4", 1, 2001)) local s = tostring(b) - assertpack(4, 1, pack(b, 1, "i4", 2001)) + assertpack(4, 1, pack(b, "i4", 1, 2001)) assert(tostring(b) == s) end -print("testing invalid formats") - +print("invalid formats") do - local b = buffer.create(math.max(16, NB+1)) - checkerror("out of limits", pack, b, 1, "i0", 0) - checkerror("out of limits", pack, b, 1, "i" .. NB + 1, 0) - checkerror("out of limits", pack, b, 1, "!" .. NB + 1, 0) - checkerror("%(17%) out of limits %[1,16%]", pack, b, 1, "Xi" .. NB + 1) - checkerror("invalid format option 'r'", pack, b, 1, "i3r", 0) - buffer.fill(b, 16, 1, 16) - checkerror("16%-byte integer", unpack, b, 1, "i16") - checkerror("not power of 2", pack, b, 1, "!4i3", 0); - checkerror("missing size", pack, b, 1, "c", "") + local b = memory.create(math.max(16, NB+1)) + checkerror("out of limits", pack, b, "i0", 1, 0) + checkerror("out of limits", pack, b, "i" .. NB + 1, 1, 0) + checkerror("out of limits", pack, b, "!" .. NB + 1, 1, 0) + checkerror("%(17%) out of limits %[1,16%]", pack, b, "Xi" .. NB + 1, 1) + checkerror("invalid format option 'r'", pack, b, "i3r", 1, 0) + memory.fill(b, 16, 1, 16) + checkerror("16%-byte integer", unpack, b, "i16", 1) + checkerror("not power of 2", pack, b, "!4i3", 1, 0); + checkerror("missing size", pack, b, "c", 1, "") end --- overflow in packing +print("overflow in packing") for i = 1, sizeLI - 1 do - local b = buffer.create(i) + local b = memory.create(i) local umax = (1 << (i * 8)) - 1 local max = umax >> 1 local min = ~max - checkerror("overflow", pack, b, 1, "I" .. i, umax + 1) - - checkerror("overflow", pack, b, 1, ">i" .. i, umax) - checkerror("overflow", pack, b, 1, ">i" .. i, max + 1) - checkerror("overflow", pack, b, 1, "i" .. i, max)) - assertunpack(i, {max}, unpack(b, 1, ">i" .. i)) - assertpack(i, 1, pack(b, 1, "I" .. i, umax)) - assertunpack(i, {umax}, unpack(b, 1, ">I" .. i)) + checkerror("overflow", pack, b, "I" .. i, 1, umax + 1) + + checkerror("overflow", pack, b, ">i" .. i, 1, umax) + checkerror("overflow", pack, b, ">i" .. i, 1, max + 1) + checkerror("overflow", pack, b, "i" .. i, 1, max)) + assertunpack(i, {max}, unpack(b, ">i" .. i)) + assertpack(i, 1, pack(b, "I" .. i, 1, umax)) + assertunpack(i, {umax}, unpack(b, ">I" .. i)) end --- Lua integer size +print("Lua integer size") do - local b = buffer.create(sizeLI) - assertpack(sizeLI, 1, pack(b, 1, ">j", math.maxinteger)) - assertunpack(sizeLI, {math.maxinteger}, unpack(b, 1, ">j")) - assertpack(sizeLI, 1, pack(b, 1, "j", 1, math.maxinteger)) + assertunpack(sizeLI, {math.maxinteger}, unpack(b, ">j")) + assertpack(sizeLI, 1, pack(b, "f", 24) + pack(b2, ">f", 1, 24) end assert(tostring(b1) == tostring(b2)) end -do return end +do return print("OK") end print "testing pack/unpack of floating-point numbers" - for _, n in ipairs{0, -1.1, 1.9, 1/0, -1/0, 1e20, -1e20, 0.1, 2000.7} do assert(unpack("n", pack("n", n)) == n) assert(unpack("