diff options
Diffstat (limited to 'io/chunk_file.c')
-rw-r--r-- | io/chunk_file.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/io/chunk_file.c b/io/chunk_file.c new file mode 100644 index 0000000..ce346c5 --- /dev/null +++ b/io/chunk_file.c @@ -0,0 +1,305 @@ +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "../common.h" +#include "chunk_file.h" + +#define INVALID_FORMAT_ERROR(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) + +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) + { + *status = WRITE_ERROR; + return; + } + 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; + 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++) + { + 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); + } + lua_setfield(L, -2, "metadata"); + lfp = (ChunkFileHandle *)malloc(sizeof(ChunkFileHandle)); + lfp->fp = fp; + 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); +} + +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 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); + 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"); + 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); + 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); + 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); + return 1; +} + +int nerv_chunk_file_handle_destroy(lua_State *L) { + ChunkFileHandle *pfh = luaT_checkudata(L, 1, + nerv_chunk_file_handle_tname); + fclose(pfh->fp); + free(pfh); + return 0; +} + +static int nerv_chunk_destroy(lua_State *L) { + ChunkInfo *pci = luaT_checkudata(L, 1, nerv_chunk_info_tname); + free(pci); + 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); + return 0; +} + +static const luaL_Reg nerv_chunk_file_methods[] = { + {"get_chunkdata", nerv_chunk_file_get_chunkdata}, + {"_write_chunkdata", nerv_chunk_file_write_chunkdata}, + {"__init", nerv_chunk_file___init}, + {NULL, NULL} +}; + +void nerv_chunk_file_init(lua_State *L) { + luaT_newmetatable(L, nerv_chunk_file_tname, NULL, + nerv_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); + luaT_newmetatable(L, nerv_chunk_info_tname, NULL, + NULL, nerv_chunk_destroy, NULL); + luaT_newmetatable(L, nerv_chunk_data_tname, NULL, + NULL, nerv_chunk_data_destroy, NULL); +} + |