aboutsummaryrefslogblamecommitdiff
path: root/nerv/lib/io/chunk_file.c
blob: e70ffc98e5f9f8116b39ba84c2071ae05e4d766e (plain) (tree)




























































































                                                                               
                     



















                                                          






























                                                                      
                 
                     
                          






























































                                                                                       
                  
     





                                      
     
                                                  





















                                                                 
#include "../../common.h"
#include "chunk_file.h"
#include <stdlib.h>
#include <string.h>
#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->info = NULL;
    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));
    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->fp = fp;
    cfp->info = head;
    cfp->status = CF_READ;
    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 *