├── .gitignore ├── Makefile ├── README.rst ├── lbuffer-scm-0.rockspec ├── lbuffer.c ├── lbuffer.h ├── lbufflib.c ├── lua_with_lbuffer ├── linit_modified.c └── luavs_lbuffer.bat └── test.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.obj 4 | *.dll 5 | *.lib 6 | *.exp 7 | *.exe 8 | *.lua 9 | *.html 10 | *~ 11 | !test.lua 12 | tags* 13 | vs2013/ 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This Makefile is based on LuaSec's Makefile. Thanks to the LuaSec developers. 2 | # Inform the location to intall the modules 3 | LUAPATH = /usr/share/lua/5.1 4 | LUACPATH = /usr/lib/lua/5.1 5 | INCDIR = -I/usr/include/lua5.1 6 | LIBDIR = -L/usr/lib 7 | 8 | # For Mac OS X: set the system version 9 | MACOSX_VERSION = 10.4 10 | 11 | PLAT = none 12 | DEFS = 13 | CMOD = buffer.so 14 | OBJS = lbuffer.o lbufflib.o 15 | 16 | LIBS = -llua 17 | WARN = -Wall -pedantic 18 | 19 | BSD_CFLAGS = -O2 -fPIC 20 | BSD_LDFLAGS = -O -shared -fPIC $(LIBDIR) 21 | 22 | LNX_CFLAGS = -O2 -fPIC 23 | LNX_LDFLAGS = -O -shared -fPIC $(LIBDIR) 24 | 25 | MAC_ENV = env MACOSX_DEPLOYMENT_TARGET='$(MACVER)' 26 | MAC_CFLAGS = -O2 -fPIC -fno-common 27 | MAC_LDFLAGS = -bundle -undefined dynamic_lookup -fPIC $(LIBDIR) 28 | 29 | MGW_INCDIR = -Id:/lua/include 30 | MGW_LIBS = d:/lua/lua51.dll 31 | MGW_CMOD = buffer.dll 32 | MGW_CFLAGS = -O2 -mdll -DLUA_BUILD_AS_DLL 33 | MGW_LDFLAGS = -mdll 34 | 35 | CC = gcc 36 | LD = $(MYENV) gcc 37 | CFLAGS = $(MYCFLAGS) $(WARN) $(INCDIR) $(DEFS) 38 | LDFLAGS = $(MYLDFLAGS) $(LIBDIR) 39 | 40 | .PHONY: test clean install none linux bsd macosx 41 | 42 | none: 43 | @echo "Usage: $(MAKE) " 44 | @echo " * linux" 45 | @echo " * bsd" 46 | @echo " * macosx" 47 | @echo " * mingw" 48 | 49 | install : $(CMOD) 50 | cp $(CMOD) $(LUACPATH) 51 | 52 | uninstall: 53 | -rm $(LUACPATH)/$(CMOD) 54 | 55 | linux: 56 | @$(MAKE) $(CMOD) PLAT=linux MYCFLAGS="$(LNX_CFLAGS)" MYLDFLAGS="$(LNX_LDFLAGS)" 57 | 58 | bsd: 59 | @$(MAKE) $(CMOD) PLAT=bsd MYCFLAGS="$(BSD_CFLAGS)" MYLDFLAGS="$(BSD_LDFLAGS)" 60 | 61 | macosx: 62 | @$(MAKE) $(CMOD) PLAT=macosx MYCFLAGS="$(MAC_CFLAGS)" MYLDFLAGS="$(MAC_LDFLAGS)" MYENV="$(MAC_ENV)" 63 | 64 | mingw: 65 | $(MAKE) $(MGW_CMOD) PLAT=mingw CMOD="$(MGW_CMOD)" MYCFLAGS="$(MGW_CFLAGS)" MYLDFLAGS="$(MGW_LDFLAGS)" INCDIR="$(MGW_INCDIR)" LIBS="$(MGW_LIBS)" 66 | 67 | test: 68 | lua ./test.lua 69 | 70 | clean: 71 | -rm -f $(OBJS) $(CMOD) $(MGW_CMOD) 72 | 73 | .c.o: 74 | $(CC) $(CFLAGS) $< -c -o $@ 75 | 76 | $(CMOD) : $(OBJS) 77 | $(LD) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ 78 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | lbuffer - mutable string support to lua 2 | ========================================= 3 | 4 | lbuffer is a C module for lua, it provides mutable string feature to 5 | the lua_ language. it has all routines from lua's string module, add 6 | several modify functions. it provides: 7 | 8 | * change the value of buffer without copy it. 9 | * a pair of full feature pack/unpack functions. 10 | * get a subbuffer from the original buffer, and the changes to 11 | subbufer will affects the original buffer. 12 | 13 | and you can add ``lbuffer.h`` and recompile the other C module to get 14 | full lbuffer compatible with other lua module. just add: :: 15 | 16 | -DLB_REPLACE_LUA_API -include lbuffer.h 17 | 18 | when you compile other Lua C modules (using gcc). e.g. if you compile 19 | a lbuffer compatible md5_ module, you can use them together like 20 | this: :: 21 | 22 | require 'buffer' 23 | require 'md5' 24 | print(md5.sumhexa(buffer "hello world")) 25 | 26 | 27 | .. _lua: http://www.lua.org 28 | .. _md5: https://github.com/keplerproject/md5 29 | 30 | 31 | install 32 | ======= 33 | 34 | compile it just like other lua module. in ``*nix``, just: :: 35 | 36 | gcc -shared -obuffer.so *.c -llua51 37 | 38 | and in Windows using MinGW, just: :: 39 | 40 | gcc -mdll -DLUA_BUILD_AS_DLL -I/path/to/lua/include *.c /path/to/lua51.dll -o buffer.dll 41 | 42 | and in Windows using ``MSVC``, just create a Win32/DLL Project, and 43 | add all .c and .h files to project, set output name to ``buffer.dll`` 44 | and compile it. 45 | 46 | if you want subbuffer feature, you need define a macro named 47 | ``LB_SUBBUFFER``, because subbuffer will slow the memory realloc 48 | function in **all** buffers. 49 | 50 | there are two method to do ``pack``/``unpack`` operations. defaultly 51 | we read soem bits to a buffer, and cast it to int, and do bit swap. 52 | but you can also choose bit-op ways to extract binary numbers in file, 53 | this can be used in machines that has any number of bit in a byte, but 54 | this may somehow slow a bit. define ``LB_ARTHBIT`` to enable this. 55 | 56 | example 57 | ======= 58 | 59 | there are some examples, and main usage please see test.lua. 60 | 61 | first, you can use lbuffer just like using a normal string, you can 62 | call string functions on it, but the functions are in lbuffer module of 63 | course: :: 64 | 65 | local B = require 'buffer' 66 | local b = B'hello' 67 | print(#b) 68 | print(b:upper()) 69 | print(b:reverse()) 70 | 71 | this will output: :: 72 | 73 | 5 74 | HELLO 75 | OLLEH 76 | 77 | notice that all these functions don't create a new buffer/string, 78 | instead, it changes the content of operated buffer. so the output of 79 | the last line is ``OLLEH``, but not ``olleh``. 80 | 81 | beside the normal functions inherit from the standard string_ module, 82 | there are also many functions that only in lbuffer module, see 83 | reference_ of buffer to know these special functions, there are some 84 | examples: :: 85 | 86 | $ lua -lbuffer 87 | Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio 88 | > b = buffer "abc" 89 | > =b:set(2, "pple") 90 | apple 91 | > =b:append "pie" 92 | applepie 93 | > =b:insert(-3, '-') 94 | apple-pie 95 | > =b:insert(1, '('):append ')' 96 | (apple-pie) 97 | > =b:assign "abc" 98 | abc 99 | > =b:tohex " " 100 | 61 62 63 101 | > ='{ 0x'..b:tohex ", 0x"..' }' 102 | { 0x61, 0x62, 0x63 } 103 | > =b:alloc(5, "a") 104 | aaaaa 5 105 | > =b:len() 106 | 5 107 | > =b:len(3) 108 | 3 109 | > =b 110 | aaa 111 | > =b:free(), #b 112 | nil 0 113 | > =b:assign "abc" :eq "def" 114 | false 115 | > =b :cmp "def" 116 | -1 117 | > =b:move(3, 2) 118 | abbc 119 | > =b:clear() 120 | 121 | > =b:quote() 122 | "\000\000\000\000" 123 | > 124 | 125 | .. _string: http://www.lua.org/manual/5.1/manual.html#5.4 126 | 127 | 128 | if you define ``LB_SUBBUFFER`` flags when compile lbuffer, the 129 | subbuffer feature would enable. then you can use ``buffer.sub`` to get 130 | a reference to original buffer, if you modify the reference buffer, 131 | the original buffer will be modified as well. you can only have 132 | ``buffer._SUBS_MAX`` subbuffer at the moment, this value can be 133 | modified by define a macro named ``LB_SUBS_MAX``, the default value is 134 | 4. 135 | 136 | this is a example using subbuffer feature: :: 137 | 138 | $ lua -lbuffer 139 | Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio 140 | > b = buffer "apply" 141 | > sb = b:sub(5,5) 142 | > =sb:assign "epie" 143 | epie 144 | > sb2 = sb:sub(2,1) 145 | > =sb2:assign "-" 146 | - 147 | > =b 148 | apple-pie 149 | > 150 | 151 | and, beside all, buffer module has a pair of full featured pack/unpack 152 | functions. it can be used extract struct from binary text to lua, this 153 | a example to read ``*.mo`` file created from ``*.po`` file, using 154 | msgfmt: :: 155 | 156 | -- read *.mo file 157 | function read_mofile(b) 158 | local info = b:unpack [[ { 159 | magic = i, 160 | revision = i, 161 | nstrings = i, 162 | orig_tab_offset = i, 163 | trans_tab_offset = i, 164 | hash_tab_size = i, 165 | hash_tab_offset = i, 166 | } ]] 167 | 168 | local trans = {} 169 | for i = 0, info.nstrings-1 do 170 | local o_len, o_offset = b:unpack(info.orig_tab_offset+8*i+1, " =buffer(10, "a") 257 | aaaaaaaaaa 258 | > =buffer(3, "apple") 259 | app 260 | > =buffer(10, "apple") 261 | appleapple 262 | > =buffer(10):quote() 263 | "\000\000\000\000\000\000\000\000\000\000" 264 | > 265 | 266 | ``b`` can be buffer or string. ``i`` and ``j`` locale the useful piece 267 | in ``b`` :: 268 | 269 | > =buffer("apple", 2, -2) 270 | ppl 271 | 272 | the second form is :: 273 | 274 | modify([pos, ]ud, len) 275 | 276 | it accept a userdata and a length, and reads ``len`` bytes from the 277 | address of ``ud``, **be careful** with this form, it may very 278 | dangerous!! 279 | 280 | binary pack functions 281 | --------------------- 282 | 283 | * pack 284 | * unpack 285 | * getint 286 | * getuint 287 | * setint 288 | * setuint 289 | 290 | subbuffer functions 291 | ------------------- 292 | 293 | * sub 294 | * subcount 295 | * offset 296 | 297 | misc functions 298 | -------------- 299 | 300 | * alloc 301 | * clear 302 | * cmp 303 | * copy 304 | * eq 305 | * free 306 | * ipairs 307 | * isbuffer 308 | * move 309 | * quote 310 | * remove 311 | * swap 312 | * tohex 313 | * topointer 314 | * tostring 315 | 316 | C module developer note 317 | ======================= 318 | -------------------------------------------------------------------------------- /lbuffer-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | -- vim: ft=lua 2 | package = "lbuffer" 3 | version = "scm-0" 4 | 5 | source = { 6 | url = "git://github.com/starwing/lbuffer.git", 7 | } 8 | 9 | description = { 10 | summary = "mutable string support to lua", 11 | detailed = [[ 12 | lbuffer is a C module for lua, it provides mutable string feature to 13 | the lua_ language. it has all routines from lua's string module, add 14 | several modify functions. it provides: 15 | 16 | * change the value of buffer without copy it. 17 | * a pair of full feature pack/unpack functions. 18 | * get a subbuffer from the original buffer, and the changes to 19 | subbufer will affects the original buffer. 20 | 21 | ]], 22 | homepage = "https://github.com/starwing/lbuffer", 23 | license = "MIT/X11", 24 | } 25 | 26 | dependencies = { 27 | "lua >= 5.1", 28 | } 29 | 30 | build = { 31 | copy_directories = {}, 32 | 33 | type = "builtin", 34 | 35 | modules = { 36 | buffer = { 37 | "lbuffer.c", 38 | "lbufflib.c", 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lbuffer.c: -------------------------------------------------------------------------------- 1 | #define LUA_LIB 2 | #include "lbuffer.h" 3 | 4 | 5 | #include 6 | 7 | 8 | #define MAX_SIZE_T ((size_t)(~(size_t)0) - 2) 9 | 10 | 11 | #if LUA_VERSION_NUM < 502 12 | void lua_rawgetp(lua_State *L, int narg, const void *p) { 13 | lua_pushlightuserdata(L, (void*)p); 14 | lua_rawget(L, narg < 0 ? narg - 1 : narg); 15 | } 16 | 17 | void lua_rawsetp(lua_State *L, int narg, const void *p) { 18 | lua_pushlightuserdata(L, (void*)p); 19 | lua_insert(L, -2); 20 | lua_rawset(L, narg < 0 ? narg - 1 : narg); 21 | } 22 | 23 | int lua_absindex(lua_State *L, int idx) { 24 | return (idx > 0 || idx <= LUA_REGISTRYINDEX) 25 | ? idx 26 | : lua_gettop(L) + idx + 1; 27 | } 28 | 29 | const char *luaL_tolstring(lua_State *L, int idx, size_t *plen) { 30 | if (!luaL_callmeta(L, idx, "__tostring")) { /* no metafield? */ 31 | switch (lua_type(L, idx)) { 32 | case LUA_TNUMBER: 33 | case LUA_TSTRING: 34 | lua_pushvalue(L, idx); 35 | break; 36 | case LUA_TBOOLEAN: 37 | lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false")); 38 | break; 39 | case LUA_TNIL: 40 | lua_pushliteral(L, "nil"); 41 | break; 42 | default: 43 | lua_pushfstring(L, "%s: %p", luaL_typename(L, idx), 44 | lua_topointer(L, idx)); 45 | break; 46 | } 47 | } 48 | return lua_tolstring(L, -1, plen); 49 | } 50 | #endif 51 | 52 | #ifdef LB_REPLACE_LUA_API 53 | # undef lua_isstring 54 | # undef lua_tolstring 55 | # undef luaL_checklstring 56 | # undef luaL_optlstring 57 | #endif 58 | 59 | static int type_error(lua_State *L, int narg, const char *tname) { 60 | const char *msg = lua_pushfstring(L, "%s expected, got %s", 61 | tname, luaL_typename(L, narg)); 62 | return luaL_argerror(L, narg, msg); 63 | } 64 | 65 | 66 | /* luaL_Buffer compatible interface */ 67 | 68 | LB_API void lb_buffinit(lua_State *L, lb_Buffer *B) { 69 | B->L = L; 70 | B->b = B->initb; 71 | B->n = 0; 72 | B->size = LUAL_BUFFERSIZE; 73 | } 74 | 75 | LB_API char *lb_prepbuffsize(lb_Buffer *B, size_t sz) { 76 | lua_State *L = B->L; 77 | if (B->size - B->n < sz) { /* not enough space? */ 78 | char *newbuff; 79 | size_t newsize = B->size * 2; /* double buffer size */ 80 | if (newsize - B->n < sz) /* not big enough? */ 81 | newsize = B->n + sz; 82 | if (newsize < B->n || newsize - B->n < sz) 83 | luaL_error(L, "buffer too large"); 84 | /* create larger buffer */ 85 | newbuff = (char*)lua_newuserdata(L, newsize * sizeof(char)); 86 | /* move content to new buffer */ 87 | memcpy(newbuff, B->b, B->n * sizeof(char)); 88 | /* remove old buffer and archor new buffer */ 89 | lua_pushvalue(L, -1); 90 | lua_rawsetp(L, LUA_REGISTRYINDEX, B); 91 | B->b = newbuff; 92 | B->size = newsize; 93 | } 94 | return &B->b[B->n]; 95 | } 96 | 97 | LB_API void lb_addlstring(lb_Buffer *B, const char *s, size_t l) { 98 | char *b = lb_prepbuffsize(B, l); 99 | memcpy(b, s, l * sizeof(char)); 100 | lb_addsize(B, l); 101 | } 102 | 103 | LB_API void lb_addstring(lb_Buffer *B, const char *s) { 104 | lb_addlstring(B, s, strlen(s)); 105 | } 106 | 107 | LB_API void lb_addpadding(lb_Buffer *B, int ch, size_t l) { 108 | char *b = lb_prepbuffsize(B, l); 109 | memset(b, ch, l * sizeof(char)); 110 | lb_addsize(B, l); 111 | } 112 | 113 | LB_API void lb_addvalue(lb_Buffer *B) { 114 | lua_State *L = B->L; 115 | size_t l; 116 | const char *s = luaL_tolstring(L, -1, &l); 117 | lb_addlstring(B, s, l); 118 | lua_pop(L, 1); 119 | } 120 | 121 | LB_API void lb_pushresult(lb_Buffer *B) { 122 | lua_pushlstring(B->L, B->b, B->n); 123 | lb_resetbuffer(B); 124 | } 125 | 126 | 127 | /* buffer type routines */ 128 | 129 | static void get_metatable_fast(lua_State *L) { 130 | lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)LB_METAKEY); 131 | } 132 | 133 | LB_API lb_Buffer *lb_newbuffer(lua_State *L) { 134 | lb_Buffer *B = (lb_Buffer*)lua_newuserdata(L, sizeof(lb_Buffer)); 135 | lb_buffinit(L, B); 136 | get_metatable_fast(L); 137 | lua_setmetatable(L, -2); 138 | return B; 139 | } 140 | 141 | LB_API lb_Buffer *lb_copybuffer(lb_Buffer *B) { 142 | lb_Buffer *nb = lb_newbuffer(B->L); 143 | lb_addlstring(nb, B->b, B->n); 144 | return nb; 145 | } 146 | 147 | LB_API void lb_resetbuffer(lb_Buffer *B) { 148 | lua_State *L = B->L; 149 | if (B->b != B->initb) { /* remove old buffer */ 150 | lua_pushnil(L); 151 | lua_rawsetp(L, LUA_REGISTRYINDEX, B); 152 | } 153 | lb_buffinit(L, B); 154 | } 155 | 156 | LB_API lb_Buffer *lb_testbuffer(lua_State *L, int narg) { 157 | void *p = lua_touserdata(L, narg); 158 | if (p != NULL && /* value is a userdata? */ 159 | lua_getmetatable(L, narg)) { /* does it have a metatable? */ 160 | /* get correct metatable */ 161 | get_metatable_fast(L); 162 | if (!lua_rawequal(L, -1, -2)) /* not the same? */ 163 | p = NULL; /* value is a userdata with wrong metatable */ 164 | lua_pop(L, 2); /* remove both metatables */ 165 | return (lb_Buffer*)p; 166 | } 167 | return NULL; /* value is not a userdata with a metatable */ 168 | } 169 | 170 | LB_API lb_Buffer *lb_checkbuffer(lua_State *L, int narg) { 171 | lb_Buffer *b = lb_testbuffer(L, narg); 172 | if (b == NULL) 173 | type_error(L, narg, LB_LIBNAME); 174 | return b; 175 | } 176 | 177 | LB_API lb_Buffer *lb_pushbuffer(lua_State *L, const char *str, size_t len) { 178 | lb_Buffer *B = lb_newbuffer(L); 179 | lb_addlstring(B, str, len); 180 | return B; 181 | } 182 | 183 | 184 | /* compatible with lua api */ 185 | 186 | LB_API int lb_isbufferorstring(lua_State *L, int narg) { 187 | return lua_isstring(L, narg) || lb_testbuffer(L, narg) != NULL; 188 | } 189 | 190 | LB_API const char *lb_tolstring(lua_State *L, int narg, size_t *plen) { 191 | lb_Buffer *B; 192 | const char *str = lua_tolstring(L, narg, plen); 193 | if (str == NULL && (B = lb_testbuffer(L, narg)) != NULL) { 194 | if (plen != NULL) *plen = B->n; 195 | str = B->b; 196 | } 197 | return str; 198 | } 199 | 200 | LB_API const char *lb_checklstring(lua_State *L, int narg, size_t *plen) { 201 | const char *s = lb_tolstring(L, narg, plen); 202 | if (s == NULL) 203 | type_error(L, narg, "buffer/string"); 204 | return s; 205 | } 206 | 207 | LB_API const char *lb_optlstring(lua_State *L, int narg, const char *def, size_t *plen) { 208 | if (lua_isnoneornil(L, narg)) { 209 | if (plen != NULL) *plen = def ? strlen(def) : 0; 210 | return def; 211 | } 212 | return lb_checklstring(L, narg, plen); 213 | } 214 | 215 | 216 | /* bit pack/unpack operations */ 217 | 218 | #ifndef _MSC_VER 219 | # include 220 | #else 221 | # define uint32_t unsigned long 222 | # define uint64_t unsigned __int64 223 | # define int32_t signed long 224 | # define int64_t signed __int64 225 | #endif 226 | 227 | typedef union numcast_t { 228 | uint32_t i32; 229 | float f; 230 | uint64_t i64; 231 | double d; 232 | } numcast_t; 233 | 234 | static uint32_t read_int32(const char *s, int bigendian, int wide) { 235 | uint32_t n = 0; 236 | if (bigendian) { 237 | switch (wide) { 238 | default: return 0; 239 | case 4: n |= *s++ & 0xFF; 240 | case 3: n <<= 8; n |= *s++ & 0xFF; 241 | case 2: n <<= 8; n |= *s++ & 0xFF; 242 | case 1: n <<= 8; n |= *s++ & 0xFF; 243 | } 244 | } 245 | else { 246 | switch (wide) { 247 | default: return 0; 248 | case 4: n |= (*s++ & 0xFF) << 24; 249 | case 3: n |= (*s++ & 0xFF) << 16; 250 | case 2: n |= (*s++ & 0xFF) << 8; 251 | case 1: n |= (*s++ & 0xFF); 252 | } 253 | } 254 | return n; 255 | } 256 | 257 | static void write_int32(char *s, int bigendian, uint32_t n, int wide) { 258 | if (bigendian) { 259 | switch (wide) { 260 | default: return; 261 | case 4: *s++ = (n >> 24) & 0xFF; 262 | case 3: *s++ = (n >> 16) & 0xFF; 263 | case 2: *s++ = (n >> 8) & 0xFF; 264 | case 1: *s++ = (n ) & 0xFF; 265 | } 266 | } 267 | else { 268 | switch (wide) { 269 | default: return; 270 | case 4: *s++ = n & 0xFF; n >>= 8; 271 | case 3: *s++ = n & 0xFF; n >>= 8; 272 | case 2: *s++ = n & 0xFF; n >>= 8; 273 | case 1: *s++ = n & 0xFF; 274 | } 275 | } 276 | } 277 | 278 | static void read_binary(const char *str, int bigendian, numcast_t *buf, size_t wide) { 279 | if (wide <= 4) 280 | buf->i32 = read_int32(str, bigendian, wide); 281 | else if (bigendian) { 282 | buf->i64 = read_int32(str, bigendian, 4); buf->i64 <<= ((wide-4)<<3); 283 | buf->i64 |= read_int32(&str[4], bigendian, wide - 4); 284 | } 285 | else { 286 | buf->i64 = read_int32(str, bigendian, 4); 287 | buf->i64 |= (uint64_t)read_int32(str, bigendian, 4) << 32; 288 | } 289 | } 290 | 291 | static void write_binary(char *str, int bigendian, numcast_t *buf, size_t wide) { 292 | if (wide <= 4) 293 | write_int32(str, bigendian, buf->i32, wide); 294 | else if (bigendian) { 295 | write_int32(str, bigendian, (uint32_t)(buf->i64 >> 32), wide - 4); 296 | write_int32(&str[wide - 4], bigendian, (uint32_t)buf->i64, 4); 297 | } 298 | else { 299 | write_int32(str, bigendian, (uint32_t)buf->i64, 4); 300 | write_int32(&str[4], bigendian, (uint32_t)(buf->i64 >> 32), wide - 4); 301 | } 302 | } 303 | 304 | static void expand_sign(numcast_t *buf, size_t wide) { 305 | int shift = wide<<3; 306 | if (wide <= 4) { 307 | if (wide != 4 && ((uint32_t)1 << (shift - 1) & buf->i32) != 0) 308 | buf->i32 |= ~(uint32_t)0 << shift; 309 | } 310 | else { 311 | if (wide != 8 && ((uint64_t)1 << (shift - 1) & buf->i64) != 0) 312 | buf->i64 |= ~(uint64_t)0 << shift; 313 | } 314 | } 315 | 316 | LB_API int lb_packint(lb_Buffer *B, size_t wide, int bigendian, lua_Integer i) { 317 | numcast_t buff; 318 | /* we use int64_t with i64, because if we use uint64_t, the 319 | * high 32 bit of 64bit integer will be stripped, don't know 320 | * why it happened. */ 321 | if (wide <= 4) 322 | buff.i32 = (/*u*/int32_t)i; 323 | else 324 | buff.i64 = (/*u*/int64_t)i; 325 | write_binary(lb_prepbuffsize(B, wide), 326 | bigendian, &buff, wide); 327 | lb_addsize(B, wide); 328 | return wide; 329 | } 330 | 331 | LB_API int lb_packfloat(lb_Buffer *B, size_t wide, int bigendian, lua_Number n) { 332 | numcast_t buff; 333 | if (wide <= 4) 334 | buff.f = (float)n; 335 | else 336 | buff.d = (double)n; 337 | write_binary(lb_prepbuffsize(B, wide), 338 | bigendian, &buff, wide); 339 | lb_addsize(B, wide); 340 | return wide; 341 | } 342 | 343 | LB_API int lb_unpackint(const char *s, size_t wide, int bigendian, lua_Integer *pi) { 344 | numcast_t buff; 345 | read_binary(s, bigendian, &buff, wide); 346 | expand_sign(&buff, wide); 347 | if (wide <= 4) 348 | *pi = (lua_Integer)buff.i32; 349 | else 350 | *pi = (lua_Integer)buff.i64; 351 | return wide; 352 | } 353 | 354 | LB_API int lb_unpackuint(const char *s, size_t wide, int bigendian, lua_Integer *pi) { 355 | numcast_t buff; 356 | read_binary(s, bigendian, &buff, wide); 357 | if (wide <= 4) 358 | *pi = (lua_Integer)buff.i32; 359 | else 360 | *pi = (lua_Integer)buff.i64; 361 | return wide; 362 | } 363 | 364 | LB_API int lb_unpackfloat(const char *s, size_t wide, int bigendian, lua_Number *pn) { 365 | numcast_t buff; 366 | read_binary(s, bigendian, &buff, wide); 367 | if (wide <= 4) 368 | *pn = (lua_Number)buff.f; 369 | else 370 | *pn = (lua_Number)buff.d; 371 | return wide; 372 | } 373 | 374 | /* 375 | * cc: lua='lua53' flags+='-s -O2 -Wall -std=c99 -pedantic -mdll -Id:/$lua/include' libs+='d:/$lua/$lua.dll' 376 | * cc: flags+='-DLB_REDIR_STRLIB=1 -DLB_FILEHANDLE' 377 | * cc: flags+='-DLUA_BUILD_AS_DLL' input='lb*.c' output='buffer.dll' 378 | * cc: run='$lua test.lua' 379 | */ 380 | -------------------------------------------------------------------------------- /lbuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef LBUFFER_H 2 | #define LBUFFER_H 3 | 4 | 5 | #define LB_LIBNAME "buffer" 6 | #define LB_VERSION "0.2" 7 | 8 | 9 | #include 10 | #include 11 | 12 | 13 | #if defined( __sparc__ ) || defined( __ppc__ ) 14 | # define LB_BIGENDIAN 1 15 | #else 16 | # define LB_BIGENDIAN 0 17 | #endif 18 | 19 | #ifndef LB_API 20 | # define LB_API LUA_API 21 | #endif 22 | 23 | /* compatible apis */ 24 | #if LUA_VERSION_NUM < 502 25 | # define LUA_OK 0 26 | # define lua_getuservalue lua_getfenv 27 | # define lua_setuservalue lua_setfenv 28 | # define lua_rawlen lua_objlen 29 | # define luaL_setfuncs(L,l,nups) luaI_openlib((L),NULL,(l),(nups)) 30 | # define luaL_newlibtable(L,l) \ 31 | lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) 32 | # define luaL_newlib(L,l) \ 33 | (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) 34 | 35 | LUA_API void lua_rawgetp (lua_State *L, int idx, const void *p); 36 | LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p); 37 | LUA_API int lua_absindex (lua_State *L, int idx); 38 | 39 | LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *plen); 40 | #endif /* LUA_VERSION_NUM < 502 */ 41 | 42 | 43 | /* luaL_Buffer compatible interface */ 44 | 45 | typedef struct lb_Buffer { 46 | char *b; 47 | size_t size; 48 | size_t n; 49 | lua_State *L; 50 | char initb[LUAL_BUFFERSIZE]; 51 | } lb_Buffer; 52 | 53 | #define lb_buffinitsize(L,B,sz) (lb_buffinit((L),(B)),lb_prepbuffsize((B),(sz))) 54 | #define lb_addsize(B,s) ((B)->n += (s)) 55 | #define lb_prepbuffer(B) lb_prepbuffsize((B), LUAL_BUFFERSIZE) 56 | #define lb_pushresultsize(B,sz) (lb_addsize(B,sz),lb_pushresult(b)) 57 | 58 | #define lb_addchar(B,c) \ 59 | ((void)((B)->n < (B)->size || lb_prepbuffsize((B), 1)), \ 60 | ((B)->b[(B)->n++] = (c))) 61 | 62 | #define lb_atpos(B, pos, codes) \ 63 | do { size_t Bn_ = (B)->n; \ 64 | (B)->n = (pos); codes; \ 65 | if ((B)->n < Bn_) (B)->n = Bn_; } while (0) 66 | 67 | 68 | LB_API void lb_buffinit (lua_State *L, lb_Buffer *B); 69 | LB_API char *lb_prepbuffsize (lb_Buffer *B, size_t sz); 70 | LB_API void lb_addlstring (lb_Buffer *B, const char *s, size_t l); 71 | LB_API void lb_addstring (lb_Buffer *B, const char *s); 72 | LB_API void lb_addpadding (lb_Buffer *B, int ch, size_t l); 73 | LB_API void lb_addvalue (lb_Buffer *B); 74 | LB_API void lb_pushresult (lb_Buffer *B); 75 | 76 | 77 | /* buffer type routines */ 78 | 79 | #define LB_METAKEY 0xF7B2FFE7 80 | 81 | LUALIB_API int luaopen_buffer (lua_State *L); 82 | 83 | LB_API lb_Buffer *lb_newbuffer (lua_State *L); 84 | LB_API lb_Buffer *lb_copybuffer (lb_Buffer *B); 85 | LB_API void lb_resetbuffer(lb_Buffer *B); 86 | 87 | LB_API lb_Buffer *lb_testbuffer (lua_State *L, int idx); 88 | LB_API lb_Buffer *lb_checkbuffer (lua_State *L, int idx); 89 | LB_API lb_Buffer *lb_pushbuffer (lua_State *L, const char *str, size_t len); 90 | 91 | LB_API int lb_isbufferorstring (lua_State *L, int idx); 92 | LB_API const char *lb_tolstring (lua_State *L, int idx, size_t *plen); 93 | LB_API const char *lb_checklstring (lua_State *L, int idx, size_t *plen); 94 | LB_API const char *lb_optlstring (lua_State *L, int idx, const char *def, size_t *plen); 95 | 96 | 97 | /* pack/unpack operations */ 98 | 99 | LB_API int lb_packint (lb_Buffer *B, size_t wide, int bigendian, lua_Integer i); 100 | LB_API int lb_unpackint (const char *s, size_t wide, int bigendian, lua_Integer *pi); 101 | LB_API int lb_unpackuint (const char *s, size_t wide, int bigendian, lua_Integer *pi); 102 | 103 | LB_API int lb_packfloat (lb_Buffer *B, size_t wide, int bigendian, lua_Number n); 104 | LB_API int lb_unpackfloat (const char *s, size_t wide, int bigendian, lua_Number *pn); 105 | 106 | LB_API int lb_pack (lb_Buffer *B, const char *fmt, int args); 107 | LB_API int lb_unpack (lua_State *L, const char *s, size_t n, const char *fmt); 108 | 109 | 110 | /* lua compatible APIs */ 111 | 112 | #ifdef LB_REPLACE_LUA_API 113 | # define lua_isstring lb_isbufferorstring 114 | # define lua_tolstring lb_tolstring 115 | # define luaL_checklstring lb_checklstring 116 | # define luaL_optlstring lb_optlstring 117 | #endif /* LB_REPLACE_LUA_API */ 118 | 119 | 120 | #endif /* LBUFFER_H */ 121 | -------------------------------------------------------------------------------- /lbufflib.c: -------------------------------------------------------------------------------- 1 | #define LUA_LIB 2 | #include "lbuffer.h" 3 | #include "lualib.h" /* for LUA_FILEHANDLE */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #ifdef LB_REPLACE_LUA_API 12 | # undef lua_isstring 13 | # undef lua_tolstring 14 | # undef luaL_checklstring 15 | # undef luaL_optlstring 16 | #endif 17 | 18 | #define return_self(L) do { lua_settop(L, 1); return 1; } while (0) 19 | #define uchar(ch) ((unsigned char)(ch)) 20 | 21 | 22 | static size_t posrelat(lua_Integer offset, size_t len) { 23 | if (offset >= 1 && (size_t)offset <= len) 24 | return offset - 1; 25 | else if (offset <= -1 && (size_t)-offset <= len) 26 | return offset + len; 27 | return offset > 0 ? len : 0; 28 | } 29 | 30 | static size_t rangerelat(lua_State *L, int idx, size_t *plen) { 31 | size_t i = posrelat(luaL_optinteger(L, idx, 1), *plen); 32 | lua_Integer sj = luaL_optinteger(L, idx + 1, -1); 33 | size_t j = posrelat(sj, *plen); 34 | *plen = i <= j ? j - i + (sj != 0 && j != *plen) : 0; 35 | return i; 36 | } 37 | 38 | static int type_error(lua_State *L, int narg, const char *tname) { 39 | const char *msg = lua_pushfstring(L, "%s expected, got %s", 40 | tname, luaL_typename(L, narg)); 41 | return luaL_argerror(L, narg, msg); 42 | } 43 | 44 | 45 | /* buffer information */ 46 | 47 | static int Lisbuffer(lua_State *L) { 48 | return lb_testbuffer(L, 1) != NULL; 49 | } 50 | 51 | static int L__tostring(lua_State *L) { 52 | lb_Buffer *B; 53 | if ((B = lb_testbuffer(L, 1)) == NULL) 54 | luaL_tolstring(L, 1, NULL); 55 | else 56 | lua_pushlstring(L, B->b, B->n); 57 | return 1; 58 | } 59 | 60 | static int Ltohex(lua_State *L) { 61 | lb_Buffer B; 62 | size_t i, len, seplen, gseplen = 0; 63 | const char *str = lb_tolstring(L, 1, &len); 64 | const char *sep, *gsep = NULL; 65 | int upper, group = -1, col = 0; 66 | int has_group = lua_type(L, 2) == LUA_TNUMBER, arg = 2; 67 | if (has_group) group = lua_tointeger(L, arg++); 68 | if (group == 0) group = -1; 69 | sep = lb_optlstring(L, arg++, "", &seplen); 70 | if (has_group) gsep = lb_optlstring(L, arg++, "\n", &gseplen); 71 | upper = lua_toboolean(L, arg++); 72 | lb_buffinit(L, &B); 73 | for (i = 0; i < len; ++i, ++col) { 74 | char *hexa = upper ? "0123456789ABCDEF" : "0123456789abcdef"; 75 | if (col == group) 76 | col = 0, lb_addlstring(&B, gsep, gseplen); 77 | else if (i != 0) 78 | lb_addlstring(&B, sep, seplen); 79 | lb_addchar(&B, hexa[uchar(str[i]) >> 4]); 80 | lb_addchar(&B, hexa[uchar(str[i]) & 0xF]); 81 | } 82 | lb_pushresult(&B); 83 | return 1; 84 | } 85 | 86 | static int Lquote(lua_State *L) { 87 | size_t i, len; 88 | const char *str = lb_tolstring(L, 1, &len); 89 | lb_Buffer B; 90 | lb_buffinit(L, &B); 91 | lb_addchar(&B, '"'); 92 | for (i = 0; i < len; ++i) { 93 | if (str[i] != '"' && str[i] != '\\' && isprint(str[i])) 94 | lb_addchar(&B, uchar(str[i])); 95 | else { 96 | char *numa = "0123456789"; 97 | switch (uchar(str[i])) { 98 | case '\a': lb_addstring(&B, "\\a"); break; 99 | case '\b': lb_addstring(&B, "\\b"); break; 100 | case '\f': lb_addstring(&B, "\\f"); break; 101 | case '\n': lb_addstring(&B, "\\n"); break; 102 | case '\r': lb_addstring(&B, "\\r"); break; 103 | case '\t': lb_addstring(&B, "\\t"); break; 104 | case '\v': lb_addstring(&B, "\\v"); break; 105 | case '\\': lb_addstring(&B, "\\\\"); break; 106 | default: 107 | lb_addchar(&B, '\\'); 108 | lb_addchar(&B, numa[uchar(str[i])/100%10]); 109 | lb_addchar(&B, numa[uchar(str[i])/10%10]); 110 | lb_addchar(&B, numa[uchar(str[i])%10]); 111 | break; 112 | } 113 | } 114 | } 115 | lb_addchar(&B, '"'); 116 | lb_pushresult(&B); 117 | return 1; 118 | } 119 | 120 | static int Ltopointer(lua_State *L) { 121 | lb_Buffer *B = lb_checkbuffer(L, 1); 122 | size_t offset = posrelat(luaL_optinteger(L, 2, 1), B->n); 123 | lua_pushlightuserdata(L, offset == B->n ? NULL : &B->b[offset]); 124 | return 1; 125 | } 126 | 127 | static int Lcmp(lua_State *L) { 128 | size_t l1, l2; 129 | const char *s1 = lb_checklstring(L, 1, &l1); 130 | const char *s2 = lb_checklstring(L, 2, &l2); 131 | int res; 132 | if ((res = memcmp(s1, s2, l1 < l2 ? l1 : l2)) == 0) 133 | res = l1 - l2; 134 | lua_pushinteger(L, res > 0 ? 1 : res < 0 ? -1 : 0); 135 | return 1; 136 | } 137 | 138 | static int Leq(lua_State *L) { 139 | /* We can do this slightly faster than lb_cmp() by comparing 140 | * string length first. */ 141 | size_t l1, l2; 142 | const char *s1 = lb_checklstring(L, 1, &l1); 143 | const char *s2 = lb_checklstring(L, 2, &l2); 144 | lua_pushboolean(L, l1 == l2 && memcmp(s1, s2, l1) == 0); 145 | return 1; 146 | } 147 | 148 | static int auxipairs(lua_State *L) { 149 | lb_Buffer *B = lb_checkbuffer(L, 1); 150 | int key = (int)luaL_checkinteger(L, 2) + 1; 151 | if (key <= 0 || (size_t)key > B->n) return 0; 152 | lua_pushinteger(L, key); 153 | lua_pushinteger(L, uchar(B->b[key - 1])); 154 | return 2; 155 | } 156 | 157 | static int Lipairs(lua_State *L) { 158 | lb_Buffer *B = lb_checkbuffer(L, 1); 159 | int pos = posrelat(luaL_optinteger(L, 2, 1), B->n); 160 | lua_pushcfunction(L, auxipairs); 161 | lua_insert(L, 1); 162 | lua_pushinteger(L, pos); 163 | return 3; 164 | } 165 | 166 | static int Lsetlen(lua_State *L) { 167 | lb_Buffer *B = lb_checkbuffer(L, 1); 168 | int newlen = lua_tointeger(L, 2); 169 | if (newlen < 0) newlen += B->n; 170 | if (newlen < 0) newlen = 0; 171 | if ((size_t)newlen <= B->n) 172 | B->n = (size_t)newlen; 173 | else 174 | lb_addpadding(B, 0, (size_t)newlen - B->n); 175 | return_self(L); 176 | } 177 | 178 | static int Llen(lua_State *L) { 179 | size_t len; 180 | lb_checklstring(L, 1, &len); 181 | lua_pushinteger(L, len); 182 | return 1; 183 | } 184 | 185 | static int Lbyte(lua_State *L) { 186 | lb_Buffer *B = lb_checkbuffer(L, 1); 187 | size_t i, len = B->n, pos = rangerelat(L, 2, &len); 188 | luaL_checkstack(L, len, "string slice too long"); 189 | for (i = 0; i < len; ++i) 190 | lua_pushinteger(L, uchar(B->b[pos+i])); 191 | return len; 192 | } 193 | 194 | static int Lchar(lua_State *L) { 195 | lb_Buffer *B = NULL; 196 | size_t i, n = lua_gettop(L); 197 | int invalid = 0; 198 | char *p; 199 | if ((B = lb_testbuffer(L, 1)) == NULL) { 200 | B = lb_newbuffer(L); 201 | lua_insert(L, 1); 202 | invalid = -1; 203 | n += 1; 204 | } 205 | p = lb_prepbuffsize(B, n); 206 | for (i = 2; i <= n; ++i) { 207 | int c = (int)luaL_checkinteger(L, i); 208 | if (uchar(c) != c && invalid > 0) 209 | invalid += i; 210 | p[i - 2] = uchar(c); 211 | } 212 | lb_addsize(B, n - 1); 213 | lua_settop(L, 1); 214 | if (invalid > 0) { 215 | lua_pushinteger(L, invalid); 216 | return 2; 217 | } 218 | return 1; 219 | } 220 | 221 | 222 | /* buffer routines */ 223 | 224 | static void* testudata(lua_State *L, int narg, const char *tname) { 225 | void *p = lua_touserdata(L, narg); 226 | if (p != NULL) { /* value is a userdata? */ 227 | if (lua_getmetatable(L, narg)) { /* does it have a metatable? */ 228 | lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */ 229 | if (!lua_rawequal(L, -1, -2)) /* not the same? */ 230 | p = NULL; /* value is a userdata with wrong metatable */ 231 | lua_pop(L, 2); /* remove both metatables */ 232 | return p; 233 | } 234 | } 235 | return NULL; /* value is not a userdata with a metatable */ 236 | } 237 | 238 | static const char *readfile(lua_State *L, int narg, size_t *plen) { 239 | /* narg must absolute index */ 240 | if (testudata(L, narg, LUA_FILEHANDLE) != NULL) { 241 | int top = lua_gettop(L); 242 | lua_getfield(L, narg, "read"); 243 | lua_insert(L, narg); 244 | if (top == narg) { 245 | lua_pushliteral(L, "*a"); 246 | top += 1; 247 | } 248 | lua_call(L, top - narg + 1, 1); 249 | return lua_tolstring(L, narg, plen); 250 | } 251 | return NULL; 252 | } 253 | 254 | static const char *udtolstring(lua_State *L, int idx, size_t *plen) { 255 | void *u = NULL; 256 | if ((u = (void*)readfile(L, idx, plen)) != NULL) 257 | return (const char*)u; 258 | if ((u = lua_touserdata(L, idx)) != NULL) { 259 | int len; 260 | if (!lua_isnumber(L, idx + 1)) { 261 | type_error(L, idx + 1, "number"); 262 | return NULL; /* avoid warning */ 263 | } 264 | len = (int)lua_tointeger(L, idx + 1); 265 | if (plen != NULL) *plen = len >= 0 ? len : 0; 266 | #ifdef LB_COMPAT_TOLUA 267 | if (!lua_islightuserdata(L, idx)) /* compatble with tolua */ 268 | u = *(void**)u; 269 | #endif /* LB_COMPAT_TOLUA */ 270 | } 271 | if (u == NULL && plen != NULL) *plen = 0; 272 | return (const char*)u; 273 | } 274 | 275 | static const char *check_strarg(lua_State *L, int idx, 276 | size_t *plen, size_t *ppadlen) { 277 | lb_Buffer *B; 278 | switch (lua_type(L, idx)) { 279 | case LUA_TNONE: 280 | case LUA_TNIL: 281 | if (plen) *plen = 0; 282 | if (ppadlen) *ppadlen = 0; 283 | return NULL; 284 | 285 | case LUA_TNUMBER: 286 | if (plen) *plen = lua_tointeger(L, idx); 287 | if (lua_type(L, idx+1) == LUA_TNUMBER) 288 | type_error(L, idx+1, "nil/string/buffer/file/userdata"); 289 | return check_strarg(L, idx+1, ppadlen, NULL); 290 | 291 | case LUA_TSTRING: 292 | if (ppadlen) *ppadlen = 0; 293 | { 294 | size_t len; 295 | const char *s = lua_tolstring(L, idx, &len); 296 | size_t pos = rangerelat(L, idx + 1, &len); 297 | if (plen) *plen = len; 298 | return &s[pos]; 299 | } 300 | 301 | case LUA_TLIGHTUSERDATA: 302 | if (ppadlen) *ppadlen = 0; 303 | return udtolstring(L, idx, plen); 304 | 305 | case LUA_TUSERDATA: 306 | if (ppadlen) *ppadlen = 0; 307 | if ((B = lb_testbuffer(L, idx)) != NULL) { 308 | size_t len = B->n; 309 | const char *b = &B->b[rangerelat(L, idx + 1, &len)]; 310 | if (plen) *plen = len; 311 | return b; 312 | } 313 | return udtolstring(L, idx, plen); 314 | 315 | default: 316 | type_error(L, idx, "nil/number/string/buffer/file/userdata"); 317 | } 318 | return NULL; 319 | } 320 | 321 | static void apply_strarg(lb_Buffer *B, size_t pos, 322 | const char *s, size_t len, size_t padlen) { 323 | if (pos + len > B->size) 324 | lb_prepbuffsize(B, pos+len-B->n); 325 | if (len == 0) return; 326 | if (s != NULL && padlen == 0) 327 | memcpy(&B->b[pos], s, len); 328 | else if (s == NULL || padlen == 1) 329 | memset(&B->b[pos], s ? s[0] : 0, len); 330 | else { 331 | int i = 0, count = len / padlen; 332 | char *b = &B->b[pos]; 333 | if (s == b) i = 1, b += padlen; 334 | for (; i < count; ++i, b += padlen) 335 | memcpy(b, s, padlen); 336 | memcpy(b, s, len % padlen); 337 | } 338 | } 339 | 340 | static int Lnew(lua_State *L) { 341 | lb_Buffer *B; 342 | size_t padlen, len; 343 | const char *s = check_strarg(L, 1, &len, &padlen); 344 | B = lb_newbuffer(L); 345 | apply_strarg(B, 0, s, len, padlen); 346 | B->b[len] = '\0'; 347 | lb_addsize(B, len); 348 | return 1; 349 | } 350 | 351 | static int map_char(lua_State *L, int (*f)(int)) { 352 | lb_Buffer *B = lb_checkbuffer(L, 1); 353 | size_t first = posrelat(luaL_optinteger(L, 2, 1), B->n); 354 | size_t last = posrelat(luaL_optinteger(L, 3, -1), B->n); 355 | for (; first <= last; ++first) 356 | B->b[first] = uchar(f(B->b[first])); 357 | return_self(L); 358 | } 359 | 360 | static int Llower(lua_State *L) { return map_char(L, tolower); } 361 | static int Lupper(lua_State *L) { return map_char(L, toupper); } 362 | 363 | static int Linsert(lua_State *L) { 364 | lb_Buffer *B = lb_checkbuffer(L, 1); 365 | size_t len, padlen, pos = B->n; 366 | const char *s; 367 | if (lua_type(L, 2) != LUA_TNUMBER) { /* append */ 368 | s = check_strarg(L, 2, &len, &padlen); 369 | apply_strarg(B, pos, s, len, padlen); 370 | lb_addsize(B, len); 371 | } 372 | else { /* insert */ 373 | pos = posrelat(lua_tointeger(L, 2), B->n); 374 | s = check_strarg(L, 3, &len, &padlen); 375 | if (len != 0) { 376 | lb_prepbuffsize(B, len); 377 | memmove(&B->b[pos+len], &B->b[pos], B->n-pos); 378 | apply_strarg(B, pos, s, len, padlen); 379 | lb_addsize(B, len); 380 | } 381 | } 382 | return_self(L); 383 | } 384 | 385 | static int Lclear(lua_State *L) { 386 | lb_Buffer *B = lb_checkbuffer(L, 1); 387 | size_t padlen, len = B->n, pos = rangerelat(L, 2, &len); 388 | const char *s = luaL_optlstring(L, 4, NULL, &padlen); 389 | apply_strarg(B, pos, s, len, padlen); 390 | return_self(L); 391 | } 392 | 393 | static int Lset(lua_State *L) { 394 | lb_Buffer *B = lb_checkbuffer(L, 1); 395 | size_t len, padlen, pos; 396 | const char *s; 397 | if (lua_type(L, 2) != LUA_TNUMBER) { /* assign */ 398 | s = check_strarg(L, 2, &len, &padlen); 399 | apply_strarg(B, 0, s, len, padlen); 400 | B->n = len; 401 | } 402 | else { /* overwrite */ 403 | pos = posrelat(lua_tointeger(L, 2), B->n); 404 | s = check_strarg(L, 3, &len, &padlen); 405 | apply_strarg(B, pos, s, len, padlen); 406 | if (B->n < pos + len) 407 | B->n = pos + len; 408 | } 409 | return_self(L); 410 | } 411 | 412 | static int Lrep(lua_State *L) { 413 | lb_Buffer *B = lb_checkbuffer(L, 1); 414 | size_t len = B->n; 415 | const char *str = B->b; 416 | lua_Integer rep = 0; 417 | if (lua_type(L, 2) == LUA_TNUMBER) 418 | rep = lua_tointeger(L, 2); 419 | else if ((str = lb_tolstring(L, 2, &len)) != NULL) 420 | rep = luaL_checkinteger(L, 3); 421 | else type_error(L, 2, "number/buffer/string"); 422 | if (rep < 0) rep = 0; 423 | apply_strarg(B, 0, str, len*rep, len); 424 | B->n = len*rep; 425 | return_self(L); 426 | } 427 | 428 | static int Lcopy(lua_State *L) { 429 | lb_Buffer *B = lb_checkbuffer(L, 1); 430 | size_t len = B->n, pos = rangerelat(L, 2, &len); 431 | lb_pushbuffer(L, &B->b[pos], len); 432 | return 1; 433 | } 434 | 435 | static int Lmove(lua_State *L) { 436 | lb_Buffer *B = lb_checkbuffer(L, 1); 437 | lua_Integer dst = luaL_checkinteger(L, 2); 438 | size_t len = B->n, pos = rangerelat(L, 3, &len); 439 | 440 | if (dst > 0) dst -= 1; 441 | if (dst < 0) dst += B->n; 442 | if (dst < 0) dst = 0; 443 | 444 | if (dst+len > B->n) 445 | lb_prepbuffsize(B, dst + len - B->n); 446 | memmove(&B->b[dst], &B->b[pos], len); 447 | if ((size_t)dst > B->n) 448 | memset(&B->b[B->n], 0, dst - B->n); 449 | if (B->n < dst+len) 450 | B->n = dst+len; 451 | return_self(L); 452 | } 453 | 454 | static int Lremove(lua_State *L) { 455 | lb_Buffer *B = lb_checkbuffer(L, 1); 456 | size_t len = B->n, pos = rangerelat(L, 2, &len); 457 | size_t end = pos + len; 458 | if (len != 0) 459 | memmove(&B->b[pos], &B->b[end], B->n - end); 460 | B->n -= len; 461 | return_self(L); 462 | } 463 | 464 | static void my_strrev(char *p1, char *p2) { 465 | for (--p2; p1 < p2; ++p1, --p2) { 466 | char t = *p1; 467 | *p1 = *p2; 468 | *p2 = t; 469 | } 470 | } 471 | 472 | static int Lreverse(lua_State *L) { 473 | lb_Buffer *B = lb_checkbuffer(L, 1); 474 | size_t len = B->n, pos = rangerelat(L, 2, &len); 475 | my_strrev(&B->b[pos], &B->b[pos + len]); 476 | return_self(L); 477 | } 478 | 479 | static void exchange(char *p1, char *p2, char *p3) { 480 | my_strrev(p1, p2); 481 | my_strrev(p2, p3); 482 | my_strrev(p1, p3); 483 | } 484 | 485 | static void exchange_split(char *p1, char *p2, char *p3, char *p4) { 486 | my_strrev(p1, p2); 487 | my_strrev(p2, p3); 488 | my_strrev(p3, p4); 489 | my_strrev(p1, p4); 490 | } 491 | 492 | static int Lswap(lua_State *L) { 493 | size_t p1, l1, p2, l2; 494 | lb_Buffer *B = lb_checkbuffer(L, 1); 495 | if (lua_isnoneornil(L, 3)) { 496 | p2 = posrelat(luaL_checkinteger(L, 2), B->n); 497 | l2 = B->n - p2; 498 | p1 = 0, l1 = p2; 499 | } 500 | else { 501 | l1 = B->n, p1 = rangerelat(L, 2, &l1); 502 | l2 = B->n, p2 = rangerelat(L, 4, &l2); 503 | if (lua_isnoneornil(L, 5)) { 504 | size_t new_p2 = p1 + l1; 505 | l2 = p2 - new_p2 + 1; 506 | p2 = new_p2; 507 | } 508 | else if (p1 + l1 > p2) { 509 | size_t old_l1 = l1, old_p2 = p2; 510 | l1 = p2 - p1; 511 | p2 = p1 + old_l1; 512 | l2 -= p2 - old_p2; 513 | } 514 | } 515 | if (p1 + l1 == p2) 516 | exchange(&B->b[p1], &B->b[p2], &B->b[p2 + l2]); 517 | else 518 | exchange_split(&B->b[p1], &B->b[p1 + l1], 519 | &B->b[p2], &B->b[p2 + l2]); 520 | return_self(L); 521 | } 522 | 523 | 524 | /* bianry operations */ 525 | 526 | static size_t check_giargs(lua_State *L, int narg, size_t len, size_t *wide, int *bigendian) { 527 | size_t pos = posrelat(luaL_optinteger(L, narg, 1), len); 528 | *wide = (size_t)luaL_optinteger(L, narg + 1, 4); 529 | if (*wide < 1 || *wide > 8) 530 | luaL_argerror(L, narg + 1, "only 1 to 8 wide support"); 531 | switch (*luaL_optlstring(L, narg + 2, "native", NULL)) { 532 | case 'b': case 'B': case '>': *bigendian = 1; break; 533 | case 'l': case 'L': case '<': *bigendian = 0; break; 534 | case 'n': case 'N': case '=': *bigendian = LB_BIGENDIAN; break; 535 | default: luaL_argerror(L, 4, "only \"big\" or \"little\" or \"native\" endian support"); 536 | } 537 | return pos; 538 | } 539 | 540 | static int Lgetint(lua_State *L) { 541 | lua_Integer i; 542 | size_t len; 543 | const char *str = lb_checklstring(L, 1, &len); 544 | int bigendian; 545 | size_t wide, pos = check_giargs(L, 2, len, &wide, &bigendian); 546 | if (pos + wide > len) return 0; 547 | lb_unpackint(&str[pos], wide, bigendian, &i); 548 | lua_pushinteger(L, i); 549 | return 1; 550 | } 551 | 552 | static int Lgetuint(lua_State *L) { 553 | lua_Integer i; 554 | size_t len; 555 | const char *str = lb_checklstring(L, 1, &len); 556 | int bigendian; 557 | size_t wide, pos = check_giargs(L, 2, len, &wide, &bigendian); 558 | if (pos + wide > len) return 0; 559 | lb_unpackuint(&str[pos], wide, bigendian, &i); 560 | lua_pushinteger(L, i); 561 | return 1; 562 | } 563 | 564 | static int Lsetuint(lua_State *L) { 565 | lb_Buffer *B = lb_checkbuffer(L, 1); 566 | lua_Integer i = luaL_checkinteger(L, 2); 567 | int bigendian; 568 | size_t wide, pos = check_giargs(L, 3, B->n, &wide, &bigendian); 569 | lb_atpos(B, pos, lb_packint(B, wide, bigendian, i)); 570 | lua_settop(L, 1); 571 | return 1; 572 | } 573 | 574 | 575 | /* pack/unpack */ 576 | 577 | typedef struct parse_info { 578 | lb_Buffer *B; /* working lb_Buffer */ 579 | size_t pos; /* current working position in buffer */ 580 | int is_bigendian; 581 | int is_pack; 582 | int is_stringkey; 583 | unsigned int flags; /* see PIF_* flags below */ 584 | int narg, nret; /* numbers of arguments/return values */ 585 | int level, index; /* the level/index of nest table */ 586 | int fmtpos; /* the pos of fmt */ 587 | const char *fmt; /* the format string pointer */ 588 | } parse_info; 589 | 590 | #define I(field) (info->field) 591 | 592 | static int source(parse_info *info) { 593 | if (I(level) == 0) 594 | lua_pushvalue(I(B)->L, I(narg)++); 595 | else { 596 | if (!I(is_stringkey)) 597 | lua_pushinteger(I(B)->L, I(index)++); 598 | lua_gettable(I(B)->L, -2); 599 | } 600 | return -1; 601 | } 602 | 603 | static const char *source_lstring(parse_info *info, size_t *plen) { 604 | int narg = source(info); 605 | if (lb_isbufferorstring(I(B)->L, narg)) 606 | return lb_tolstring(I(B)->L, narg, plen); 607 | else if (I(level) == 0) 608 | type_error(I(B)->L, I(narg) - 1, "string"); 609 | else { 610 | lua_pushfstring(I(B)->L, 611 | "buffer/string expected in [%d], got %s", 612 | I(index) - 1, luaL_typename(I(B)->L, narg)); 613 | luaL_argerror(I(B)->L, I(narg) - 1, lua_tostring(I(B)->L, -1)); 614 | } 615 | return NULL; 616 | } 617 | 618 | static lua_Integer source_integer(parse_info *info) { 619 | int narg = source(info); 620 | if (lua_isnumber(I(B)->L, narg)) 621 | return lua_tointeger(I(B)->L, narg); 622 | else if (I(level) == 0) 623 | type_error(I(B)->L, I(narg) - 1, "integer"); 624 | else { 625 | lua_pushfstring(I(B)->L, 626 | "integer expected in [%d], got %s", 627 | I(index) - 1, luaL_typename(I(B)->L, narg)); 628 | luaL_argerror(I(B)->L, I(narg) - 1, lua_tostring(I(B)->L, -1)); 629 | } 630 | return 0; 631 | } 632 | 633 | static lua_Number source_number(parse_info *info) { 634 | int narg = source(info); 635 | if (lua_isnumber(I(B)->L, narg)) 636 | return lua_tonumber(I(B)->L, narg); 637 | else if (I(level) == 0) 638 | type_error(I(B)->L, I(narg) - 1, "number"); 639 | else { 640 | lua_pushfstring(I(B)->L, 641 | "number expected in [%d], got %s", 642 | I(index) - 1, luaL_typename(I(B)->L, narg)); 643 | luaL_argerror(I(B)->L, I(narg) - 1, lua_tostring(I(B)->L, -1)); 644 | } 645 | return 0; 646 | } 647 | 648 | static void sink(parse_info *info) { 649 | if (I(level) == 0) 650 | ++I(nret); 651 | else { 652 | if (!I(is_stringkey)) { 653 | lua_pushinteger(I(B)->L, I(index)++); 654 | lua_insert(I(B)->L, -2); 655 | } 656 | lua_settable(I(B)->L, -3); 657 | } 658 | } 659 | 660 | #define pack_checkstack(n) \ 661 | luaL_checkstack(I(B)->L, (n), "too much top level formats") 662 | 663 | static int fmterror(parse_info *info, const char *msgfmt, ...) { 664 | const char *msg; 665 | va_list list; 666 | va_start(list, msgfmt); 667 | msg = lua_pushvfstring(I(B)->L, msgfmt, list); 668 | va_end(list); 669 | return luaL_argerror(I(B)->L, I(fmtpos), msg); 670 | } 671 | 672 | static const char *lb_pushlstring(lua_State *L, const char *str, size_t len) { 673 | return lb_pushbuffer(L, str, len)->b; 674 | } 675 | 676 | #if LUA_VERSION_NUM < 502 677 | static const char *my_lua_pushlstring(lua_State *L, const char *str, size_t len) { 678 | lua_pushlstring(L, str, len); 679 | return str; 680 | } 681 | #else 682 | # define my_lua_pushlstring lua_pushlstring 683 | #endif 684 | 685 | static int do_packfmt(parse_info *info, char fmt, size_t wide, int count) { 686 | size_t pos; 687 | int top = lua_gettop(I(B)->L); 688 | typedef const char *(*pushlstring_t)(lua_State * L, const char * str, size_t len); 689 | pushlstring_t pushlstring = isupper(fmt) ? lb_pushlstring : my_lua_pushlstring; 690 | 691 | #define SINK() do { if (I(level) == 0 && count < 0) pack_checkstack(1); \ 692 | sink(info); } while (0) 693 | #define BEGIN_PACK() \ 694 | if (I(is_pack)) { \ 695 | while ((count >= 0 || I(narg) <= top) && count--) 696 | #define BEGIN_UNPACK() \ 697 | } else { \ 698 | size_t blen = I(B)->n; \ 699 | if (count > 0) pack_checkstack(count); \ 700 | while (count--) 701 | #define END_PACK() \ 702 | } break 703 | 704 | switch (fmt) { 705 | case 's': case 'S': /* zero-terminated string */ 706 | case 'z': case 'Z': /* zero-terminated string */ 707 | BEGIN_PACK() { 708 | size_t len; 709 | const char *s = source_lstring(info, &len); 710 | if (wide != 0 && len > wide) len = wide; 711 | lb_atpos(I(B), I(pos), { 712 | lb_addlstring(I(B), s, len); 713 | lb_addchar(I(B), '\0'); 714 | }); 715 | I(pos) += len + 1; 716 | lua_pop(I(B)->L, 1); /* pop source */ 717 | } 718 | BEGIN_UNPACK() { 719 | size_t len = 0; 720 | while ((wide == 0 || len < wide) 721 | && I(pos) + len < blen 722 | && I(B)->b[I(pos) + len] != '\0') 723 | ++len; 724 | if ((fmt == 'z' || fmt == 'Z') 725 | && (I(pos) + len) >= blen 726 | && (wide == 0 || len < wide)) 727 | return 0; 728 | pushlstring(I(B)->L, &I(B)->b[I(pos)], len); SINK(); 729 | I(pos) += len; 730 | if (I(pos) < blen && I(B)->b[I(pos)] == '\0') ++I(pos); 731 | } 732 | END_PACK(); 733 | 734 | case 'b': case 'B': /* byte */ 735 | case 'c': case 'C': /* char */ 736 | if (wide == 0) wide = 1; 737 | BEGIN_PACK() { 738 | size_t len; 739 | const char *s = source_lstring(info, &len); 740 | if (wide != 0 && len > wide) len = wide; 741 | lb_atpos(I(B), I(pos), { 742 | lb_addlstring(I(B), s, len); 743 | if (wide > len) 744 | lb_addpadding(I(B), 0, wide - len); 745 | }); 746 | I(pos) += len; 747 | lua_pop(I(B)->L, 1); /* pop source */ 748 | } 749 | BEGIN_UNPACK() { 750 | if ((fmt == 'b' || fmt == 'B') 751 | && (I(pos) + wide >= blen)) 752 | return 0; 753 | if (I(pos) + wide > blen) wide = blen - I(pos); 754 | pushlstring(I(B)->L, &I(B)->b[I(pos)], wide); SINK(); 755 | I(pos) += wide; 756 | } 757 | END_PACK(); 758 | 759 | case 'd': case 'D': /* length preceded data */ 760 | case 'p': case 'P': /* length preceded string */ 761 | if (wide == 0) wide = 4; 762 | if (wide > 8) fmterror( 763 | info, 764 | "invalid wide of format '%c': only 1 to 8 supported.", fmt); 765 | BEGIN_PACK() { 766 | size_t len; 767 | const char *str = source_lstring(info, &len); 768 | lb_atpos(I(B), I(pos), { 769 | lb_packint(I(B), wide, I(is_bigendian), 770 | (lua_Integer)len); 771 | lb_addlstring(I(B), str, len); 772 | }); 773 | I(pos) += wide+len; 774 | lua_pop(I(B)->L, 1); /* pop source */ 775 | } 776 | BEGIN_UNPACK() { 777 | lua_Integer len; 778 | if (I(pos) + wide > blen) return 0; 779 | lb_unpackuint(&I(B)->b[I(pos)], wide, 780 | I(is_bigendian), &len); 781 | if (len > (size_t)(~(size_t)0)/2) 782 | fmterror(info, "string too big in format '%c'", fmt); 783 | if ((fmt == 'd' || fmt == 'D') && I(pos) + wide + len > blen) 784 | return 0; 785 | I(pos) += wide; 786 | if (I(pos) + len > blen) 787 | len = blen - I(pos); 788 | pushlstring(I(B)->L, &I(B)->b[I(pos)], len); SINK(); 789 | I(pos) += len; 790 | } 791 | END_PACK(); 792 | 793 | case 'i': case 'I': /* int */ 794 | case 'u': case 'U': /* unsigned int */ 795 | if (wide == 0) wide = 4; 796 | if (wide > 8) fmterror( 797 | info, 798 | "invalid wide of format '%c': only 1 to 8 supported.", fmt); 799 | BEGIN_PACK() { 800 | lb_atpos(I(B), I(pos), lb_packint(I(B), wide, I(is_bigendian), 801 | source_integer(info))); 802 | I(pos) += wide; 803 | lua_pop(I(B)->L, 1); /* pop source */ 804 | } 805 | BEGIN_UNPACK() { 806 | lua_Integer i; 807 | if (I(pos) + wide > blen) return 0; 808 | if (fmt == 'u' || fmt == 'U') 809 | lb_unpackuint(&I(B)->b[I(pos)], wide, 810 | I(is_bigendian), &i); 811 | else 812 | lb_unpackint(&I(B)->b[I(pos)], wide, 813 | I(is_bigendian), &i); 814 | I(pos) += wide; 815 | lua_pushinteger(I(B)->L, i); SINK(); 816 | } 817 | END_PACK(); 818 | 819 | case 'f': case 'F': /* float */ 820 | if (wide == 0) wide = 4; 821 | if (wide != 4 && wide != 8) fmterror( 822 | info, 823 | "invalid wide of format '%c': only 4 or 8 supported.", fmt); 824 | BEGIN_PACK() { 825 | lua_Number num = source_number(info); 826 | lb_atpos(I(B), I(pos), 827 | lb_packfloat(I(B), wide, I(is_bigendian), num)); 828 | I(pos) += wide; 829 | lua_pop(I(B)->L, 1); /* pop source */ 830 | } 831 | BEGIN_UNPACK() { 832 | lua_Number n; 833 | if (I(pos) + wide > blen) return 0; 834 | lb_unpackfloat(&I(B)->b[I(pos)], wide, 835 | I(is_bigendian), &n); 836 | I(pos) += wide; 837 | lua_pushnumber(I(B)->L, n); SINK(); 838 | } 839 | END_PACK(); 840 | 841 | case '@': /* seek for absolute address */ 842 | pos = wide * count - 1; goto check_seek; 843 | case '+': /* seek for positive address */ 844 | pos = I(pos) + wide * count; goto check_seek; 845 | case '-': /* seek for negitive address */ 846 | pos = wide * count; 847 | if (I(pos) > pos) 848 | pos = I(pos) - pos; 849 | else 850 | pos = 0; 851 | check_seek: 852 | if (count < 0) 853 | fmterror(info, "invalid count of format '%c'", fmt); 854 | if (pos > I(B)->n) pos = I(B)->n; 855 | I(pos) = pos; 856 | break; 857 | 858 | default: 859 | fmterror(info, "invalid format '%c'", fmt); 860 | break; 861 | } 862 | return 1; 863 | #undef SINK 864 | #undef END_PACK 865 | #undef BEGIN_UNPACK 866 | #undef BEGIN_PACK 867 | } 868 | 869 | static int do_delimiter(parse_info *info, char fmt) { 870 | switch (fmt) { 871 | case '{': 872 | /* when meet a open-block, 3 value will be pushed onto stack: 873 | * the current index, the string key (or nil), and a new table 874 | * of block. so the extra used stack space equals level * 3. 875 | * stack: [args] [[index][stringkey][table]] ... [stringkey] 876 | * NOTE: if you changed stack structure, you *MUST* change the 877 | * pop stack behavior in parse_fmt !! */ 878 | luaL_checkstack(I(B)->L, 4, "table level too big"); 879 | if (!I(is_pack)) { 880 | if (!I(is_stringkey)) 881 | lua_pushnil(I(B)->L); 882 | lua_pushinteger(I(B)->L, I(index)); 883 | lua_insert(I(B)->L, -2); 884 | lua_newtable(I(B)->L); 885 | } 886 | else { 887 | source(info); 888 | lua_pushinteger(I(B)->L, I(index)); 889 | lua_insert(I(B)->L, -2); 890 | lua_pushnil(I(B)->L); 891 | lua_insert(I(B)->L, -2); 892 | } 893 | I(level) += 1; 894 | I(index) = 1; 895 | break; 896 | 897 | case '}': 898 | if (I(level) <= 0) 899 | fmterror(info, "unbalanced '}' in format near "LUA_QS, I(fmt) - 1); 900 | I(index) = lua_tointeger(I(B)->L, -3); 901 | I(level) -= 1; 902 | lua_remove(I(B)->L, -3); 903 | if (I(is_pack)) { 904 | lua_pop(I(B)->L, 2); 905 | } 906 | else { 907 | if (!lua_isnil(I(B)->L, -2)) 908 | I(is_stringkey) = 1; 909 | else { 910 | lua_remove(I(B)->L, -2); 911 | I(is_stringkey) = 0; 912 | } 913 | pack_checkstack(1); 914 | sink(info); 915 | } 916 | break; 917 | 918 | case '#': /* current pos */ 919 | if (I(level) != 0) 920 | fmterror(info, "can only retrieve position out of block"); 921 | lua_pushinteger(I(B)->L, I(pos) + 1); 922 | pack_checkstack(1); 923 | sink(info); 924 | break; 925 | 926 | case '<': /* little bigendian */ 927 | I(is_bigendian) = 0; break; 928 | case '>': /* big bigendian */ 929 | I(is_bigendian) = 1; break; 930 | case '=': /* native bigendian */ 931 | #if LB_BIGENDIAN 932 | I(is_bigendian) = 1; break; 933 | #else 934 | I(is_bigendian) = 0; break; 935 | #endif 936 | break; 937 | 938 | default: 939 | return 0; 940 | } 941 | return 1; 942 | } 943 | 944 | #define skip_white(s) do { while (*(s) == ' ' || *(s) == '\t' \ 945 | || *(s) == '\r'|| *(s) == '\n' || *(s) == ',') ++(s); } while(0) 946 | 947 | static int parse_optint(const char **str, unsigned int *pn) { 948 | unsigned int n = 0; 949 | const char *oldstr = *str; 950 | while (isdigit(**str)) n = n * 10 + uchar(*(*str)++ - '0'); 951 | if (*str != oldstr) *pn = n; 952 | return n; 953 | } 954 | 955 | static void parse_fmtargs(parse_info *info, size_t *wide, int *count) { 956 | skip_white(I(fmt)); 957 | parse_optint(&I(fmt), wide); 958 | skip_white(I(fmt)); 959 | if (*I(fmt) == '*') { 960 | size_t ucount = *count; 961 | ++I(fmt); 962 | skip_white(I(fmt)); 963 | parse_optint(&I(fmt), &ucount); 964 | *count = ucount; 965 | } 966 | else if (*I(fmt) == '$') { 967 | ++I(fmt); 968 | *count = -1; 969 | } 970 | skip_white(I(fmt)); 971 | } 972 | 973 | static void parse_stringkey(parse_info *info) { 974 | skip_white(I(fmt)); 975 | if (isalpha(*I(fmt)) || *I(fmt) == '_') { 976 | const char *curpos = I(fmt)++, *end; 977 | while (isalnum(*I(fmt)) || *I(fmt) == '_') 978 | ++I(fmt); 979 | end = I(fmt); 980 | skip_white(I(fmt)); 981 | if (*I(fmt) != '=') 982 | I(fmt) = curpos; 983 | else { 984 | ++I(fmt); 985 | skip_white(I(fmt)); 986 | if (*I(fmt) == '}' || *I(fmt) == '\0') 987 | fmterror(info, "key without format near "LUA_QS, curpos); 988 | if (I(level) == 0) 989 | fmterror(info, "key at top level near "LUA_QS, curpos); 990 | lua_pushlstring(I(B)->L, curpos, end - curpos); 991 | I(is_stringkey) = 1; 992 | return; 993 | } 994 | } 995 | I(is_stringkey) = 0; 996 | } 997 | 998 | static int parse_fmt(parse_info *info) { 999 | int fmt, insert_pos = 0; 1000 | skip_white(I(fmt)); 1001 | if (*I(fmt) == '!') { 1002 | insert_pos = 1; /* only enabled in unpack */ 1003 | ++I(fmt); 1004 | } 1005 | while (parse_stringkey(info), (fmt = *I(fmt)++) != '\0') { 1006 | if (!do_delimiter(info, fmt)) { 1007 | size_t wide = 0; 1008 | int count = 1; 1009 | parse_fmtargs(info, &wide, &count); 1010 | if (!do_packfmt(info, fmt, wide, count)) { 1011 | if (I(is_stringkey)) 1012 | lua_pop(I(B)->L, 1); 1013 | lua_pop(I(B)->L, I(level) * 3); /* 3 values per level */ 1014 | I(level) = 0; 1015 | lua_pushnil(I(B)->L); ++I(nret); 1016 | skip_white(I(fmt)); 1017 | /* skip any block */ 1018 | while (*I(fmt) == '{' || *I(fmt) == '}') { 1019 | ++I(fmt); 1020 | skip_white(I(fmt)); 1021 | } 1022 | if ((fmt = *I(fmt)++) == '#') 1023 | do_delimiter(info, fmt); 1024 | break; 1025 | } 1026 | } 1027 | } 1028 | if (I(level) != 0) 1029 | fmterror(info, "unbalanced '{' in format"); 1030 | if (insert_pos) { 1031 | lua_pushinteger(I(B)->L, I(pos) + 1); 1032 | lua_insert(I(B)->L, -(++I(nret))); 1033 | } 1034 | return I(nret); 1035 | } 1036 | 1037 | static int do_pack(lb_Buffer *B, int narg, int pack) { 1038 | lua_State *L = B->L; 1039 | parse_info info = {NULL}; 1040 | info.B = B; 1041 | info.narg = narg; 1042 | info.is_pack = pack; 1043 | #if LB_BIGENDIAN 1044 | info.is_bigendian = 1; 1045 | #endif 1046 | if (lua_type(L, info.narg) == LUA_TNUMBER) 1047 | info.pos = posrelat(lua_tointeger(L, info.narg++), info.B->n); 1048 | info.fmtpos = info.narg++; 1049 | info.fmt = lb_checklstring(L, info.fmtpos, NULL); 1050 | parse_fmt(&info); 1051 | if (pack) { 1052 | lua_pushinteger(L, info.pos + 1); 1053 | lua_insert(L, -(++info.nret)); 1054 | } 1055 | return info.nret; 1056 | } 1057 | 1058 | static int Lpack(lua_State *L) { 1059 | int res; 1060 | lb_Buffer *B; 1061 | if ((B = lb_testbuffer(L, 1)) != NULL) { 1062 | res = do_pack(B, 2, 1); 1063 | lua_pushvalue(L, 1); 1064 | } 1065 | else { 1066 | lb_Buffer buff; 1067 | lb_buffinit(L, &buff); 1068 | res = do_pack(&buff, 1, 1); 1069 | lb_copybuffer(&buff); 1070 | } 1071 | lua_insert(L, -res-1); 1072 | return res+1; 1073 | } 1074 | 1075 | static int Lunpack(lua_State *L) { 1076 | if (lua_type(L, 1) == LUA_TSTRING) { 1077 | lb_Buffer B; /* a fake buffer */ 1078 | lb_buffinit(L, &B); 1079 | /* in unpack, all functions never changed the content of 1080 | * buffer, so use force cast is safe */ 1081 | B.b = (char*)lua_tolstring(L, 1, &B.n); 1082 | return do_pack(&B, 2, 0); 1083 | } 1084 | return do_pack(lb_checkbuffer(L, 1), 2, 0); 1085 | } 1086 | 1087 | #undef I 1088 | 1089 | 1090 | /* meta methods */ 1091 | 1092 | static int L__gc(lua_State *L) { 1093 | lb_Buffer *B; 1094 | if ((B = lb_testbuffer(L, 1)) != NULL) 1095 | lb_resetbuffer(B); 1096 | return 0; 1097 | } 1098 | 1099 | static int L__concat(lua_State *L) { 1100 | size_t l1, l2; 1101 | const char *s1 = lb_checklstring(L, 1, &l1); 1102 | const char *s2 = lb_checklstring(L, 2, &l2); 1103 | lb_Buffer *B = lb_newbuffer(L); 1104 | lb_addlstring(B, s1, l1); 1105 | lb_addlstring(B, s2, l2); 1106 | return 1; 1107 | } 1108 | 1109 | static int L__index(lua_State *L) { 1110 | lb_Buffer *B; 1111 | int pos; 1112 | 1113 | switch (lua_type(L, 2)) { 1114 | case LUA_TSTRING: 1115 | if (lua_getmetatable(L, 1)) { 1116 | lua_pushvalue(L, 2); 1117 | lua_rawget(L, -2); 1118 | return 1; 1119 | } 1120 | return 0; 1121 | 1122 | case LUA_TNUMBER: 1123 | B = lb_checkbuffer(L, 1); 1124 | pos = lua_tointeger(L, 2); 1125 | if (pos == 0 || (pos = posrelat(pos, B->n)) == B->n) 1126 | return 0; 1127 | lua_pushinteger(L, uchar(B->b[pos])); 1128 | return 1; 1129 | 1130 | default: 1131 | return 0; 1132 | } 1133 | } 1134 | 1135 | static int L__newindex(lua_State *L) { 1136 | lb_Buffer *B = lb_checkbuffer(L, 1); 1137 | int ch, pos = (int)luaL_checkinteger(L, 2); 1138 | size_t len; 1139 | const char *s; 1140 | 1141 | if ((size_t)-pos < B->n || (size_t)pos > B->n+1) 1142 | luaL_error(L, "invalid index #%d to buffer, should in %d-%d", 1143 | pos, 1, B->n+1); 1144 | 1145 | if (pos < 0) pos += B->n; 1146 | else pos -= 1; 1147 | 1148 | switch (lua_type(L, 3)) { 1149 | case LUA_TNUMBER: 1150 | ch = (int)lua_tointeger(L, 3); 1151 | set_char: 1152 | if (pos != B->n) 1153 | B->b[pos] = uchar(ch); 1154 | else { 1155 | lb_prepbuffsize(B, 1); 1156 | B->b[pos] = uchar(ch); 1157 | lb_addsize(B, 1); 1158 | } 1159 | return 1; 1160 | 1161 | case LUA_TSTRING: 1162 | case LUA_TUSERDATA: 1163 | s = lb_checklstring(L, 3, &len); 1164 | if (len == 1) { 1165 | ch = s[0]; 1166 | goto set_char; 1167 | } 1168 | else if (pos == B->n) { /* append */ 1169 | lb_prepbuffsize(B, len); 1170 | memcpy(&B->b[pos], s, len); 1171 | lb_addsize(B, len); 1172 | } 1173 | else { /* replace */ 1174 | lb_prepbuffsize(B, len - 1); 1175 | memmove(&B->b[pos + len], &B->b[pos + 1], B->n - pos - 1); 1176 | memcpy(&B->b[pos], s, len); 1177 | lb_addsize(B, len - 1); 1178 | } 1179 | return 1; 1180 | 1181 | case LUA_TNIL: 1182 | case LUA_TNONE: 1183 | if (pos == B->n-1) B->n -= 1; 1184 | 1185 | default: 1186 | type_error(L, 3, "string/buffer/number"); 1187 | return 0; 1188 | } 1189 | } 1190 | 1191 | static int Llibcall(lua_State *L) { 1192 | lua_pushcfunction(L, Lnew); 1193 | lua_replace(L, 1); 1194 | lua_call(L, lua_gettop(L)-1, LUA_MULTRET); 1195 | return lua_gettop(L); 1196 | } 1197 | 1198 | #ifdef LB_REDIR_STRLIB 1199 | static int redir_to_strlib(lua_State *L, const char *name) { 1200 | lb_Buffer *B = lb_testbuffer(L, 1); 1201 | int i, base = 1, top = lua_gettop(L); 1202 | if (B != NULL) { 1203 | lua_pushlstring(L, B->b, B->n); 1204 | lua_insert(L, 2); 1205 | base += 1; 1206 | top += 1; 1207 | } 1208 | for (i = base; i <= top; ++i) { 1209 | lb_Buffer *b = lb_testbuffer(L, i); 1210 | if (b != NULL) { 1211 | lua_pushlstring(L, B->b, B->n); 1212 | lua_replace(L, i); 1213 | } 1214 | } 1215 | lua_getglobal(L, "string"); 1216 | lua_getfield(L, -1, name); 1217 | lua_remove(L, -2); 1218 | if (lua_isnil(L, -1)) 1219 | return luaL_error(L, "can not find function "LUA_QS" in "LUA_QS, 1220 | name, "string"); 1221 | lua_insert(L, base); 1222 | lua_call(L, top - base + 1, LUA_MULTRET); 1223 | if (lua_isstring(L, 2) && B != NULL) { 1224 | size_t len; 1225 | const char *str = lua_tolstring(L, 2, &len); 1226 | B->n = 0; 1227 | memcpy(lb_prepbuffsize(B, len), str, len); 1228 | B->n = len; 1229 | lua_remove(L, 2); 1230 | } 1231 | return lua_gettop(L); 1232 | } 1233 | 1234 | #define redir_functions(X) \ 1235 | X(dump) X(find) X(format) X(gmatch) X(gsub) X(match) 1236 | 1237 | #define X(name) \ 1238 | static int lbR_##name (lua_State *L) \ 1239 | { return redir_to_strlib(L, #name); } 1240 | redir_functions(X) 1241 | #undef X 1242 | #endif /* LB_REDIR_STRLIB */ 1243 | 1244 | /* module registration */ 1245 | 1246 | int luaopen_buffer(lua_State *L) { 1247 | luaL_Reg libs[] = { 1248 | #ifdef LB_REDIR_STRLIB 1249 | #define ENTRY(name) { #name, lbR_##name }, 1250 | redir_functions(ENTRY) 1251 | #undef ENTRY 1252 | #endif /* LB_REDIR_STRLIB */ 1253 | 1254 | #define ENTRY(name) { #name, L##name } 1255 | ENTRY(new), 1256 | ENTRY(__gc), 1257 | ENTRY(__concat), 1258 | ENTRY(__tostring), 1259 | ENTRY(__index), 1260 | ENTRY(__newindex), 1261 | { "__len", Llen }, 1262 | { "__eq", Leq }, 1263 | #if LUA_VERSION_NUM >= 502 1264 | { "__ipairs", Lipairs }, 1265 | { "__pairs", Lipairs }, 1266 | #endif 1267 | 1268 | /* request */ 1269 | ENTRY(byte), 1270 | ENTRY(cmp), 1271 | ENTRY(eq), 1272 | ENTRY(ipairs), 1273 | ENTRY(isbuffer), 1274 | ENTRY(len), 1275 | ENTRY(quote), 1276 | ENTRY(topointer), 1277 | 1278 | /* modify */ 1279 | ENTRY(char), 1280 | ENTRY(clear), 1281 | ENTRY(copy), 1282 | ENTRY(insert), 1283 | ENTRY(lower), 1284 | ENTRY(move), 1285 | ENTRY(remove), 1286 | ENTRY(rep), 1287 | ENTRY(reverse), 1288 | ENTRY(set), 1289 | ENTRY(setlen), 1290 | ENTRY(swap), 1291 | ENTRY(upper), 1292 | 1293 | /* binary support */ 1294 | ENTRY(tohex), 1295 | ENTRY(getint), 1296 | ENTRY(getuint), 1297 | ENTRY(pack), 1298 | { "setint", Lsetuint }, 1299 | ENTRY(setuint), 1300 | ENTRY(unpack), 1301 | #undef ENTRY 1302 | { NULL, NULL } 1303 | }; 1304 | 1305 | /* create metatable */ 1306 | if (luaL_newmetatable(L, LB_LIBNAME)) { 1307 | luaL_setfuncs(L, libs, 0); /* 3->2 */ 1308 | lua_pushvalue(L, -1); /* 3 */ 1309 | lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)LB_METAKEY); /* 3->env */ 1310 | } 1311 | 1312 | lua_createtable(L, 0, 1); /* 2 */ 1313 | lua_pushcfunction(L, Llibcall); /* 3 */ 1314 | lua_setfield(L, -2, "__call"); /* 3->2 */ 1315 | lua_setmetatable(L, -2); /* 2->1 */ 1316 | 1317 | lua_pushliteral(L, LB_VERSION); /* 2 */ 1318 | lua_setfield(L, -2, "_VERSION"); /* 2->1 */ 1319 | 1320 | return 1; 1321 | } 1322 | 1323 | /* 1324 | * cc: flags+='-shared -s -O2 -Wall -pedantic' libs+='-llua53' 1325 | * cc: flags+='-DLB_REDIR_STRLIB=1 -DLB_FILEHANDLE -DLUA_BUILD_AS_DLL' 1326 | * cc: input='lb*.c' output='buffer.dll' run='lua test.lua' */ 1327 | -------------------------------------------------------------------------------- /lua_with_lbuffer/linit_modified.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $ 3 | ** Initialization of libraries for lua.c 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | 8 | #define linit_c 9 | #define LUA_LIB 10 | 11 | #include "lua.h" 12 | 13 | #include "lualib.h" 14 | #include "lauxlib.h" 15 | #include "lbuffer.h" 16 | 17 | 18 | static const luaL_Reg lualibs[] = { 19 | {"", luaopen_base}, 20 | {LUA_LOADLIBNAME, luaopen_package}, 21 | {LUA_TABLIBNAME, luaopen_table}, 22 | {LUA_IOLIBNAME, luaopen_io}, 23 | {LUA_OSLIBNAME, luaopen_os}, 24 | {LUA_STRLIBNAME, luaopen_string}, 25 | {LUA_MATHLIBNAME, luaopen_math}, 26 | {LUA_DBLIBNAME, luaopen_debug}, 27 | {LUA_DBLIBNAME, luaopen_debug}, 28 | {LB_LIBNAME, luaopen_buffer}, 29 | {NULL, NULL} 30 | }; 31 | 32 | 33 | LUALIB_API void luaL_openlibs (lua_State *L) { 34 | const luaL_Reg *lib = lualibs; 35 | for (; lib->func; lib++) { 36 | lua_pushcfunction(L, lib->func); 37 | lua_pushstring(L, lib->name); 38 | lua_call(L, 1, 0); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /lua_with_lbuffer/luavs_lbuffer.bat: -------------------------------------------------------------------------------- 1 | @rem Script to build Lua under "Visual Studio .NET Command Prompt". 2 | @rem Do not run from this directory; run it from the toplevel: etc\luavs.bat . 3 | @rem It creates lua51.dll, lua51.lib, lua.exe, and luac.exe in src. 4 | @rem (contributed by David Manura and Mike Pall) 5 | @if not .%1 == . goto :compile 6 | @echo usage: %0 lua_src_folder (without trailing slash) 7 | @goto :end 8 | 9 | :compile 10 | 11 | @setlocal 12 | @set LUA_SRC=%1/src 13 | @set MYCOMPILE=cl /nologo /MT /O2 /W3 /c /D_CRT_SECURE_NO_DEPRECATE 14 | @set LBLIB_CFLAGS=/I "%LUA_SRC%" /DLUA_LIB /DLB_REPLACE_LUA_API /FI "%~dp0..\lbuffer.h" /wd4005 15 | @set MYLINK=link /nologo 16 | @set MYLIB=lib /nologo 17 | @set MYMT=mt /nologo 18 | 19 | %MYCOMPILE% /DLUA_BUILD_AS_DLL %LUA_SRC%\l*.c 20 | %MYCOMPILE% /DLUA_BUILD_AS_DLL /I "%LUA_SRC%" /I "%~dp0.." ..\*.c *.c 21 | %MYCOMPILE% /DLUA_BUILD_AS_DLL /Dlbaselib_c %LBLIB_CFLAGS% %LUA_SRC%\lbaselib.c 22 | %MYCOMPILE% /DLUA_BUILD_AS_DLL /Dliolib_c %LBLIB_CFLAGS% %LUA_SRC%\liolib.c 23 | %MYCOMPILE% /DLUA_BUILD_AS_DLL /Dlmathlib_c %LBLIB_CFLAGS% %LUA_SRC%\lmathlib.c 24 | %MYCOMPILE% /DLUA_BUILD_AS_DLL /Dloslib_c %LBLIB_CFLAGS% %LUA_SRC%\loslib.c 25 | %MYCOMPILE% /DLUA_BUILD_AS_DLL /Dlstrlib_c %LBLIB_CFLAGS% %LUA_SRC%\lstrlib.c 26 | %MYCOMPILE% /DLUA_BUILD_AS_DLL /Dltablib_c %LBLIB_CFLAGS% %LUA_SRC%\ltablib.c 27 | del lua.obj luac.obj linit.obj 28 | %MYLINK% /DLL /out:lua51.dll l*.obj 29 | move lua51.lib lua51dyn.lib 30 | %MYLIB% /out:lua51.lib l*.obj 31 | if exist lua51.dll.manifest^ 32 | %MYMT% -manifest lua51.dll.manifest -outputresource:lua51.dll;2 33 | %MYCOMPILE% /DLUA_BUILD_AS_DLL %LUA_SRC%\lua.c 34 | %MYLINK% /out:lua.exe lua.obj lua51dyn.lib 35 | if exist lua.exe.manifest^ 36 | %MYMT% -manifest lua.exe.manifest -outputresource:lua.exe 37 | %MYCOMPILE% %LUA_SRC%\l*.c %LUA_SRC%\print.c 38 | del lua.obj linit.obj lbaselib.obj ldblib.obj liolib.obj lmathlib.obj^ 39 | loslib.obj ltablib.obj lstrlib.obj loadlib.obj linit_modified.obj 40 | %MYLINK% /out:luac.exe *.obj 41 | if exist luac.exe.manifest^ 42 | %MYMT% -manifest luac.exe.manifest -outputresource:luac.exe 43 | del *.obj *.manifest 44 | 45 | :end 46 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | local buffer = require 'buffer' 2 | 3 | -- borrowed from brimworks's lua-zlib 4 | local counter = 1 5 | local failed = false 6 | function ok(assert_true, desc) 7 | local msg = ( assert_true and "ok " or "not ok " ) .. counter 8 | if ( not assert_true ) then 9 | failed = true 10 | msg = msg .. " at line "..debug.getinfo(2, "l").currentline 11 | end 12 | if ( desc ) then 13 | msg = msg .. " - " .. desc 14 | end 15 | print(msg) 16 | counter = counter + 1 17 | end 18 | 19 | function test_msg(msg) 20 | print(('-'):rep(20)) 21 | print(msg) 22 | print(('-'):rep(20)) 23 | end 24 | 25 | function test() 26 | print('buffer '..buffer._VERSION..' test') 27 | if jit then 28 | print('using', jit.version) 29 | end 30 | test_new() 31 | test_map() 32 | test_rep() 33 | test_reverse() 34 | test_alloc() 35 | test_modify() 36 | test_byte() 37 | test_char() 38 | test_clear() 39 | test_copy() 40 | test_move() 41 | test_remove() 42 | test_swap() 43 | test_cmp() 44 | test_mt() 45 | test_pack() 46 | if not failed then 47 | test_msg "** ALL TEST PASSED!!" 48 | else 49 | test_msg "** TEST FAILED!!" 50 | end 51 | end 52 | 53 | function test_new() 54 | test_msg "test initialize buffer" 55 | local b = buffer "one" 56 | ok(b :eq "one", "using string ("..b..')') 57 | local b = buffer(buffer "one") 58 | ok(b :eq "one", "using buffer ("..b..')') 59 | local pb = buffer "one" -- only get pointers from non-collected object!! 60 | local b = buffer(pb:topointer(2), 2) 61 | ok(b :eq "ne", "using userdata ("..b..")") 62 | local fh = assert(io.open "test.lua") 63 | local b = buffer(fh, "*l") 64 | ok(b:len() ~= 0, "using file ("..b..")") 65 | local b = buffer("one", 0) 66 | ok(b :eq "one", "using string range 0 ("..b..")") 67 | local b = buffer("one", -2) 68 | ok(b :eq "ne", "using string range -2 ("..b..")") 69 | local b = buffer("one", 1, 0) 70 | ok(b :eq "", "using string range 1, 0 ("..b..")") 71 | local b = buffer("one", 1, 1) 72 | ok(b :eq "o", "using string range 1, 1 ("..b..")") 73 | local b = buffer("one", 2, -2) 74 | ok(b :eq "n", "using string range 2, -2 ("..b..")") 75 | local b = buffer(buffer "one", 2, -2) 76 | ok(b :eq "n", "using buffer range 2, -2 ("..b..")") 77 | local b = buffer(10) 78 | ok(b :eq(("\0"):rep(10)), "using number ("..b:tohex' '..")") 79 | local b = buffer(10, "one") 80 | ok(b :eq "oneoneoneo", "using number and string ("..b..")") 81 | local b = buffer(10, buffer "one") 82 | ok(b :eq "oneoneoneo", "using number and buffer ("..b..")") 83 | local b = buffer(10, "one", 2, -2) 84 | ok(b :eq "nnnnnnnnnn", "using number and string range 2, -2 ("..b..")") 85 | local b = buffer(10, buffer "one", 2, -2) 86 | ok(b :eq "nnnnnnnnnn", "using number and buffer range 2, -2 ("..b..")") 87 | local b = buffer(10, buffer "one":topointer(2), 2) 88 | ok(b :eq "nenenenene", "using number and userdata ("..b..")") 89 | end 90 | 91 | function test_map() 92 | test_msg "test map operations" 93 | local b = buffer "HeLlO123" :upper() 94 | ok(b :eq "HELLO123", "toupper: "..b) 95 | local b = buffer "HeLlO123" :lower() 96 | ok(b :eq "hello123", "tolower: "..b) 97 | end 98 | 99 | function test_mt() 100 | test_msg "test metatable operations" 101 | local b = buffer "abc" 102 | ok(b[1] == 97 and b[2] == 98 and b[3] == 99 and not b[4], 103 | "index operation ("..b..")") 104 | ok(b[-3] == 97 and b[-2] == 98 and b[-1] == 99 and not b[0], 105 | "index operation - negitive index ("..b..")") 106 | b[2] = 'z' 107 | ok(b :eq 'azc', "newindex operation ("..b..")") 108 | b[4] = 112 109 | ok(b :eq 'azcp', "newindex operation - append number ("..b..")") 110 | b[5] = '-apple' 111 | ok(b :eq 'azcp-apple', "newindex operation - append string ("..b..")") 112 | ok(#b == 10, "length operation ("..#b..")") 113 | ok((b .. "(string)") :eq "azcp-apple(string)", "concat operation ("..b..")") 114 | local c = 0 115 | local bb = buffer() 116 | for i, v in b:ipairs() do 117 | bb[i] = v 118 | c = c + 1 119 | end 120 | ok(c == 10 and bb:eq(b), "ipairs operation ("..bb..")") 121 | end 122 | 123 | function test_reverse() 124 | test_msg "test reverse operation" 125 | local b = buffer "apple-pie" 126 | ok(b:reverse() :eq "eip-elppa", "reversed whole buffer ("..b..")") 127 | ok(b:reverse(2, 2) :eq "eip-elppa", "reversed a single byte ("..b..")") 128 | ok(b:reverse(2, -2) :eq "epple-pia", "reversed a range ("..b..")") 129 | local b = ("A man, a plan, a Canal - Panama!"):lower():gsub("%W", "") 130 | ok(buffer(b):reverse():eq(b), "test a plalindrome ("..b..")") 131 | end 132 | 133 | function test_rep() 134 | test_msg "test repeat operation" 135 | local b = buffer "abc" 136 | ok(b:rep(10) :eq (("abc"):rep(10)), "rep with number ("..b..")") 137 | ok(b:rep(0) :eq "", "rep with number 0 ("..b..")") 138 | ok(b:rep("abc", 10) :eq (("abc"):rep(10)), "rep with string and number ("..b..")") 139 | ok(b:rep("abc", 0) :eq "", "rep with string and number 0 ("..b..")") 140 | ok(b:rep(-2) :eq "", "rep with negitive number ("..b..")") 141 | ok(b:rep("abc", -2) :eq "", "rep with negitive number ("..b..")") 142 | end 143 | 144 | function test_alloc() 145 | test_msg "test alloc & free & len" 146 | local b = buffer(10) 147 | ok(#b == 10, "length of buffer ("..#b..")") 148 | ok(#b == b:len(), "length of buffer ("..#b..")") 149 | b:setlen(0) 150 | ok(#b == 0, "free buffer ("..#b..")") 151 | b:setlen(20) 152 | ok(#b == 20, "alloc buffer ("..#b..")") 153 | b:setlen(15) 154 | ok(#b == 15, "set length of buffer ("..#b..")") 155 | b:setlen(-14) 156 | ok(#b == 1, "set negitive length of buffer ("..#b..")") 157 | b:setlen(-10) 158 | ok(#b == 0, "set negitive length of buffer ("..#b..")") 159 | end 160 | 161 | function test_modify() 162 | test_msg "test modified operations" 163 | local b = buffer "apple" 164 | b:insert "-pie" 165 | ok(b :eq "apple-pie", "append operations ("..b..")") 166 | b:insert(7, "(xxx)-") 167 | ok(b :eq "apple-(xxx)-pie", "insert operations ("..b..")") 168 | b:set(7, "[===]") 169 | ok(b :eq "apple-[===]-pie", "set operations ("..b..")") 170 | b:set "apple-pie" 171 | ok(b :eq "apple-pie", "assign operations ("..b..")") 172 | end 173 | 174 | function test_byte() 175 | test_msg "test byte operations" 176 | local b = buffer "apple-pie" 177 | ok(b:byte() == ('a'):byte(), 'byte of first byte of ('..b..'): '..b:byte()) 178 | ok(b:byte(-1) == ('e'):byte(), 'byte of last byte of ('..b..'): '..b:byte(-1)) 179 | ok(b:byte(-1) == ('e'):byte(), 'byte of last byte of ('..b..'): '..b:byte(-1)) 180 | local t = {b:byte(1, -1)} 181 | ok(#t == #b, "bytes of 1, -1 length "..#t) 182 | local same = true 183 | for i = 1, #t do 184 | if t[i] ~= ("apple-pie"):byte(i) then 185 | same = false 186 | end 187 | end 188 | ok(same, "evey bytes from buffer is same as string") 189 | end 190 | 191 | function test_char() 192 | test_msg "test char operations" 193 | local b = buffer():char(1,2,3) 194 | ok(#b == 3, "chars of three bytes is "..#b) 195 | local b = buffer "apple-pie":char(0x61,0x62,0x63) 196 | ok(b :eq "apple-pieabc", "chars of three bytes is "..b) 197 | end 198 | 199 | function test_clear() 200 | test_msg "test clear operation" 201 | local b = buffer "apple-pie" :clear(1, 5) 202 | ok(b :eq "\0\0\0\0\0-pie", "clear prefix of buffer ("..b:quote()..")") 203 | local b = buffer "apple-pie" :clear(-3) 204 | ok(b :eq "apple-\0\0\0", "clear postfix of buffer ("..b:quote()..")") 205 | local b = buffer "apple-pie" :clear(-3, 20) 206 | ok(b :eq "apple-\0\0\0", "clear postfix of buffer ("..b:quote()..")") 207 | end 208 | 209 | function test_copy() 210 | test_msg "test copy operation" 211 | local b = buffer "apple-pie" 212 | ok(b:copy() :eq (b), "copy whole buffer ("..b:copy()..")") 213 | ok(b:copy(1,5) :eq "apple", "copy prefix of buffer ("..b:copy(1,5)..")") 214 | ok(b:copy(-3) :eq "pie", "copy postfix of buffer ("..b:copy(-3)..")") 215 | ok(b:copy(6,6) :eq "-", "copy middle of buffer ("..b:copy(6,6)..")") 216 | ok(b:copy(6,4) :eq "", "copy none of buffer ("..b:copy(6,4)..")") 217 | end 218 | 219 | function test_move() 220 | test_msg "test move operation" 221 | local b = buffer "apple-pie" :move(11, 7) 222 | ok(b :eq "apple-pie\0pie", "move postfix over end of buffer ("..b:quote()..')') 223 | local b = buffer "apple-pie" :move(1, 7) 224 | ok(b :eq "piele-pie", "move postfix to the head of buffer ("..b..")") 225 | local b = buffer "apple-pie" :move(-1, 7) 226 | ok(b :eq "apple-pipie", "move overlay of buffer ("..b..")") 227 | local b = buffer "apple-pie" :move(7, 1, 5) 228 | ok(b :eq "apple-apple", "move prefix of buffer ("..b..")") 229 | local b = buffer "apple-pie" :move(-3, 2, 4) 230 | ok(b :eq "apple-ppl", "move middle of buffer ("..b..")") 231 | end 232 | 233 | function test_swap() 234 | test_msg "test swap operation" 235 | local b = buffer"apple-pie" 236 | for i = 1, #b do 237 | local s = tostring(b) 238 | b:swap(i) 239 | ok(b :eq (s:sub(i)..s:sub(1, i-1)), "swap 1~"..(i-1).." and "..i.."~"..#b.." ("..b..")") 240 | end 241 | local b = buffer "apple-pie" 242 | ok(b:swap(1,5,-4):eq "-applepie", "swap two sibling ("..b..")") 243 | local b = buffer "apple-pie" 244 | ok(b:swap(1,5,-3,-1):eq "pie-apple", "swap two range ("..b..")") 245 | end 246 | 247 | function test_remove() 248 | test_msg "test remove operation" 249 | local b = buffer "apple-pie" 250 | b:remove(6) 251 | ok(b :eq "apple", "remove end of buffer ("..b..')') 252 | b = buffer "apple-pie" 253 | b:remove(6, -4) 254 | ok(b :eq "applepie", "remove range of buffer ("..b..')') 255 | b:remove() 256 | ok(b :eq "", "remove all of buffer ("..b..')') 257 | end 258 | 259 | function test_cmp() 260 | test_msg "test cmp operation" 261 | ok(buffer("bb"):cmp(buffer("a")) == 1, "bb:cmp(a) -> 1") 262 | ok(buffer("b"):cmp(buffer("a")) == 1, "b:cmp(a) -> 1") 263 | ok(buffer("a"):cmp(buffer("bb")) == -1, "a:cmp(bb) -> -1") 264 | ok(buffer("a"):cmp(buffer("b")) == -1, "a:cmp(b) -> -1") 265 | end 266 | 267 | function test_pack() 268 | test_msg "test pack operation" 269 | local b, pos = buffer.pack("!s", "apple") 270 | ok(pos == 7 and b :eq "apple\0", "pack null terminated string ("..b:quote()..")") 271 | local s, pos = b :unpack "s#" 272 | ok(pos == 7 and s == "apple", "unpack null terminated string ("..s..")") 273 | local s, pos = b :unpack "S#" 274 | ok(pos == 7 and s :eq "apple", "unpack null terminated string to buffer ("..s..")") 275 | local s = buffer"apple" :unpack "s#" 276 | ok(s == "apple", "unpack imcomplete null terminated string ("..s..")") 277 | local s, pos = buffer"apple" :unpack "z#" 278 | ok(pos == 1 and s == nil, "unpack imcomplete null terminated data ("..pos..")") 279 | local s, pos = buffer"apple" :unpack "{{z}}#" 280 | ok(pos == 1 and s == nil, "table balance of imcomplete data ("..pos..")") 281 | local b, pos = buffer.pack("p#", "apple") 282 | ok(pos == 10 and b :eq "\5\0\0\0apple", "pack length prepend string ("..b:quote()..")") 283 | local pos, s = b :unpack "!p" 284 | ok(pos == 10 and s == "apple", "unpack length prepend string ("..s..")") 285 | local pos, s = buffer"\0\0\0\0" :unpack "!p" 286 | ok(pos == 5 and s == "", "pack length without string ("..pos..")") 287 | local s = buffer "\9\0\0\0apple" :unpack "p" 288 | ok(s == "apple", "pack imcomplete length prepend string ("..s..")") 289 | local pos, s = buffer "\9\0\0\0apple" :unpack "!d" 290 | ok(pos == 1 and s == nil, "pack imcomplete length prepend data ("..pos..")") 291 | local res = string.char(0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0) 292 | for i = 1, 8 do 293 | local b, pos = buffer.pack("!i"..i, -0x12345678) 299 | ok(pos == i+1 and b:eq(res:sub(9-i)), "pack big endian numbers ("..b:tohex' '..")") 300 | end 301 | local res = string.char(0xFF):rep(8) 302 | for i = 1, 8 do 303 | local b, pos = buffer.pack("!>i"..i, -1) 304 | ok(pos == i+1 and b:eq(res:sub(9-i)), "pack big endian numbers ("..b:tohex' '..")") 305 | end 306 | local res = string.char(0xFF):rep(8) 307 | for i = 1, 8 do 308 | local b, pos = buffer.pack("!i8", 0x12345678) 315 | ok(pos == 9 and b :eq(string.char(0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78)), 316 | "pack 64bit big endian number ("..b:tohex' '..")") 317 | local a, b = buffer.unpack("abcdefgh", ">ii") 318 | ok(a == 0x61626364 and b == 0x65666768, "unpack can work with lua string ("..("%08x, %08x"):format(a, b)..")") 319 | end 320 | 321 | test() 322 | --------------------------------------------------------------------------------