From 8f13607cba9d6cf4fc4a213ba5ae4bcd46f7e18d Mon Sep 17 00:00:00 2001 From: Determinant Date: Tue, 23 Jun 2015 13:32:42 +0800 Subject: separate non-Lua part of io code to a dedicated dir; code clean-up --- nerv/Makefile | 8 +- nerv/common.h | 2 +- nerv/io/chunk_file.c | 345 +++---------- nerv/io/chunk_file.h | 19 +- nerv/io/init.lua | 8 +- nerv/lib/io/chunk_file.c | 239 +++++++++ nerv/lib/io/chunk_file.h | 43 ++ nerv/lib/luaT/README.md | 239 +++++++++ nerv/lib/luaT/luaT.c | 1079 +++++++++++++++++++++++++++++++++++++++++ nerv/lib/luaT/luaT.h | 111 +++++ nerv/luaT/README.md | 239 --------- nerv/luaT/luaT.c | 1079 ----------------------------------------- nerv/luaT/luaT.h | 111 ----- nerv/matrix/generic/mmatrix.c | 10 +- nerv/nerv-scm-1.rockspec | 10 +- 15 files changed, 1814 insertions(+), 1728 deletions(-) create mode 100644 nerv/lib/io/chunk_file.c create mode 100644 nerv/lib/io/chunk_file.h create mode 100644 nerv/lib/luaT/README.md create mode 100644 nerv/lib/luaT/luaT.c create mode 100644 nerv/lib/luaT/luaT.h delete mode 100644 nerv/luaT/README.md delete mode 100644 nerv/luaT/luaT.c delete mode 100644 nerv/luaT/luaT.h diff --git a/nerv/Makefile b/nerv/Makefile index b69a63e..4008453 100644 --- a/nerv/Makefile +++ b/nerv/Makefile @@ -1,7 +1,7 @@ .PHONY: build install clean SHELL := /bin/bash BUILD_DIR := $(CURDIR)/build -OBJS := nerv.o luaT.o common.o \ +OBJS := nerv.o lib/luaT/luaT.o lib/io/chunk_file.o common.o \ matrix/mmatrix.o matrix/cumatrix.o matrix/init.o matrix/cukernel.o \ io/init.o io/chunk_file.o \ examples/oop_example.o @@ -19,7 +19,7 @@ INCLUDE += $(CUDA_INCLUDE) LDFLAGS := -L$(CUDA_BASE)/lib64/ -Wl,-rpath=$(CUDA_BASE)/lib64/ -lcudart -lcublas CFLAGS := -Wall -Wextra -O2 OBJ_DIR := $(BUILD_DIR)/objs -SUBDIR := matrix io layer examples nn +SUBDIR := matrix io layer examples nn lib/io lib/luaT NVCC := $(CUDA_BASE)/bin/nvcc NVCC_FLAGS := -Xcompiler -fPIC,-Wall,-Wextra @@ -39,8 +39,8 @@ $(OBJ_DIR)/matrix/cukernel.o: matrix/cukernel.cu $(NVCC) -c -o $@ $< $(INCLUDE) $(NVCC_FLAGS) $(LUA_DIR)/%.lua: %.lua cp $< $@ -$(OBJ_DIR)/luaT.o: - gcc -c -o $@ luaT/luaT.c $(INCLUDE) -fPIC +#$(OBJ_DIR)/luaT.o: +# gcc -c -o $@ luaT/luaT.c $(INCLUDE) -fPIC $(LIBS): $(OBJS) gcc -shared -o $@ $(OBJS) $(LDFLAGS) diff --git a/nerv/common.h b/nerv/common.h index e21c7a5..6657dc4 100644 --- a/nerv/common.h +++ b/nerv/common.h @@ -3,7 +3,7 @@ #include "lua.h" #include "lauxlib.h" #include "lualib.h" -#include "luaT/luaT.h" +#include "lib/luaT/luaT.h" #include #include diff --git a/nerv/io/chunk_file.c b/nerv/io/chunk_file.c index c0b6b9f..e275d9b 100644 --- a/nerv/io/chunk_file.c +++ b/nerv/io/chunk_file.c @@ -5,321 +5,136 @@ #include "chunk_file.h" #define INVALID_FORMAT_ERROR(fn) \ - nerv_error(L, "Invalid chunk file: %s", fn) + nerv_error(L, "invalid chunk file: %s", fn) #define CHECK_FORMAT(exp, ret, fname) \ do { \ if ((exp) != (ret)) INVALID_FORMAT_ERROR(fn); \ } while (0) -#define CHECK_FILE_OPEN(pfh) \ - do { \ - if ((pfh)->closed) \ - nerv_error(L, "operations on a closed file"); \ - } while (0) const char *nerv_chunk_file_tname = "nerv.ChunkFile"; const char *nerv_chunk_file_handle_tname = "nerv.ChunkFileHandle"; const char *nerv_chunk_info_tname = "nerv.ChunkInfo"; const char *nerv_chunk_data_tname = "nerv.ChunkData"; -#define PARAM_HEADER_SIZE 16 - -enum { - NORMAL, - INVALID_FORMAT, - END_OF_FILE, - SECTION_OVERFLOW, - WRITE_ERROR -}; - -size_t read_chunk_header_plain(FILE *fp, int *status) { - static char buff[PARAM_HEADER_SIZE]; - int i; - size_t size = 0; - *status = NORMAL; - if (fread(buff, 1, PARAM_HEADER_SIZE, fp) != PARAM_HEADER_SIZE) - { - if (feof(fp)) *status = END_OF_FILE; - else *status = INVALID_FORMAT; - } - for (i = 0; i < PARAM_HEADER_SIZE; i++) - if (isdigit(buff[i])) - size = size * 10 + buff[i] - '0'; -/* fprintf(stderr, "header: %lu\n", size); */ - return size; -} - -#define CHECK_WRITE(status) \ - do { \ - if (status == SECTION_OVERFLOW) \ - nerv_error(L, "section overflowed"); \ - else if (status == WRITE_ERROR) \ - nerv_error(L, "error while writing"); \ - } while (0) - -void write_chunk_header_plain(FILE *fp, size_t size, int *status) { - static char buff[PARAM_HEADER_SIZE]; - int i; - *status = NORMAL; - for (i = PARAM_HEADER_SIZE - 3; i > 0; i--, size /= 10) - buff[i] = size % 10 + '0'; - if (size) - { - *status = SECTION_OVERFLOW; - return; - } - buff[0] = '['; - buff[PARAM_HEADER_SIZE - 2] = ']'; - buff[PARAM_HEADER_SIZE - 1] = '\n'; - if (fwrite(buff, 1, PARAM_HEADER_SIZE, fp) != PARAM_HEADER_SIZE) - { - *status = WRITE_ERROR; - return; - } -} - -ChunkData *get_chunk_data(FILE *fp, ChunkInfo *info) { - ChunkData *pcd = (ChunkData *)malloc(sizeof(ChunkData)); - pcd->data = (char *)malloc(info->length); - pcd->fp = fmemopen(pcd->data, info->length, "r"); - assert(fseeko(fp, info->offset, SEEK_SET) == 0); - if (fread(pcd->data, 1, info->length, fp) != (size_t)info->length) - return NULL; - return pcd; -} - -const char *read_chunk_metadata(lua_State *L, FILE *fp, const char *fn) { -#define LINEBUFF_SIZE 1024 - static char buff[7 + LINEBUFF_SIZE] = "return "; - CHECK_FORMAT(fgets(buff + 7, LINEBUFF_SIZE, fp), buff + 7, fn); - /* fprintf(stderr, "metadata: %s\n", buff); */ - return buff; -} - -void write_chunk_metadata(FILE *fp, const char *metadata_str, int *status) { - size_t size = strlen(metadata_str); - *status = NORMAL; - if (fwrite(metadata_str, 1, size, fp) != size || - fprintf(fp, "\n") < 0) +int nerv_lua_chunk_file_new(lua_State *L) { + int status; + const char *fn = luaL_checkstring(L, 1); + ChunkFile *cfp = nerv_chunk_file_create(fn, + luaL_checkstring(L, 2), + &status); + if (status != CF_NORMAL) { - *status = WRITE_ERROR; - return; + nerv_error(L, "%s: %s", fn, nerv_chunk_file_errstr(status)); } - /* fprintf(stderr, "metadata: %s\n", metadata_str); */ -} - - -int nerv_chunk_file_open_write(lua_State *L, const char *fn) { - FILE *fp = fopen(fn, "w"); - ChunkFileHandle *lfp; - if (!fp) nerv_error(L, "Error while opening chunk file: %s", fn); - lfp = (ChunkFileHandle *)malloc(sizeof(ChunkFileHandle)); - lfp->fp = fp; - lfp->closed = 0; - luaT_pushudata(L, lfp, nerv_chunk_file_handle_tname); - lua_setfield(L, -2, "handle"); - luaT_pushmetatable(L, nerv_chunk_file_tname); - lua_setmetatable(L, -2); - return 1; -} - -int nerv_chunk_file_open_read(lua_State *L, const char *fn) { - FILE *fp = fopen(fn, "r"); - int i, status; - size_t chunk_len; - off_t offset; - ChunkFileHandle *lfp; - - if (!fp) nerv_error(L, "Error while opening chunk file: %s", fn); - offset = ftello(fp); lua_newtable(L); - /* fprintf(stderr, "%d\n", (int)offset); */ - for (i = 0;; offset += chunk_len, i++) + luaT_pushudata(L, cfp, nerv_chunk_file_handle_tname); + lua_setfield(L, -2, "handle"); + if (cfp->status == CF_READ) { - ChunkInfo *pci; - /* fprintf(stderr, "reading chunk %d from %d\n", i, (int)offset); */ - /* skip to the begining of chunk i */ - CHECK_FORMAT(fseeko(fp, offset, SEEK_SET), 0, fn); - /* read header */ - chunk_len = read_chunk_header_plain(fp, &status); - if (status == END_OF_FILE) break; - else if (status == INVALID_FORMAT) - INVALID_FORMAT_ERROR(fn); - /* read metadata */ - luaL_loadstring(L, read_chunk_metadata(L, fp, fn)); - CHECK_FORMAT(lua_pcall(L, 0, 1, 0), 0, fn); - CHECK_FORMAT(lua_istable(L, -1), 1, fn); - /* stack: obj_table, metadata */ - /* chunk info */ - pci = (ChunkInfo *)malloc(sizeof(ChunkInfo)); - pci->offset = ftello(fp); - pci->length = chunk_len - (pci->offset - offset); - /* fprintf(stderr, "%d + %d (skip %lu)\n", (int)pci->offset, - (int)pci->length, chunk_len); */ - luaT_pushudata(L, pci, nerv_chunk_info_tname); - lua_setfield(L, -2, "chunk"); - /* stack: obj_table, metadata */ - /* get id */ - lua_getfield(L, -1, "id"); - /* stack: obj_table, metadata, id */ - if (!lua_isstring(L, -1)) - nerv_error(L, "id field in metadata must be a string"); - lua_pushvalue(L, -1); - /* stack: obj_table, metadata, id, id */ - lua_gettable(L, -4); - /* stack: obj_table, metadata, id, obj[id] */ - if (!lua_isnil(L, -1)) - nerv_error(L, "conflicting id"); - lua_pop(L, 1); - /* stack: obj_table, metadata, id */ - lua_pushvalue(L, -2); - /* stack: obj_table, metadata, id, metadata */ - lua_settable(L, -4); - /* stack: obj_table, metadata */ - lua_pop(L, 1); + ChunkInfo *cip; + /* build a table with interpreted metadata in Lua because C API only + * provides with linked list with uninterpreted metadata */ + lua_newtable(L); + /* stack: self, metadata_table */ + for (cip = cfp->info; cip; cip = cip->next) + { + luaL_loadstring(L, cip->metadata); + CHECK_FORMAT(lua_pcall(L, 0, 1, 0), 0, fn); + CHECK_FORMAT(lua_istable(L, -1), 1, fn); + lua_getfield(L, -1, "id"); + if (!lua_isstring(L, -1)) + nerv_error(L, "id field in metadata must be a string"); + /* stack: ... metadata_table, metadata, id */ + lua_pushvalue(L, -1); + /* stack: ... metadata_table, metadata, id, id */ + lua_gettable(L, -4); + /* stack: ... metadata_table, metadata, id, metadata_table[id] */ + if (!lua_isnil(L, -1)) + nerv_error(L, "conflicting id"); + lua_pop(L, 1); + /* stack: ... metadata_table, metadata, id */ + lua_pushvalue(L, -2); + /* stack: ... metadata_table, metadata, id, metadata */ + lua_settable(L, -4); + /* stack: ... metadata_table, metadata */ + luaT_pushudata(L, cip, nerv_chunk_info_tname); + /* stack: ... metadata_table, cip */ + lua_setfield(L, -2, "_chunk_info"); + /* stack: ... metadata_table */ + lua_pop(L, 1); + /* stack: ... metadata_table */ + } + lua_setfield(L, -2, "metadata"); + /* stack: ... */ } - lua_setfield(L, -2, "metadata"); - lfp = (ChunkFileHandle *)malloc(sizeof(ChunkFileHandle)); - lfp->fp = fp; - lfp->closed = 0; - luaT_pushudata(L, lfp, nerv_chunk_file_handle_tname); - lua_setfield(L, -2, "handle"); luaT_pushmetatable(L, nerv_chunk_file_tname); lua_setmetatable(L, -2); return 1; } -int nerv_chunk_file_new_(lua_State *L, const char *fn, const char *mode) { - int rd = 1, bin = 0; - size_t i, len = strlen(mode); - for (i = 0; i < len; i++) - switch (mode[i]) - { - case 'r': rd = 1; break; - case 'w': rd = 0; break; - case 'b': bin = 1; break; - } - return rd ? nerv_chunk_file_open_read(L, fn) : \ - nerv_chunk_file_open_write(L, fn); +static void writer(void *L) { + lua_call((lua_State *)L, 2, 0); /* let the write() to write */ } -int nerv_chunk_file___init(lua_State *L) { - lua_pushvalue(L, 1); - return nerv_chunk_file_new_(L, luaL_checkstring(L, 2), - luaL_checkstring(L, 3)); -} - -int nerv_chunk_file_new(lua_State *L) { - lua_newtable(L); - return nerv_chunk_file_new_(L, luaL_checkstring(L, 1), - luaL_checkstring(L, 2)); -} - -int nerv_chunk_file_write_chunkdata(lua_State *L) { - ChunkFileHandle *pfh; +int nerv_lua_chunk_file_write_chunkdata(lua_State *L) { int status; - off_t start; - size_t size; - const char *metadata_str = lua_tolstring(L, 2, NULL); - lua_getfield(L, 1, "handle"); - pfh = luaT_checkudata(L, -1, nerv_chunk_file_handle_tname); - CHECK_FILE_OPEN(pfh); - start = ftello(pfh->fp); - write_chunk_header_plain(pfh->fp, 0, &status); /* fill zeros */ - CHECK_WRITE(status); - write_chunk_metadata(pfh->fp, metadata_str, &status); - CHECK_WRITE(status); - lua_pushvalue(L, 3); - lua_getfield(L, -1, "write"); + ChunkFile *cfp = luaT_checkudata(L, 1, nerv_chunk_file_handle_tname); + const char *mdstr = lua_tolstring(L, 2, NULL); + lua_getfield(L, 3, "write"); if (!lua_isfunction(L, -1)) nerv_error(L, "\"write\" method must be implemented"); - lua_pushvalue(L, -2); - lua_pushvalue(L, 4); /* pass handle as parameter to write() */ - lua_call(L, 2, 0); /* let the write() to write */ - lua_pop(L, 1); - size = ftello(pfh->fp) - start; - fseeko(pfh->fp, start, SEEK_SET); - /* write the calced size */ - write_chunk_header_plain(pfh->fp, size, &status); - CHECK_WRITE(status); - fseeko(pfh->fp, 0, SEEK_END); + lua_pushvalue(L, 3); /* lua writer itself */ + lua_pushvalue(L, 1); /* pass handle as parameter to write() */ + nerv_chunk_file_write_chunkdata(cfp, mdstr, writer, (void *)L); return 0; } -int nerv_chunk_file_get_chunkdata(lua_State *L) { - ChunkFileHandle *pfh; - ChunkInfo *pci; - ChunkData *pcd; - const char *id = luaL_checkstring(L, 2); - - lua_getfield(L, 1, "handle"); - pfh = luaT_checkudata(L, -1, nerv_chunk_file_handle_tname); - CHECK_FILE_OPEN(pfh); - lua_pop(L, 1); /* pop handle */ - lua_getfield(L, 1, "metadata"); - /* now stack: self, k, metadata */ - lua_getfield(L, -1, id); - /* now stack: self, k, metadata, kth{} */ - if (lua_isnil(L, -1)) /* no chunck with the id */ - return 0; - lua_getfield(L, -1, "chunk"); - pci = luaT_checkudata(L, -1, nerv_chunk_info_tname); - if (!(pcd = get_chunk_data(pfh->fp, pci))) - nerv_error(L, "unexpected end of file"); - luaT_pushudata(L, pcd, nerv_chunk_data_tname); +int nerv_lua_chunk_file_get_chunkdata(lua_State *L) { + int status; + ChunkFile *cfp = luaT_checkudata(L, 1, nerv_chunk_file_handle_tname); + ChunkInfo *cip = luaT_checkudata(L, 2, nerv_chunk_info_tname); + ChunkData *cdp = nerv_chunk_file_get_chunkdata(cfp, cip, &status); + if (status != CF_NORMAL) + nerv_error(L, "%s", nerv_chunk_file_errstr(status)); + luaT_pushudata(L, cdp, nerv_chunk_data_tname); return 1; } -int nerv_chunk_file_close(lua_State *L) { - ChunkFileHandle *pfh; - lua_getfield(L, 1, "handle"); - pfh = luaT_checkudata(L, -1, nerv_chunk_file_handle_tname); - CHECK_FILE_OPEN(pfh); - fclose(pfh->fp); - pfh->closed = 1; - return 0; -} - -int nerv_chunk_file_handle_destroy(lua_State *L) { - ChunkFileHandle *pfh = luaT_checkudata(L, 1, - nerv_chunk_file_handle_tname); - if (!pfh->closed) fclose(pfh->fp); - free(pfh); +int nerv_lua_chunk_file_close(lua_State *L) { + ChunkFile *cfp = luaT_checkudata(L, -1, nerv_chunk_file_handle_tname); + nerv_chunk_file_close(cfp); return 0; } -static int nerv_chunk_info_destroy(lua_State *L) { - ChunkInfo *pci = luaT_checkudata(L, 1, nerv_chunk_info_tname); - free(pci); +int nerv_lua_chunk_file_destroy(lua_State *L) { + ChunkFile *cfp = luaT_checkudata(L, -1, nerv_chunk_file_handle_tname); + nerv_chunk_file_destroy(cfp); return 0; } -static int nerv_chunk_data_destroy(lua_State *L) { - ChunkData *pcd = luaT_checkudata(L, 1, nerv_chunk_data_tname); - fclose(pcd->fp); - free(pcd->data); - free(pcd); +static int nerv_lua_chunk_data_destroy(lua_State *L) { + ChunkData *cdp = luaT_checkudata(L, 1, nerv_chunk_data_tname); + nerv_chunk_data_destroy(cdp); return 0; } static const luaL_Reg nerv_chunk_file_methods[] = { - {"get_chunkdata", nerv_chunk_file_get_chunkdata}, - {"_write_chunkdata", nerv_chunk_file_write_chunkdata}, - {"close", nerv_chunk_file_close}, - {"__init", nerv_chunk_file___init}, + {"_get_chunkdata", nerv_lua_chunk_file_get_chunkdata}, + {"_write_chunkdata", nerv_lua_chunk_file_write_chunkdata}, + {"_close", nerv_lua_chunk_file_close}, {NULL, NULL} }; void nerv_chunk_file_init(lua_State *L) { luaT_newmetatable(L, nerv_chunk_file_tname, NULL, - nerv_chunk_file_new, + nerv_lua_chunk_file_new, NULL, NULL); luaL_register(L, NULL, nerv_chunk_file_methods); lua_pop(L, 1); luaT_newmetatable(L, nerv_chunk_file_handle_tname, NULL, - NULL, nerv_chunk_file_handle_destroy, NULL); + NULL, nerv_lua_chunk_file_destroy, NULL); luaT_newmetatable(L, nerv_chunk_info_tname, NULL, - NULL, nerv_chunk_info_destroy, NULL); + NULL, NULL, NULL); luaT_newmetatable(L, nerv_chunk_data_tname, NULL, - NULL, nerv_chunk_data_destroy, NULL); + NULL, nerv_lua_chunk_data_destroy, NULL); } - diff --git a/nerv/io/chunk_file.h b/nerv/io/chunk_file.h index 9bae59d..71094a5 100644 --- a/nerv/io/chunk_file.h +++ b/nerv/io/chunk_file.h @@ -1,23 +1,10 @@ -#ifndef NERV_LAYER_FILE_H -#define NERV_LAYER_FILE_H +#ifndef NERV_LUA_CHUNK_FILE_H +#define NERV_LUA_CHUNK_FILE_H +#include "../lib/io/chunk_file.h" extern const char *nerv_chunk_file_tname; extern const char *nerv_chunk_file_handle_tname; extern const char *nerv_chunk_info_tname; extern const char *nerv_chunk_data_tname; -typedef struct ChunkFileHandle { - FILE *fp; - int closed; -} ChunkFileHandle; - -typedef struct ChunkInfo { - off_t offset, length; -} ChunkInfo; - -typedef struct ChunkData { - FILE *fp; - char *data; -} ChunkData; - #endif diff --git a/nerv/io/init.lua b/nerv/io/init.lua index 647ff93..eb2e3e5 100644 --- a/nerv/io/init.lua +++ b/nerv/io/init.lua @@ -3,7 +3,7 @@ function nerv.ChunkFile:write_chunkdata(metadata, writer) nerv.error("metadata should be a Lua table") return end - return self:_write_chunkdata(table.tostring(metadata), writer) + return self._write_chunkdata(self.handle, table.tostring(metadata), writer) end function nerv.ChunkFile:write_chunk(chunk) @@ -28,10 +28,14 @@ function nerv.ChunkFile:read_chunk(id, global_conf) local chunk_type = nerv.get_type(metadata.type) local chunk = chunk_type(id, global_conf) chunk:set_info(metadata.info) - chunk:read(self:get_chunkdata(id)) + chunk:read(self._get_chunkdata(self.handle, metadata._chunk_info)) return chunk end +function nerv.ChunkFile:close() + self._close(self.handle) +end + local DataReader = nerv.class("nerv.DataReader") function DataReader:__init(global_conf, reader_conf) diff --git a/nerv/lib/io/chunk_file.c b/nerv/lib/io/chunk_file.c new file mode 100644 index 0000000..a305962 --- /dev/null +++ b/nerv/lib/io/chunk_file.c @@ -0,0 +1,239 @@ +#include "../../common.h" +#include "chunk_file.h" +#include +#include +#define PARAM_HEADER_SIZE 16 + +static size_t read_chunk_header_plain(FILE *fp, int *status) { + static char buff[PARAM_HEADER_SIZE]; + int i; + size_t size = 0; + if (fread(buff, 1, PARAM_HEADER_SIZE, fp) != PARAM_HEADER_SIZE) + { + if (feof(fp)) *status = CF_END_OF_FILE; + else + { + *status = CF_INVALID_FORMAT; + return 0; + } + } + else *status = CF_NORMAL; + for (i = 0; i < PARAM_HEADER_SIZE; i++) + if (isdigit(buff[i])) + size = size * 10 + buff[i] - '0'; +/* fprintf(stderr, "header: %lu\n", size); */ + return size; +} + +static void write_chunk_header_plain(FILE *fp, size_t size, int *status) { + static char buff[PARAM_HEADER_SIZE]; + int i; + for (i = PARAM_HEADER_SIZE - 3; i > 0; i--, size /= 10) + buff[i] = size % 10 + '0'; + if (size) + { + *status = CF_SECTION_OVERFLOW; + return; + } + buff[0] = '['; + buff[PARAM_HEADER_SIZE - 2] = ']'; + buff[PARAM_HEADER_SIZE - 1] = '\n'; + if (fwrite(buff, 1, PARAM_HEADER_SIZE, fp) != PARAM_HEADER_SIZE) + { + *status = CF_WRITE_ERROR; + return; + } + *status = CF_NORMAL; +} + +static ChunkData *get_chunk_data(FILE *fp, ChunkInfo *info) { + ChunkData *cdp = (ChunkData *)malloc(sizeof(ChunkData)); + cdp->data = (char *)malloc(info->length); + cdp->fp = fmemopen(cdp->data, info->length, "r"); + assert(fseeko(fp, info->offset, SEEK_SET) == 0); + if (fread(cdp->data, 1, info->length, fp) != (size_t)info->length) + return NULL; + return cdp; +} + +static const char *read_chunk_metadata(FILE *fp, const char *fn, int *status) { +#define LINEBUFF_SIZE 1024 +#define LUA_RETURN "return " +#define LUA_RETURN_LEN (sizeof(LUA_RETURN) - 1) + static char buff[LUA_RETURN_LEN + LINEBUFF_SIZE] = LUA_RETURN; + *status = fgets(buff + LUA_RETURN_LEN, + LINEBUFF_SIZE, fp) == (buff + LUA_RETURN_LEN) ? \ + CF_NORMAL : CF_INVALID_FORMAT; + fprintf(stderr, "metadata: %s\n", buff); + return buff; +} + +static void write_chunk_metadata(FILE *fp, const char *mdstr, int *status) { + size_t size = strlen(mdstr); + if (fwrite(mdstr, 1, size, fp) != size || + fprintf(fp, "\n") < 0) + { + *status = CF_WRITE_ERROR; + return; + } + /* fprintf(stderr, "metadata: %s\n", metadata_str); */ + *status = CF_NORMAL; +} + +static ChunkFile *open_write(const char *fn, int *status) { + ChunkFile *cfp; + FILE *fp = fopen(fn, "w"); + + if (!fp) + { + *status = CF_ERR_OPEN_FILE; + return NULL; + } + cfp = (ChunkFile *)malloc(sizeof(ChunkFile)); + cfp->fp = fp; + cfp->status = CF_WRITE; + *status = CF_NORMAL; + return cfp; +} + +static ChunkFile *open_read(const char *fn, int *status) { + size_t chunk_len; + off_t offset; + int i; + const char *mdstr; + ChunkFile *cfp; + ChunkInfo *head = NULL; + FILE *fp = fopen(fn, "r"); + + if (!fp) + { + *status = CF_ERR_OPEN_FILE; + return NULL; + } + cfp = (ChunkFile *)malloc(sizeof(ChunkFile)); + cfp->fp = fp; + cfp->status = CF_READ; + offset = ftello(fp); + /* fprintf(stderr, "%d\n", (int)offset); */ + for (i = 0;; offset += chunk_len, i++) + { + ChunkInfo *cip; + fprintf(stderr, "reading chunk %d from %d\n", i, (int)offset); + /* skip to the begining of chunk i */ + if (fseeko(fp, offset, SEEK_SET) != 0) + { + *status = CF_INVALID_FORMAT; + return NULL; + } + /* read header */ + chunk_len = read_chunk_header_plain(fp, status); + if (*status == CF_END_OF_FILE) break; + if (*status != CF_NORMAL) + return NULL; + cip = (ChunkInfo *)malloc(sizeof(ChunkInfo)); + /* read metadata */ + mdstr = read_chunk_metadata(fp, fn, status); + if (*status != CF_NORMAL) + return NULL; + cip->metadata = strdup(mdstr); + cip->offset = ftello(fp); + cip->length = chunk_len - (cip->offset - offset); + /* fprintf(stderr, "%d + %d (skip %lu)\n", (int)cip->offset, + (int)cip->length, chunk_len); */ + cip->next = head; + head = cip; + } + *status = CF_NORMAL; + cfp->info = head; + return cfp; +} + +ChunkFile *nerv_chunk_file_create(const char *fn, const char *mode, int *status) { + int rd = 1, bin = 0; + size_t i, len = strlen(mode); + for (i = 0; i < len; i++) + switch (mode[i]) + { + case 'r': rd = 1; break; + case 'w': rd = 0; break; + case 'b': bin = 1; break; + } + return rd ? open_read(fn, status) : \ + open_write(fn, status); +} + +int nerv_chunk_file_write_chunkdata(ChunkFile *cfp, const char *mdstr, + ChunkDataWriter_t writer, void *writer_arg) { + int status; + off_t start; + size_t size; + if (cfp->status != CF_WRITE) + return CF_INVALID_OP; + start = ftello(cfp->fp); + write_chunk_header_plain(cfp->fp, 0, &status); /* fill zeros */ + if (status != CF_NORMAL) return status; + write_chunk_metadata(cfp->fp, mdstr, &status); + if (status != CF_NORMAL) return status; + writer(writer_arg); + size = ftello(cfp->fp) - start; + fseeko(cfp->fp, start, SEEK_SET); + /* write the calced size */ + write_chunk_header_plain(cfp->fp, size, &status); + if (status != CF_NORMAL) return status; + fseeko(cfp->fp, 0, SEEK_END); + return CF_NORMAL; +} + +ChunkData *nerv_chunk_file_get_chunkdata(ChunkFile *cfp, ChunkInfo *cip, int *status) { + ChunkData *cdp; + if (cfp->status != CF_READ) + { + *status = CF_INVALID_OP; + return NULL; + } + if (!(cdp = get_chunk_data(cfp->fp, cip))) + { + *status = CF_END_OF_FILE; + return NULL; + } + *status = CF_NORMAL; + return cdp; +} + +void nerv_chunk_file_close(ChunkFile *cfp) { + if (cfp->status != CF_CLOSED) + fclose(cfp->fp); + cfp->status = CF_CLOSED; +} + +void nerv_chunk_file_destroy(ChunkFile *cfp) { + ChunkInfo *i, *ni; + if (cfp->status != CF_CLOSED) fclose(cfp->fp); + for (i = cfp->info; i; i = ni) + { + ni = i->next; + free(i->metadata); + free(i); + } + free(cfp); +} + +void nerv_chunk_data_destroy(ChunkData *cdp) { + fclose(cdp->fp); + free(cdp->data); + free(cdp); +} + +const char *nerv_chunk_file_errstr(int status) { + switch (status) + { + case CF_INVALID_FORMAT: return "invalid format"; + case CF_END_OF_FILE: return "unexpected end of file"; + case CF_SECTION_OVERFLOW: return "section overflow"; + case CF_WRITE_ERROR: return "error while writing"; + case CF_ERR_OPEN_FILE: return "error while opening file"; + case CF_INVALID_OP: return "invalid operation"; + default: return "unknown"; + } + return NULL; +} diff --git a/nerv/lib/io/chunk_file.h b/nerv/lib/io/chunk_file.h new file mode 100644 index 0000000..71f0d03 --- /dev/null +++ b/nerv/lib/io/chunk_file.h @@ -0,0 +1,43 @@ +#ifndef NERV_CHUNK_FILE_H +#define NERV_CHUNK_FILE_H +#include "../../common.h" +enum { + CF_NORMAL, + CF_INVALID_FORMAT, + CF_END_OF_FILE, + CF_SECTION_OVERFLOW, + CF_WRITE_ERROR, + CF_ERR_OPEN_FILE, + CF_INVALID_OP, + CF_READ, + CF_WRITE, + CF_CLOSED +}; + +typedef struct ChunkInfo { + struct ChunkInfo *next; + char *metadata; + off_t offset, length; +} ChunkInfo; + +typedef struct ChunkFile { + FILE *fp; + ChunkInfo *info; + int status; +} ChunkFile; + +typedef struct ChunkData { + FILE *fp; + char *data; +} ChunkData; + +typedef void (*ChunkDataWriter_t)(void *); +ChunkFile *nerv_chunk_file_create(const char *fn, const char *mode, int *status); +int nerv_chunk_file_write_chunkdata(ChunkFile *cfp, const char *mdstr, + ChunkDataWriter_t writer, void *writer_arg); +ChunkData *nerv_chunk_file_get_chunkdata(ChunkFile *cfp, ChunkInfo *cip, int *status); +void nerv_chunk_file_close(ChunkFile *cfp); +void nerv_chunk_file_destroy(ChunkFile *cfp); +void nerv_chunk_data_destroy(ChunkData *cdp); +const char *nerv_chunk_file_errstr(int status); +#endif diff --git a/nerv/lib/luaT/README.md b/nerv/lib/luaT/README.md new file mode 100644 index 0000000..6e9cf0d --- /dev/null +++ b/nerv/lib/luaT/README.md @@ -0,0 +1,239 @@ + +# Lua Torch C API # + +luaT provides an API to interface Lua and C in Torch packages. It defines a +concept of _classes_ to Lua for Torch, and provides a mechanism to easily +handle these Lua classes from C. + +It additionally provides few functions that `luaL` should have defined, and +defines several functions similar to `luaL` ones for better type error printing when using +`luaT` classes. + + +## Memory functions ## + +Classical memory allocation functions which generate a Lua error in case of +problem. + + +### void* luaT_alloc(lua_State *L, long size) ### + +Allocates `size` bytes, and return a pointer on the allocated +memory. A Lua error will be generated if running out of memory. + + +### void* luaT_realloc(lua_State *L, void *ptr, long size) ### + +Realloc `ptr` to `size` bytes. `ptr` must have been previously +allocated with [luaT_alloc](#luaT_alloc) or +[luaT_realloc](#luaT_realloc), or the C `malloc` or `realloc` +functions. A Lua error will be generated if running out of memory. + + +### void luaT_free(lua_State *L, void *ptr) ### + +Free memory allocated at address `ptr`. The memory must have been +previously allocated with [luaT_alloc](#luaT_alloc) or +[luaT_realloc](#luaT_realloc), or the C `malloc` or `realloc` +functions. + + +## Class creation and basic handling ## + +A `luaT` class is basically either a Lua _table_ or _userdata_ with +an appropriate _metatable_. This appropriate metatable is created with +[luaT_newmetatable](#luaT_newmetatable). Contrary to luaL userdata +functions, luaT mechanism handles inheritance. If the class inherit from +another class, then the metatable will itself have a metatable +corresponding to the _parent metatable_: the metatables are cascaded +according to the class inheritance. Multiple inheritance is not supported. + + +### Operator overloading ### + +The metatable of a `luaT` object contains `Lua` operators like +`__index`, `__newindex`, `__tostring`, `__add` +(etc...). These operators will respectively look for `__index__`, +`__newindex__`, `__tostring__`, `__add__` (etc...) in the +metatable. If found, the corresponding function or value will be returned, +else a Lua error will be raised. + +If one wants to provide `__index__` or `__newindex__` in the +metaclass, these operators must follow a particular scheme: + + * `__index__` must either return a value _and_ `true` or return `false` only. In the first case, it means `__index__` was able to handle the given argument (for e.g., the type was correct). The second case means it was not able to do anything, so `__index` in the root metatable can then try to see if the metaclass contains the required value. + + * `__newindex__` must either return `true` or `false`. As for `__index__`, `true` means it could handle the argument and `false` not. If not, the root metatable `__newindex` will then raise an error if the object was a userdata, or apply a rawset if the object was a Lua table. + +Other metaclass operators like `__tostring__`, `__add__`, etc... do not have any particular constraint. + + +### const char* luaT_newmetatable(lua_State *L, const char *tname, const char *parenttname, lua_CFunction constructor, lua_CFunction destructor, lua_CFunction factory) ### + +This function creates a new metatable, which is the Lua way to define a new +object class. As for `luaL_newmetatable`, the metatable is registered in +the Lua registry table, with the key `tname`. In addition, `tname` is +also registered in the Lua registry, with the metatable as key (the +typename of a given object can be thus easily retrieved). + +The class name `tname` must be of the form `modulename.classname`. The module name +If not NULL, `parenttname` must be a valid typename corresponding to the +parent class of the new class. + +If not NULL, `constructor`, a function `new` will be added to the metatable, pointing to this given function. The constructor might also +be called through `modulename.classname()`, which is an alias setup by `luaT_metatable`. + +If not NULL, `destructor` will be called when garbage collecting the object. + +If not NULL, `factory` must be a Lua C function creating an empty object +instance of the class. This functions are used in Torch for serialization. + +Note that classes can be partly defined in C and partly defined in Lua: +once the metatable is created in C, it can be filled up with additional +methods in Lua. + +The return value is the value returned by [luaT_typenameid](#luat_typenameid). + + +### int luaT_pushmetatable(lua_State *L, const name *tname) ### + +Push the metatable with type name `tname` on the stack, it `tname` is a +valid Torch class name (previously registered with luaT_newmetatable). + +On success, returns 1. If `tname` is invalid, nothing is pushed and it +returns 0. + + +### const char* luaT_typenameid(lua_State *L, const char *tname) ### + +If `tname` is a valid Torch class name, then returns a unique string (the +contents will be the same than `tname`) pointing on the string registered +in the Lua registry. This string is thus valid as long as Lua is +running. The returned string shall not be freed. + +If `tname` is an invalid class name, returns NULL. + + +### const char* luaT_typename(lua_State *L, int ud) ### + +Returns the typename of the object at index `ud` on the stack. If it is +not a valid Torch object, returns NULL. + + +### void luaT_pushudata(lua_State *L, void *udata, const char *tname) ### + +Given a C structure `udata`, push a userdata object on the stack with +metatable corresponding to `tname`. Obviously, `tname` must be a valid +Torch name registered with [luaT_newmetatable](#luat_newmetatable). + + +### void *luaT_toudata(lua_State *L, int ud, const char *tname) ### + +Returns a pointer to the original C structure previously pushed on the +stack with [luaT_pushudata](#luat_pushudata), if the object at index +`ud` is a valid Torch class name. Returns NULL otherwise. + + +### int luaT_isudata(lua_State *L, int ud, const char *tname) ### + +Returns 1 if the object at index `ud` on the stack is a valid Torch class name `tname`. +Returns 0 otherwise. + + +### Checking fields of a table ### + +This functions check that the table at the given index `ud` on the Lua +stack has a field named `field`, and that it is of the specified type. +These function raises a Lua error on failure. + + +## void *luaT_getfieldcheckudata(lua_State *L, int ud, const char *field, const char *tname) ## + +Checks that the field named `field` of the table at index `ud` is a +Torch class name `tname`. Returns the pointer of the C structure +previously pushed on the stack with [luaT_pushudata](#luat_pushudata) on +success. The function raises a Lua error on failure. + + +## void *luaT_getfieldchecklightudata(lua_State *L, int ud, const char *field) ## + +Checks that the field named `field` of the table at index `ud` is a +lightuserdata. Returns the lightuserdata pointer on success. The function +raises a Lua error on failure. + + +## int luaT_getfieldcheckint(lua_State *L, int ud, const char *field) ## + +Checks that the field named `field` of the table at index `ud` is an +int. Returns the int value pointer on success. The function raises a Lua +error on failure. + + +## const char* luaT_getfieldcheckstring(lua_State *L, int ud, const char *field) ## + +Checks that the field named `field` of the table at index `ud` is a +string. Returns a pointer to the string on success. The function raises a +Lua error on failure. + + +## int luaT_getfieldcheckboolean(lua_State *L, int ud, const char *field) ## + +Checks that the field named `field` of the table at index `ud` is a +boolean. On success, returns 1 if the boolean is `true`, 0 if it is +`false`. The function raises a Lua error on failure. + + +## void luaT_getfieldchecktable(lua_State *L, int ud, const char *field) ## + +Checks that the field named `field` of the table at index `ud` is a +table. On success, push the table on the stack. The function raises a Lua +error on failure. + + +### int luaT_typerror(lua_State *L, int ud, const char *tname) ### + +Raises a `luaL_argerror` (and returns its value), claiming that the +object at index `ud` on the stack is not of type `tname`. Note that +this function does not check the type, it only raises an error. + + +### int luaT_checkboolean(lua_State *L, int ud) ### + +Checks that the value at index `ud` is a boolean. On success, returns 1 +if the boolean is `true`, 0 if it is `false`. The function raises a Lua +error on failure. + + +### int luaT_optboolean(lua_State *L, int ud, int def) ### + +Checks that the value at index `ud` is a boolean. On success, returns 1 +if the boolean is `true`, 0 if it is `false`. If there is no value at +index `ud`, returns `def`. In any other cases, raises an error. + + +### void luaT_registeratname(lua_State *L, const struct luaL_Reg *methods, const char *name) ### + +This function assume a table is on the stack. It creates a table field +`name` in the table (if this field does not exist yet), and fill up +`methods` in this table field. + + +### const char *luaT_classrootname(const char *tname) ### + +Assuming `tname` is of the form `modulename.classname`, returns +`classname`. The returned value shall not be freed. It is a pointer +inside `tname` string. + + +### const char *luaT_classmodulename(const char *tname) ### + +Assuming `tname` is of the form `modulename.classname`, returns +`modulename`. The returned value shall not be freed. It is valid until the +next call to `luaT_classrootname`. + + +### void luaT_stackdump(lua_State *L) ### + +This function print outs the state of the Lua stack. It is useful for debug +purposes. + diff --git a/nerv/lib/luaT/luaT.c b/nerv/lib/luaT/luaT.c new file mode 100644 index 0000000..7b85ce3 --- /dev/null +++ b/nerv/lib/luaT/luaT.c @@ -0,0 +1,1079 @@ +#include +#include + +#include "luaT.h" + +void* luaT_alloc(lua_State *L, long size) +{ + void *ptr; + + if(size == 0) + return NULL; + + if(size < 0) + luaL_error(L, "$ Torch: invalid memory size -- maybe an overflow?"); + + ptr = malloc(size); + if(!ptr) + luaL_error(L, "$ Torch: not enough memory: you tried to allocate %dGB. Buy new RAM!", size/1073741824); + + return ptr; +} + +void* luaT_realloc(lua_State *L, void *ptr, long size) +{ + if(!ptr) + return(luaT_alloc(L, size)); + + if(size == 0) + { + luaT_free(L, ptr); + return NULL; + } + + if(size < 0) + luaL_error(L, "$ Torch: invalid memory size -- maybe an overflow?"); + + ptr = realloc(ptr, size); + if(!ptr) + luaL_error(L, "$ Torch: not enough memory: you tried to reallocate %dGB. Buy new RAM!", size/1073741824); + return ptr; +} + +void luaT_free(lua_State *L, void *ptr) +{ + free(ptr); +} + +void luaT_stackdump(lua_State *L) +{ + int i; + const char *tname = NULL; + int top = lua_gettop(L); + for(i = 1; i <= top; i++) + { + int t = lua_type(L, i); + printf("%3d. ", i); + switch(t) + { + case LUA_TSTRING: + printf("'%s'", lua_tostring(L,i)); + break; + case LUA_TBOOLEAN: + printf(lua_toboolean(L, i) ? "true" : "false"); + break; + case LUA_TNUMBER: + printf("%g", lua_tonumber(L,i)); + break; + case LUA_TUSERDATA: + tname = luaT_typename(L, i); + printf("userdata %lx [%s]", (long)lua_topointer(L, i), (tname ? tname : "not a Torch object")); + break; + case 10: + tname = luaT_typename(L, i); + printf("cdata %lx [%s]", (long)lua_topointer(L, i), (tname ? tname : "not a Torch object")); + break; + case LUA_TTABLE: + lua_pushvalue(L, i); + lua_rawget(L, LUA_REGISTRYINDEX); + if(lua_isstring(L, -1)) + tname = lua_tostring(L, -1); /*luaT_typenameid(L, lua_tostring(L, -1)); */ + else + tname = NULL; + lua_pop(L, 1); + if(tname) + printf("metatable [%s]", tname); + else + { + tname = luaT_typename(L, i); + printf("table %lx [%s]", (long)lua_topointer(L, i), (tname ? tname : "not a Torch object")); + } + break; + default: + printf("Lua object type: %s", lua_typename(L,t)); + break; + } + printf("\n"); + } + printf("---------------------------------------------\n"); +} + +/* metatable operator methods */ +static int luaT_mt__index(lua_State *L); +static int luaT_mt__newindex(lua_State *L); +static int luaT_mt__tostring(lua_State *L); +static int luaT_mt__add(lua_State *L); +static int luaT_mt__sub(lua_State *L); +static int luaT_mt__mul(lua_State *L); +static int luaT_mt__div(lua_State *L); +static int luaT_mt__mod(lua_State *L); +static int luaT_mt__pow(lua_State *L); +static int luaT_mt__unm(lua_State *L); +static int luaT_mt__concat(lua_State *L); +static int luaT_mt__len(lua_State *L); +static int luaT_mt__eq(lua_State *L); +static int luaT_mt__lt(lua_State *L); +static int luaT_mt__le(lua_State *L); +static int luaT_mt__call(lua_State *L); + +/* Constructor-metatable methods */ +static int luaT_cmt__call(lua_State *L); +static int luaT_cmt__newindex(lua_State *L); + +const char* luaT_newmetatable(lua_State *L, const char *tname, const char *parenttname, + lua_CFunction constructor, lua_CFunction destructor, lua_CFunction factory) +{ + lua_pushcfunction(L, luaT_lua_newmetatable); + lua_pushstring(L, tname); + (parenttname ? lua_pushstring(L, parenttname) : lua_pushnil(L)); + (constructor ? lua_pushcfunction(L, constructor) : lua_pushnil(L)); + (destructor ? lua_pushcfunction(L, destructor) : lua_pushnil(L)); + (factory ? lua_pushcfunction(L, factory) : lua_pushnil(L)); + lua_call(L, 5, 1); + return luaT_typenameid(L, tname); +} + +int luaT_pushmetatable(lua_State *L, const char *tname) +{ + lua_getfield(L, LUA_REGISTRYINDEX, tname); + if(lua_isnil(L, -1)) + { + lua_pop(L, 1); + return 0; + } + return 1; +} + +const char *luaT_typenameid(lua_State *L, const char *tname) +{ + if(luaT_pushmetatable(L, tname)) + { + const char *tnameid = NULL; + lua_rawget(L, LUA_REGISTRYINDEX); + if(lua_isstring(L, -1)) + tnameid = lua_tostring(L, -1); + lua_pop(L, 1); /* the string/nil */ + return tnameid; + } + return NULL; +} + +static const char cdataname[] = "" + "local _, ffi = pcall(require, 'ffi')\n" + "if ffi then\n" + " local id2name = {}\n" + " return function(cdata, name)\n" + " local id = tonumber(ffi.typeof(cdata))\n" + " if id then\n" + " if name then\n" + " id2name[id] = name\n" + " return name\n" + " else\n" + " return rawget(id2name, id)\n" + " end\n" + " end\n" + " return nil\n" + " end\n" + "else\n" + " return function() end\n" + "end\n"; + +static const char* luaT_cdataname(lua_State *L, int ud, const char *tname) +{ + lua_pushstring(L, "__cdataname"); + lua_rawget(L, LUA_REGISTRYINDEX); + if(lua_isnil(L,-1)) + { + lua_pop(L, 1); + + if(luaL_dostring(L, cdataname)) /* did something go wrong? */ + luaL_error(L, "internal error (could not load cdataname): %s", lua_tostring(L, -1)); + + lua_pushstring(L, "__cdataname"); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } + if(!lua_isfunction(L, -1)) /* should not happen */ + luaL_error(L, "internal error (cdataname is not a function)"); + + lua_pushvalue(L, ud); + if(tname) + lua_pushstring(L, tname); + if(lua_pcall(L, (tname ? 2 : 1), 1, 0)) + luaL_error(L, "internal error (cdataname): %s", lua_tostring(L, -1)); + + tname = lua_tostring(L, -1); + lua_pop(L, 1); + + return tname; +} + +const char* luaT_typename(lua_State *L, int ud) +{ + if(lua_type(L, ud) == 10) + return luaT_cdataname(L, ud, NULL); + else if(lua_getmetatable(L, ud)) + { + const char *tname = NULL; + lua_rawget(L, LUA_REGISTRYINDEX); + if(lua_isstring(L, -1)) + tname = lua_tostring(L, -1); + lua_pop(L, 1); /* the string/nil */ + return tname; + } + return NULL; +} + +void luaT_pushudata(lua_State *L, void *udata, const char *tname) +{ + if(udata) + { + void **udata_p = lua_newuserdata(L, sizeof(void*)); + *udata_p = udata; + if(!luaT_pushmetatable(L, tname)) + luaL_error(L, "Torch internal problem: cannot find metatable for type <%s>", tname); + lua_setmetatable(L, -2); + } + else + lua_pushnil(L); +} + +void *luaT_toudata(lua_State *L, int ud, const char *tname) +{ + void **p = lua_touserdata(L, ud); + if(p != NULL) /* value is a userdata? */ + { + if(!luaT_pushmetatable(L, tname)) + luaL_error(L, "Torch internal problem: cannot find metatable for type <%s>", tname); + + /* initialize the table we want to get the metatable on */ + /* note that we have to be careful with indices, as we just inserted stuff */ + lua_pushvalue(L, (ud < 0 ? ud - 1 : ud)); + while(lua_getmetatable(L, -1)) /* get the next metatable */ + { + lua_remove(L, -2); /* remove the previous metatable [or object, if first time] */ + if(lua_rawequal(L, -1, -2)) + { + lua_pop(L, 2); /* remove the two metatables */ + return *p; + } + } + lua_pop(L, 2); /* remove the two metatables */ + } + return NULL; +} + +int luaT_isudata(lua_State *L, int ud, const char *tname) +{ + if(luaT_toudata(L, ud, tname)) + return 1; + else + return 0; +} + +void *luaT_checkudata(lua_State *L, int ud, const char *tname) +{ + void *p = luaT_toudata(L, ud, tname); + if(!p) + luaT_typerror(L, ud, tname); + return p; +} + +void *luaT_getfieldcheckudata(lua_State *L, int ud, const char *field, const char *tname) +{ + void *p; + lua_getfield(L, ud, field); + if(lua_isnil(L, -1)) + luaL_error(L, "bad argument #%d (field %s does not exist)", ud, field); + p = luaT_toudata(L, -1, tname); + if(!p) + luaL_error(L, "bad argument #%d (field %s is not a %s)", ud, field, tname); + return p; +} + +void *luaT_getfieldchecklightudata(lua_State *L, int ud, const char *field) +{ + void *p; + lua_getfield(L, ud, field); + if(lua_isnil(L, -1)) + luaL_error(L, "bad argument #%d (field %s does not exist)", ud, field); + + if(!lua_islightuserdata(L, -1)) + luaL_error(L, "bad argument #%d (field %s is not a light userdata)", ud, field); + + p = lua_touserdata(L, -1); + + return p; +} + +double luaT_getfieldchecknumber(lua_State *L, int ud, const char *field) +{ + lua_getfield(L, ud, field); + if(lua_isnil(L, -1)) + luaL_error(L, "bad argument #%d (field %s does not exist)", ud, field); + if(!lua_isnumber(L, -1)) + luaL_error(L, "bad argument #%d (field %s is not a number)", ud, field); + return lua_tonumber(L, -1); +} + +int luaT_getfieldcheckint(lua_State *L, int ud, const char *field) +{ + lua_getfield(L, ud, field); + if(lua_isnil(L, -1)) + luaL_error(L, "bad argument #%d (field %s does not exist)", ud, field); + if(!lua_isnumber(L, -1)) + luaL_error(L, "bad argument #%d (field %s is not a number)", ud, field); + return (int)lua_tonumber(L, -1); +} + +const char* luaT_getfieldcheckstring(lua_State *L, int ud, const char *field) +{ + lua_getfield(L, ud, field); + if(lua_isnil(L, -1)) + luaL_error(L, "bad argument #%d (field %s does not exist)", ud, field); + if(!lua_isstring(L, -1)) + luaL_error(L, "bad argument #%d (field %s is not a string)", ud, field); + return lua_tostring(L, -1); +} + +int luaT_getfieldcheckboolean(lua_State *L, int ud, const char *field) +{ + lua_getfield(L, ud, field); + if(lua_isnil(L, -1)) + luaL_error(L, "bad argument #%d (field %s does not exist)", ud, field); + if(!lua_isboolean(L, -1)) + luaL_error(L, "bad argument #%d (field %s is not a boolean)", ud, field); + return lua_toboolean(L, -1); +} + +void luaT_getfieldchecktable(lua_State *L, int ud, const char *field) +{ + lua_getfield(L, ud, field); + if(lua_isnil(L, -1)) + luaL_error(L, "bad argument #%d (field %s does not exist)", ud, field); + if(!lua_istable(L, -1)) + luaL_error(L, "bad argument #%d (field %s is not a table)", ud, field); +} + +/**** type checks as in luaL ****/ +int luaT_typerror(lua_State *L, int ud, const char *tname) +{ + const char *msg; + const char *tnameud = luaT_typename(L, ud); + + if(!tnameud) + tnameud = lua_typename(L, ud); + + msg = lua_pushfstring(L, "%s expected, got %s", + tname, + (tnameud ? tnameud : "unknown object")); + + return luaL_argerror(L, ud, msg); +} + +int luaT_checkboolean(lua_State *L, int ud) +{ + if(!lua_isboolean(L, ud)) + luaT_typerror(L, ud, lua_typename(L, LUA_TBOOLEAN)); + return lua_toboolean(L, ud); +} + +int luaT_optboolean(lua_State *L, int ud, int def) +{ + if(lua_isnoneornil(L,ud)) + return def; + + return luaT_checkboolean(L, ud); +} + +void luaT_registeratname(lua_State *L, const struct luaL_Reg *methods, const char *name) +{ + int idx = lua_gettop(L); + + luaL_checktype(L, idx, LUA_TTABLE); + lua_pushstring(L, name); + lua_rawget(L, idx); + + if(lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_pushstring(L, name); + lua_newtable(L); + lua_rawset(L, idx); + + lua_pushstring(L, name); + lua_rawget(L, idx); + } + + luaL_register(L, NULL, methods); + lua_pop(L, 1); +} + + +/* utility functions */ +const char *luaT_classrootname(const char *tname) +{ + int i; + int sz = strlen(tname); + + for(i = 0; i < sz; i++) + { + if(tname[i] == '.') + return tname+i+1; + } + return tname; +} + +/* module_name must be a buffer at least as big as tname + * return true if the class is part of a module */ +int luaT_classmodulename(const char *tname, char *module_name) +{ + char chars[] = {'.', '\0'}; + size_t n; + n = strcspn(tname, chars); + strncpy(module_name, tname, n); + module_name[n] = '\0'; + return tname[n] == '.'; +} + +/* Lua only functions */ +int luaT_lua_newmetatable(lua_State *L) +{ + const char* tname = luaL_checkstring(L, 1); + char module_name[256]; + int is_in_module = 0; + is_in_module = luaT_classmodulename(tname, module_name); + + lua_settop(L, 5); + luaL_argcheck(L, lua_isnoneornil(L, 2) || lua_isstring(L, 2), 2, "parent class name or nil expected"); + luaL_argcheck(L, lua_isnoneornil(L, 3) || lua_isfunction(L, 3), 3, "constructor function or nil expected"); + luaL_argcheck(L, lua_isnoneornil(L, 4) || lua_isfunction(L, 4), 4, "destructor function or nil expected"); + luaL_argcheck(L, lua_isnoneornil(L, 5) || lua_isfunction(L, 5), 5, "factory function or nil expected"); + + if(is_in_module) + lua_getfield(L, LUA_GLOBALSINDEX, module_name); + else + lua_pushvalue(L, LUA_GLOBALSINDEX); + if(!lua_istable(L, 6)) + luaL_error(L, "while creating metatable %s: bad argument #1 (%s is an invalid module name)", tname, module_name); + + /* we first create the new metaclass if we have to */ + if(!luaT_pushmetatable(L, tname)) + { + /* create the metatable */ + lua_newtable(L); + + /* registry[name] = metatable */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, tname); + + /* registry[metatable] = tname */ + lua_pushvalue(L, -1); + lua_pushstring(L, tname); + lua_rawset(L, LUA_REGISTRYINDEX); + + /* __index handling */ + lua_pushcfunction(L, luaT_mt__index); + lua_setfield(L, -2, "__index"); + + /* __newindex handling */ + lua_pushcfunction(L, luaT_mt__newindex); + lua_setfield(L, -2, "__newindex"); + + /* __typename contains the typename */ + lua_pushstring(L, tname); + lua_setfield(L, -2, "__typename"); + + /* __metatable is self */ + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__metatable"); + + /* by default, __version equals 1 */ + lua_pushnumber(L, 1); + lua_setfield(L, -2, "__version"); + + /* assign default operator functions */ + lua_pushcfunction(L, luaT_mt__tostring); + lua_setfield(L, -2, "__tostring"); + + lua_pushcfunction(L, luaT_mt__add); + lua_setfield(L, -2, "__add"); + + lua_pushcfunction(L, luaT_mt__sub); + lua_setfield(L, -2, "__sub"); + + lua_pushcfunction(L, luaT_mt__mul); + lua_setfield(L, -2, "__mul"); + + lua_pushcfunction(L, luaT_mt__div); + lua_setfield(L, -2, "__div"); + + lua_pushcfunction(L, luaT_mt__mod); + lua_setfield(L, -2, "__mod"); + + lua_pushcfunction(L, luaT_mt__pow); + lua_setfield(L, -2, "__pow"); + + lua_pushcfunction(L, luaT_mt__unm); + lua_setfield(L, -2, "__unm"); + + lua_pushcfunction(L, luaT_mt__concat); + lua_setfield(L, -2, "__concat"); + + lua_pushcfunction(L, luaT_mt__len); + lua_setfield(L, -2, "__len"); + + lua_pushcfunction(L, luaT_mt__eq); + lua_setfield(L, -2, "__eq"); + + lua_pushcfunction(L, luaT_mt__lt); + lua_setfield(L, -2, "__lt"); + + lua_pushcfunction(L, luaT_mt__le); + lua_setfield(L, -2, "__le"); + + lua_pushcfunction(L, luaT_mt__call); + lua_setfield(L, -2, "__call"); + } + + /* we assign the parent class if necessary */ + if(!lua_isnoneornil(L, 2)) + { + if(lua_getmetatable(L, -1)) + luaL_error(L, "class %s has been already assigned a parent class\n", tname); + else + { + const char* parenttname = luaL_checkstring(L, 2); + if(!luaT_pushmetatable(L, parenttname)) + luaL_error(L, "bad argument #2 (invalid parent class name %s)", parenttname); + lua_setmetatable(L, -2); + } + } + + /* register the destructor function */ + if(!lua_isnoneornil(L, 4)) + { + /* does it exists already? */ + lua_pushstring(L, "__gc"); + lua_rawget(L, -2); + + if(lua_isnil(L, -1)) + { + lua_pop(L, 1); /* pop nil */ + lua_pushstring(L, "__gc"); + lua_pushvalue(L, 4); + lua_rawset(L, -3); + } + else + luaL_error(L, "%s has been already assigned a destructor", tname); + } + + /* register the factory function */ + if(!lua_isnoneornil(L, 5)) + { + /* does it exists already? */ + lua_pushstring(L, "__factory"); + lua_rawget(L, -2); + + if(lua_isnil(L, -1)) + { + lua_pop(L, 1); /* pop nil */ + lua_pushstring(L, "__factory"); + lua_pushvalue(L, 5); + lua_rawset(L, -3); + } + else + luaL_error(L, "%s has been already assigned a factory", tname); + } + + /******** Constructor table and metatable ********/ + lua_pushstring(L, "__constructor"); + lua_rawget(L, -2); + if(lua_isnil(L, -1)) + { + lua_pop(L, 1); /* pop nil */ + lua_newtable(L); /* fancy table */ + lua_newtable(L); /* fancy metatable */ + + lua_pushvalue(L, -3); /* metatable */ + lua_setfield(L, -2, "__index"); /* so we can get the methods */ + + lua_pushcfunction(L, luaT_cmt__newindex); + lua_setfield(L, -2, "__newindex"); /* so we add new methods */ + + lua_pushcfunction(L, luaT_cmt__call); + lua_setfield(L, -2, "__call"); /* so we can create, we are here for only that */ + + lua_pushvalue(L, -3); + lua_setfield(L, -2, "__metatable"); /* redirect to metatable with methods */ + + lua_setmetatable(L, -2); /* constructor metatable is ... this fancy metatable */ + + /* set metatable[__constructor] = constructor-metatable */ + lua_pushstring(L, "__constructor"); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } + + /* register the constructor function */ + if(!lua_isnoneornil(L, 3)) + { + /* get constructor metatable */ + lua_getmetatable(L, -1); + + /* does it exists already? */ + lua_pushstring(L, "__new"); + lua_rawget(L, -2); + + if(lua_isnil(L, -1)) + { + lua_pop(L, 1); /* pop nil */ + lua_pushstring(L, "__new"); + lua_pushvalue(L, 3); + lua_rawset(L, -3); + + /* set "new" in the metatable too */ + lua_pushstring(L, "new"); + lua_pushvalue(L, 3); + lua_rawset(L, -5); + } + else + luaL_error(L, "%s has been already assigned a constructor", tname); + + /* pop constructor metatable */ + lua_pop(L, 1); + } + + /* module.name = constructor metatable */ + lua_setfield(L, 6, luaT_classrootname(tname)); + + return 1; /* returns the metatable */ +} + +/* Lua only utility functions */ + +/* add any custom type, provided the object has a metatable */ +int luaT_lua_metatype(lua_State *L) +{ + if( (lua_gettop(L) != 2) && (lua_gettop(L) != 3) ) + luaL_error(L, "expecting: string table [ctype]"); + + luaL_checkstring(L, 1); + luaL_checktype(L, 2, LUA_TTABLE); + + if(lua_gettop(L) == 3) + { + if(!luaT_cdataname(L, 3, lua_tostring(L, 1))) + luaL_error(L, "could not register cdata type -- missing ffi library?"); + } + + /* registry[name] = metatable */ + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_rawset(L, LUA_REGISTRYINDEX); + + /* registry[metatable] = tname */ + lua_pushvalue(L, 2); + lua_pushvalue(L, 1); + lua_rawset(L, LUA_REGISTRYINDEX); + + return 0; +} + +/* return a userdata from a C pointer */ +/* you are better to know what you are doing */ +int luaT_lua_pushudata(lua_State *L) +{ + void *udata = NULL; + const char *tname = luaL_checkstring(L, 2); + + if(lua_type(L, 1) == 10) + udata = *((void**)lua_topointer(L, 1)); + else if(lua_isnumber(L, 1)) + udata = (void*)(long)lua_tonumber(L, 1); + else + luaL_argerror(L, 1, "expecting number or cdata"); + + luaT_pushudata(L, udata, tname); + + return 1; +} + +int luaT_lua_factory(lua_State *L) +{ + const char* tname = luaL_checkstring(L, 1); + if(luaT_pushmetatable(L, tname) && !lua_isnil(L, -1)) + { + lua_pushstring(L, "__factory"); + lua_rawget(L, -2); + } + else + { + lua_pushnil(L); + } + return 1; +} + +int luaT_lua_getconstructortable(lua_State *L) +{ + const char* tname = luaL_checkstring(L, 1); + if(luaT_pushmetatable(L, tname)) + { + lua_pushstring(L, "__constructor"); + lua_rawget(L, -2); + return 1; + } + return 0; +} + + +int luaT_lua_typename(lua_State *L) +{ + const char* tname = NULL; + luaL_checkany(L, 1); + if((tname = luaT_typename(L, 1))) + { + lua_pushstring(L, tname); + return 1; + } + return 0; +} + +int luaT_lua_isequal(lua_State *L) +{ + if(lua_isuserdata(L, 1) && lua_isuserdata(L, 2)) + { + void **u1, **u2; + luaL_argcheck(L, luaT_typename(L, 1), 1, "Torch object expected"); + luaL_argcheck(L, luaT_typename(L, 2), 2, "Torch object expected"); + + u1 = lua_touserdata(L, 1); + u2 = lua_touserdata(L, 2); + if(*u1 == *u2) + lua_pushboolean(L, 1); + else + lua_pushboolean(L, 0); + } + else if(lua_istable(L, 1) && lua_istable(L, 2)) + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + else + lua_pushboolean(L, 0); + return 1; +} + +int luaT_lua_pointer(lua_State *L) +{ + if(lua_isuserdata(L, 1)) + { + void **ptr; + luaL_argcheck(L, luaT_typename(L, 1), 1, "Torch object expected"); + ptr = lua_touserdata(L, 1); + lua_pushnumber(L, (long)(*ptr)); + return 1; + } + else if(lua_istable(L, 1) || lua_isthread(L, 1) || lua_isfunction(L, 1)) + { + const void* ptr = lua_topointer(L, 1); + lua_pushnumber(L, (long)(ptr)); + return 1; + } + else if(lua_type(L, 1) == 10) /* cdata */ + { + /* we want the pointer holded by cdata */ + /* not the pointer on the cdata object */ + const void* ptr = *((void**)lua_topointer(L, 1)); + lua_pushnumber(L, (long)(ptr)); + return 1; + } + else if(lua_isstring(L, 1)) + { + const char* ptr = lua_tostring(L, 1); + lua_pushnumber(L, (long)(ptr)); + return 1; + } + else + luaL_error(L, "Torch object, table, thread, cdata or function expected"); + + return 0; +} + +int luaT_lua_setenv(lua_State *L) +{ + if(!lua_isfunction(L, 1) && !lua_isuserdata(L, 1)) + luaL_typerror(L, 1, "function or userdata"); + luaL_checktype(L, 2, LUA_TTABLE); + lua_setfenv(L, 1); + return 0; +} + +int luaT_lua_getenv(lua_State *L) +{ + if(!lua_isfunction(L, 1) && !lua_isuserdata(L, 1)) + luaL_typerror(L, 1, "function or userdata"); + lua_getfenv(L, 1); + return 1; +} + +int luaT_lua_getmetatable(lua_State *L) +{ + const char *tname = luaL_checkstring(L, 1); + if(luaT_pushmetatable(L, tname)) + return 1; + return 0; +} + +int luaT_lua_version(lua_State *L) +{ + luaL_checkany(L, 1); + + if(lua_type(L, 1) == 10) + { + const char *tname = luaT_cdataname(L, 1, NULL); + if(tname) + { + luaT_pushmetatable(L, tname); + lua_pushstring(L, "__version"); + lua_rawget(L, -2); + return 1; + } + return 0; + } + else if(lua_getmetatable(L, 1)) + { + lua_pushstring(L, "__version"); + lua_rawget(L, -2); + return 1; + } + return 0; +} + +int luaT_lua_setmetatable(lua_State *L) +{ + const char *tname = luaL_checkstring(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + + if(!luaT_pushmetatable(L, tname)) + luaL_error(L, "unknown typename %s\n", tname); + lua_setmetatable(L, 1); + + return 1; +} + +/* metatable operator methods */ +static int luaT_mt__index(lua_State *L) +{ + if(!lua_getmetatable(L, 1)) + luaL_error(L, "critical internal indexing error: no metatable found"); + + if(!lua_istable(L, -1)) + luaL_error(L, "critical internal indexing error: not a metatable"); + + /* test for __index__ method first */ + lua_getfield(L, -1, "__index__"); + if(!lua_isnil(L, -1)) + { + int result; + + if(!lua_isfunction(L, -1)) + luaL_error(L, "critical internal indexing error: __index__ is not a function"); + + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + + lua_call(L, 2, LUA_MULTRET); /* DEBUG: risque: faut vraiment retourner 1 ou 2 valeurs... */ + + result = lua_toboolean(L, -1); + lua_pop(L, 1); + + if(result) + return 1; + + /* on the stack: 1. the object 2. the value 3. the metatable */ + /* apparently, __index wants only one element returned */ + /* return lua_gettop(L)-3; */ + + } + else + lua_pop(L, 1); /* remove nil __index__ on the stack */ + + lua_pushvalue(L, 2); + lua_gettable(L, -2); + + return 1; +} + +static int luaT_mt__newindex(lua_State *L) +{ + if(!lua_getmetatable(L, 1)) + luaL_error(L, "critical internal indexing error: no metatable found"); + + if(!lua_istable(L, -1)) + luaL_error(L, "critical internal indexing error: not a metatable"); + + /* test for __newindex__ method first */ + lua_getfield(L, -1, "__newindex__"); + if(!lua_isnil(L, -1)) + { + int result; + + if(!lua_isfunction(L, -1)) + luaL_error(L, "critical internal indexing error: __newindex__ is not a function"); + + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_pushvalue(L, 3); + + lua_call(L, 3, 1); /* DEBUG: risque: faut vraiment retourner qqch */ + + result = lua_toboolean(L, -1); + lua_pop(L, 1); + + if(result) + return 0; + } + else + lua_pop(L, 1); /* remove nil __newindex__ on the stack */ + + lua_pop(L, 1); /* pop the metatable */ + if(lua_istable(L, 1)) + lua_rawset(L, 1); + else + luaL_error(L, "the class %s cannot be indexed", luaT_typename(L, 1)); + + return 0; +} + +/* note: check dans metatable pour ca, donc necessaire */ +#define MT_DECLARE_OPERATOR(NAME, NIL_BEHAVIOR) \ + int luaT_mt__##NAME(lua_State *L) \ + { \ + if(!lua_getmetatable(L, 1)) \ + luaL_error(L, "internal error in __" #NAME ": no metatable"); \ + \ + lua_getfield(L, -1, "__" #NAME "__"); \ + if(lua_isnil(L, -1)) \ + { \ + NIL_BEHAVIOR; \ + } \ + else \ + { \ + if(lua_isfunction(L, -1)) \ + { \ + lua_insert(L, 1); /* insert function */ \ + lua_pop(L, 1); /* remove metatable *