├── LICENSE ├── Makefile ├── README.md ├── test.c ├── test.lua ├── vla.c └── vla.h /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | 4 | Copyright (c) 2022 codingnow.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LUA_INC=-I/usr/local/include 2 | LUA_LIB=-L/usr/local/bin -llua54 3 | 4 | CFLAGS = -g -Wall 5 | 6 | test2.exe : test2.c vla.c 7 | gcc -o $@ $(CFLAGS) $^ $(LUA_INC) $(LUA_LIB) 8 | 9 | vlatest.dll : test.c vla.c 10 | gcc --shared -o $@ $(CFLAGS) $^ $(LUA_INC) $(LUA_LIB) 11 | 12 | clean : 13 | rm -f vlatest.dll 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Variable Length Array for C 2 | ==================== 3 | 4 | `vla_handle_t` : A handle of VLA object , it can be passed into C functions or be saved in the C structs. 5 | 6 | ```C 7 | // Create a VLA object with name and type on C stack. 8 | vla_stack_handle(handle_name, type); 9 | 10 | // Create a VLA object with type in Lua memory. (Or C heap when L == NULL) 11 | vla_handle_t vla_new(type, n, L); 12 | 13 | // Create an accessor (type * name) for VLA object (handle). 14 | // You can use the accessor to access the data of VLA, it's just a raw pointer of the data. 15 | void vla_using(name, type, handle, L); 16 | 17 | // Sync accessor if VLA object changes 18 | void vla_sync(name); 19 | 20 | // Close VLA object via accessor 21 | void vla_close(name); 22 | 23 | // Close VLA handle 24 | void vla_close_handle(handle); 25 | 26 | // Resize VLA object via accessor 27 | void vla_resize(name, n, L); 28 | 29 | // Get length of VLA object via accessor 30 | int vla_size(name); 31 | 32 | // Push v into VLA object via accessor 33 | void vla_push(name, v, L); 34 | 35 | // Get Lua stack id via accessor, 0 when unchanged. If you need refer this VLA object, you should save the lua object at stack id. 36 | int vla_luaid(name, L); 37 | ``` 38 | 39 | Example 1: 40 | 41 | ```C 42 | #include "vla.h" 43 | #include 44 | 45 | static void 46 | init(vla_handle_t handle) { 47 | vla_using(p, int, handle, NULL); 48 | int i; 49 | for (i=0;i<1000;i++) { 50 | vla_push(p, i, NULL); 51 | } 52 | // Do not close p 53 | } 54 | 55 | int 56 | main() { 57 | vla_stack_handle(handle, int); // Create an int array on stack 58 | vla_using(p, int, handle, NULL); // Create an acessor int *p for data accessing. 59 | 60 | init(handle); 61 | 62 | vla_sync(p); // init() may changes array 63 | 64 | assert(vla_size(p) == 1000); 65 | 66 | int i; 67 | for (i=0;i<1000;i++) { 68 | assert(p[i] == i); 69 | } 70 | 71 | vla_close(p); // close before return 72 | 73 | return 0; 74 | } 75 | 76 | ``` 77 | 78 | Example 2: 79 | 80 | ```C 81 | #include 82 | #include 83 | 84 | static int 85 | lfoobar(lua_State *L) { 86 | int n = luaL_checkinteger(L, 1); 87 | vla_stack_handle(handle, int); 88 | vla_using(p, int, handle, L); // Create an acessor int *p 89 | vla_resize(p, n); // Resize handle to n 90 | int i; 91 | for (i=0;i 5 | #include 6 | #include 7 | #include 8 | 9 | static void 10 | init_handle(lua_State *L, vla_handle_t handle, int n) { 11 | vla_using(tmp, int, handle, L); 12 | int i; 13 | for (i=0;ilist, NULL); 61 | int v = luaL_checkinteger(L, 2); 62 | vla_push(tmp, v, NULL); 63 | return 0; 64 | } 65 | 66 | static int 67 | lpushlua(lua_State *L) { 68 | struct ud *p = (struct ud *)lua_touserdata(L, 1); 69 | vla_using(tmp, int, p->list, L); 70 | int v = luaL_checkinteger(L, 2); 71 | vla_push(tmp, v, L); 72 | int id; 73 | if ((id = vla_luaid(tmp, L))) { 74 | // ref lua vla object 75 | lua_pushvalue(L, id); 76 | lua_setiuservalue(L, 1, 1); 77 | } 78 | return 0; 79 | } 80 | 81 | static int 82 | llen(lua_State *L) { 83 | struct ud *p = (struct ud *)lua_touserdata(L, 1); 84 | vla_using(tmp, int, p->list, NULL); 85 | int sz = vla_size(tmp); 86 | lua_pushinteger(L, sz); 87 | return 1; 88 | } 89 | 90 | static int 91 | lget(lua_State *L) { 92 | struct ud *p = (struct ud *)lua_touserdata(L, 1); 93 | vla_using(tmp, int, p->list, NULL); 94 | int sz = vla_size(tmp); 95 | int idx = luaL_checkinteger(L, 2); 96 | if (idx <= 0 || idx > sz) 97 | return luaL_error(L, "boundary %d/%d", idx, sz); 98 | lua_pushinteger(L, tmp[idx-1]); 99 | return 1; 100 | } 101 | 102 | static int 103 | lset(lua_State *L) { 104 | struct ud *p = (struct ud *)lua_touserdata(L, 1); 105 | vla_using(tmp, int, p->list, NULL); 106 | int sz = vla_size(tmp); 107 | int idx = luaL_checkinteger(L, 2); 108 | if (idx <= 0 || idx > sz) 109 | return luaL_error(L, "boundary %d/%d", idx, sz); 110 | int v = luaL_checkinteger(L, 3); 111 | tmp[idx-1] = v; 112 | return 0; 113 | } 114 | 115 | static int 116 | lrelease(lua_State *L) { 117 | struct ud *p = (struct ud *)lua_touserdata(L, 1); 118 | vla_close_handle(p->list); 119 | return 0; 120 | } 121 | 122 | static int 123 | ltestud(lua_State *L) { 124 | struct ud * p = NULL; 125 | if (lua_toboolean(L, 1)) { 126 | // Use C heap 127 | p = (struct ud *)lua_newuserdatauv(L, sizeof(*p), 0); 128 | p->list = vla_new(int, 0, NULL); 129 | if (luaL_newmetatable(L, "CUD")) { 130 | luaL_Reg l[] = { 131 | { "__call", lpushc }, 132 | { "__len", llen }, 133 | { "__index", lget }, 134 | { "__newindex", lset }, 135 | { "__gc", lrelease }, 136 | { NULL, NULL }, 137 | }; 138 | luaL_setfuncs(L, l, 0); 139 | } 140 | } else { 141 | // Use Lua memory 142 | p = (struct ud *)lua_newuserdatauv(L, sizeof(*p), 1); 143 | p->list = vla_new(int, 0, L); 144 | lua_setiuservalue(L, -2, 1); // ref lua vla object 145 | if (luaL_newmetatable(L, "LUAUD")) { 146 | luaL_Reg l[] = { 147 | { "__call", lpushlua }, 148 | { "__len", llen }, 149 | { "__index", lget }, 150 | { "__newindex", lset }, 151 | { NULL, NULL }, 152 | }; 153 | luaL_setfuncs(L, l, 0); 154 | } 155 | } 156 | lua_setmetatable(L, -2); 157 | return 1; 158 | } 159 | 160 | LUAMOD_API int 161 | luaopen_vlatest(lua_State *L) { 162 | luaL_checkversion(L); 163 | luaL_Reg l[] = { 164 | { "testlua", ltestlua }, 165 | { "testheap", ltestheap }, 166 | { "testud", ltestud }, 167 | { NULL, NULL }, 168 | }; 169 | luaL_newlib(L, l); 170 | 171 | return 1; 172 | } 173 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | local test = require "vlatest" 2 | 3 | local t = test.testlua() 4 | for k,v in pairs(t) do 5 | print(k,v) 6 | end 7 | 8 | test.testheap() 9 | 10 | 11 | local function foo(ud) 12 | print (ud) 13 | for i = 1, 100 do 14 | ud(i) 15 | end 16 | for i = 1, #ud do 17 | print(ud[i]) 18 | end 19 | print "OK" 20 | end 21 | 22 | local ud = test.testud() 23 | 24 | foo(ud) 25 | 26 | local ud = test.testud(true) -- C VLA 27 | 28 | foo(ud) -------------------------------------------------------------------------------- /vla.c: -------------------------------------------------------------------------------- 1 | #include "vla.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define VLA_HEAP_DEFAULT 64 9 | 10 | struct vla_heap { 11 | VLA_COMMON_HEADER 12 | unsigned char *buffer; 13 | }; 14 | 15 | struct vla_lua { 16 | VLA_COMMON_HEADER 17 | unsigned char buffer[1]; 18 | }; 19 | 20 | static void 21 | vla_close_heap(struct vla_heap *h) { 22 | free(h->buffer); 23 | free(h); 24 | } 25 | 26 | void 27 | vla_handle_close_(vla_handle_t h) { 28 | if (h.h == NULL || !(h.h->type & VLA_TYPE_NEEDCLOSE)) 29 | return; 30 | switch (h.h->type & VLA_TYPE_MASK) { 31 | case VLA_TYPE_STACK: 32 | vla_handle_close_(h.s->extra); 33 | break; 34 | case VLA_TYPE_HEAP: 35 | vla_close_heap(h.m); 36 | break; 37 | default: 38 | assert(0); 39 | break; 40 | } 41 | } 42 | 43 | static struct vla_heap * 44 | vla_resize_heap(struct vla_heap *h, int n, int esize) { 45 | if (h->buffer == NULL) { 46 | h->cap = n < VLA_HEAP_DEFAULT ? VLA_HEAP_DEFAULT : n; 47 | h->buffer = (unsigned char *)malloc(h->cap * esize); 48 | if (h->buffer == NULL) { 49 | free(h); 50 | return NULL; 51 | } 52 | } else { 53 | int newcap = h->cap; 54 | do newcap = newcap * 3 / 2; while (newcap < n); 55 | unsigned char * buf = (unsigned char *)realloc(h->buffer, newcap * esize); 56 | if (buf == NULL) { 57 | free(h->buffer); 58 | free(h); 59 | return NULL; 60 | } 61 | h->buffer = buf; 62 | h->cap = newcap; 63 | } 64 | h->n = n; 65 | return h; 66 | } 67 | 68 | int 69 | vla_init_lua_(void *L_) { 70 | lua_State *L = (lua_State *)L_; 71 | lua_pushnil(L); 72 | return lua_gettop(L); 73 | } 74 | 75 | static inline void 76 | vla_map_heap(struct vla_heap *h, void **p) { 77 | *p = (void *)h->buffer; 78 | } 79 | 80 | static inline void 81 | vla_map_stack(struct vla_stack *s, void **p) { 82 | if (s->extra.h == NULL) 83 | *p = (void *)s->buffer; 84 | else 85 | vla_handle_map_(s->extra, p); 86 | } 87 | 88 | static inline void 89 | vla_map_lua(struct vla_lua *l, void **p) { 90 | *p = (void *)l->buffer; 91 | } 92 | 93 | void 94 | vla_handle_map_(vla_handle_t h, void **p) { 95 | switch (h.h->type & VLA_TYPE_MASK) { 96 | case VLA_TYPE_STACK: 97 | vla_map_stack(h.s, p); 98 | break; 99 | case VLA_TYPE_HEAP: 100 | vla_map_heap(h.m, p); 101 | break; 102 | case VLA_TYPE_LUA: 103 | vla_map_lua(h.l, p); 104 | break; 105 | default: 106 | assert(0); 107 | break; 108 | } 109 | } 110 | 111 | vla_handle_t 112 | vla_heap_new(int n, int esize) { 113 | static const vla_handle_t invalid; 114 | struct vla_heap * h = (struct vla_heap *)malloc(sizeof(*h)); 115 | if (h == NULL) 116 | return invalid; 117 | h->n = n; 118 | h->cap = n; 119 | h->type = VLA_TYPE_HEAP | VLA_TYPE_NEEDCLOSE; 120 | if (n > 0) { 121 | h->buffer = (unsigned char *)malloc(n * esize); 122 | if (h->buffer == NULL) { 123 | free(h); 124 | return invalid; 125 | } 126 | } else 127 | h->buffer = NULL; 128 | vla_handle_t ret; 129 | ret.m = h; 130 | return ret; 131 | } 132 | 133 | vla_handle_t 134 | vla_lua_new(void *L_, int n, int esize) { 135 | lua_State *L = (lua_State *)L_; 136 | int cap = n < VLA_HEAP_DEFAULT ? VLA_HEAP_DEFAULT : n; 137 | int sz = cap * esize + offsetof(struct vla_lua, buffer); 138 | struct vla_lua * l = (struct vla_lua *)lua_newuserdatauv(L, sz, 0); 139 | l->n = n; 140 | l->cap = cap; 141 | l->type = VLA_TYPE_LUA; 142 | vla_handle_t ret; 143 | ret.l = l; 144 | return ret; 145 | } 146 | 147 | static struct vla_stack * 148 | vla_resize_stack(void *L_, struct vla_stack *s, int n, int esize, int *lua_id) { 149 | if (s->extra.h == NULL) { 150 | if (L_ == NULL) { 151 | s->type |= VLA_TYPE_NEEDCLOSE; 152 | s->extra = vla_heap_new(n, esize); 153 | if (s->extra.h == NULL) { 154 | return NULL; 155 | } 156 | } else { 157 | lua_State *L = (lua_State *)L_; 158 | s->extra = vla_lua_new(L, n, esize); 159 | if (*lua_id == 0) { 160 | *lua_id = lua_gettop(L); 161 | } else { 162 | lua_replace(L, *lua_id); 163 | } 164 | } 165 | void *addr; 166 | vla_handle_map_(s->extra, &addr); 167 | int sz = VLA_STACK_SIZE; 168 | if (sz > n * esize) 169 | sz = n * esize; 170 | memcpy(addr, s->buffer, sz); 171 | } else { 172 | vla_handle_resize_(L_, &s->extra, n, esize, lua_id); 173 | if (s->extra.h == NULL) 174 | return NULL; 175 | } 176 | s->n = s->extra.h->n; 177 | s->cap = s->extra.h->cap; 178 | return s; 179 | } 180 | 181 | static struct vla_lua * 182 | vla_resize_lua(lua_State *L, struct vla_lua *l, int n, int esize, int *lua_id) { 183 | int newcap = l->cap; 184 | do newcap = newcap * 3 / 2; while (newcap < n); 185 | vla_handle_t ret = vla_lua_new(L, newcap, esize); 186 | memcpy(ret.l->buffer, l->buffer, l->n * esize); 187 | ret.l->n = n; 188 | if (*lua_id == 0) { 189 | *lua_id = lua_gettop(L); 190 | } else { 191 | lua_replace(L, *lua_id); 192 | } 193 | return ret.l; 194 | } 195 | 196 | void 197 | vla_handle_resize_(void *L, vla_handle_t *h, int n, int esize, int *lua_id) { 198 | switch (h->h->type & VLA_TYPE_MASK) { 199 | case VLA_TYPE_STACK: 200 | h->s = vla_resize_stack(L, h->s, n, esize, lua_id); 201 | break; 202 | case VLA_TYPE_HEAP: 203 | h->m = vla_resize_heap(h->m, n, esize); 204 | break; 205 | case VLA_TYPE_LUA: 206 | h->l = vla_resize_lua((lua_State *)L, h->l, n, esize, lua_id); 207 | break; 208 | default: 209 | assert(0); 210 | break; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /vla.h: -------------------------------------------------------------------------------- 1 | #ifndef VARIANT_LENGTH_ARRAY_H 2 | #define VARIANT_LENGTH_ARRAY_H 3 | 4 | #include 5 | 6 | #define VLA_TYPE_STACK 0 7 | #define VLA_TYPE_HEAP 1 8 | #define VLA_TYPE_LUA 2 9 | #define VLA_TYPE_MASK 0xf 10 | #define VLA_TYPE_NEEDCLOSE 0x10 11 | #define VLA_COMMON_HEADER int type; int n; int cap; 12 | 13 | #define VLA_STACK_SIZE ((int)(128 * sizeof(void *))) 14 | 15 | struct vla_header { VLA_COMMON_HEADER }; 16 | struct vla_stack; 17 | struct vla_heap; 18 | struct vla_lua; 19 | 20 | union vla_handle { 21 | struct vla_header *h; 22 | struct vla_stack *s; 23 | struct vla_heap *m; 24 | struct vla_lua *l; 25 | }; 26 | 27 | typedef union vla_handle vla_handle_t; 28 | 29 | struct vla_stack { 30 | VLA_COMMON_HEADER 31 | vla_handle_t extra; 32 | unsigned char buffer[VLA_STACK_SIZE]; 33 | }; 34 | 35 | static inline vla_handle_t 36 | vla_stack_new_(struct vla_stack *s, int esize) { 37 | s->type = VLA_TYPE_STACK; 38 | s->n = 0; 39 | s->cap = (VLA_STACK_SIZE + esize - 1) / esize; 40 | s->extra.h = NULL; 41 | vla_handle_t ret; 42 | ret.s = s; 43 | return ret; 44 | }; 45 | 46 | #define vla_stack_handle(name, type) \ 47 | struct vla_stack name##_stack_; \ 48 | vla_handle_t name = vla_stack_new_(&name##_stack_, sizeof(type)) 49 | 50 | vla_handle_t vla_heap_new(int n, int esize); 51 | vla_handle_t vla_lua_new(void *L, int n, int esize); 52 | 53 | #define vla_new(type, n, L) (L == NULL ? vla_heap_new(n, sizeof(type)) : vla_lua_new(L, n, sizeof(type))) 54 | 55 | void vla_handle_map_(vla_handle_t h, void **p); 56 | 57 | static inline void 58 | vla_using_(vla_handle_t h, void **p) { 59 | if ((h.h->type & VLA_TYPE_MASK) == VLA_TYPE_STACK && h.s->extra.h == NULL) 60 | *p = (void *)h.s->buffer; 61 | else 62 | vla_handle_map_(h, p); 63 | 64 | } 65 | 66 | int vla_init_lua_(void *L); 67 | 68 | #define vla_using(name, type, h, L) \ 69 | type * name; \ 70 | vla_handle_t * name##_ref_ = &h; (void) name##_ref_; \ 71 | int name##_lua_ = 0; (void) name##_lua_; \ 72 | if (L) { name##_lua_ = vla_init_lua_(L); } \ 73 | vla_using_(h, (void **)&name) 74 | 75 | #define vla_sync(name) vla_handle_map_( *name##_ref_, (void **)&name) 76 | 77 | void vla_handle_close_(vla_handle_t h); 78 | 79 | static inline void 80 | vla_close_handle(vla_handle_t h) { 81 | if (h.h && (h.h->type & VLA_TYPE_NEEDCLOSE)) { 82 | vla_handle_close_(h); 83 | } 84 | } 85 | 86 | #define vla_close(name) vla_close_handle(*name##_ref_) 87 | 88 | void vla_handle_resize_(void *L, vla_handle_t *h, int n, int esize, int *lua_id); 89 | 90 | static inline void 91 | vla_resize_(void *L, void **p, vla_handle_t *h, int n, int esize, int *lua_id) { 92 | if (n <= h->h->cap) 93 | h->h->n = n; 94 | else { 95 | vla_handle_resize_(L, h, n, esize, lua_id); 96 | vla_handle_map_(*h, p); 97 | } 98 | } 99 | 100 | #define vla_resize(name, n, L) vla_resize_(L, (void **)&name, name##_ref_, n, sizeof(*name), &name##_lua_) 101 | 102 | #define vla_size(name) (name##_ref_->h->n) 103 | 104 | #define vla_push(name, v, L) vla_resize(name, vla_size(name) + 1, L); name[vla_size(name)-1] = v 105 | 106 | #define vla_luaid(name, L) (lua_isnoneornil(L, name##_lua_) ? 0 : name##_lua_) 107 | 108 | #endif 109 | --------------------------------------------------------------------------------