view th_ioctx_mem.c @ 693:a622d21833e1

Implement fopen() in mem ioctx, and simplistic read-only/write flag support.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 09 Mar 2020 18:54:37 +0200
parents ea6bcbfb9d18
children 0fc5ddaccc57
line wrap: on
line source

/*
 * Simple I/O abstraction and context handling layer
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2012-2020 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#include "th_ioctx.h"


static int th_mem_fopen(th_ioctx *ctx)
{
    if (ctx->mode == NULL)
        return THERR_NULLPTR;

    // Setup some things based on the mode string
    switch (ctx->mode[0])
    {
        case 'a':
            // Append
            ctx->memWrite = TRUE;
            ctx->memOffset = ctx->memSize;
            break;

        case 'r':
            // Read or read-write
            ctx->memWrite = ctx->mode[1] == '+';
            ctx->memOffset = 0;
            break;

        case 'w':
            // Write, so truncate size
            ctx->memOffset = 0;
            ctx->memSize = 0;
            ctx->memWrite = TRUE;
            break;

        default:
            return THERR_INVALID_ARGS;
    }

    return THERR_OK;
}


static BOOL th_mem_realloc(th_ioctx *ctx, const size_t newSize)
{
    size_t grow;

    // Check write flag
    if (!ctx->memWrite)
    {
        ctx->status = THERR_FWRITE;
        return FALSE;
    }

    // Check against max size
    if (ctx->maxSize > 0 && newSize > ctx->maxSize)
    {
        ctx->status = THERR_BOUNDS;
        return FALSE;
    }

    // New size is smaller than old
    if (newSize < ctx->memAlloc)
        goto out;

    // Compute the allocation grow amount
    grow = (ctx->minAlloc > 0) ? ctx->minAlloc : 8 * 1024;
    if (newSize - ctx->memAlloc > grow)
        grow += newSize - ctx->memAlloc;

    if (ctx->maxSize > 0 && ctx->memAlloc + grow >= ctx->maxSize)
    {
        ctx->status = THERR_BOUNDS;
        return FALSE;
    }

    // Grow the buffer
    ctx->memAlloc += grow;
    if ((ctx->memData = th_realloc(ctx->memData, ctx->memAlloc)) == NULL)
    {
        ctx->status = THERR_MALLOC;
        return FALSE;
    }

out:
    ctx->memSize = newSize;

    return TRUE;
}


static int th_mem_freset(th_ioctx *ctx)
{
    ctx->memOffset = 0;
    return THERR_OK;
}


static int th_mem_ferror(th_ioctx *ctx)
{
    return ctx->status;
}


static int th_mem_fseek(th_ioctx *ctx, const off_t offset, const int whence)
{
    off_t newPos;

    // Calculate the new position
    switch (whence)
    {
        case SEEK_SET:
            newPos = offset;
            break;

        case SEEK_CUR:
            newPos = ctx->memOffset + offset;
            break;

        case SEEK_END:
            newPos = ctx->memSize + offset;
            break;

        default:
            return -1;
    }

    // Set the new position
    ctx->memOffset = newPos;

    // Check the new position
    if (newPos < 0)
        return -1;

    //if (!th_mem_realloc(ctx, newPos))
    //    return -1;

    return 0;
}


static off_t th_mem_fsize(th_ioctx *ctx)
{
    return ctx->memSize;
}


static off_t th_mem_ftell(th_ioctx *ctx)
{
    return ctx->memOffset;
}


static BOOL th_mem_feof(th_ioctx *ctx)
{
    return ((size_t) ctx->memOffset) >= ctx->memSize;
}


static int th_mem_fgetc(th_ioctx *ctx)
{
    // Check for EOF
    if ((size_t) ctx->memOffset < ctx->memSize)
        return ctx->memData[ctx->memOffset++];
    else
        return EOF;
}


static size_t th_mem_fread(void *buf, size_t size, size_t nmemb, th_ioctx *ctx)
{
    size_t length = size * nmemb;

    // Check if we can read the whole chunk
    if (((size_t) ctx->memOffset + length) >= ctx->memSize)
    {
        nmemb = (ctx->memSize - ctx->memOffset) / size;
        length = size * nmemb;
    }

    memcpy(buf, ctx->memData + ctx->memOffset, length);
    ctx->memOffset += length;
    return nmemb;
}


static int th_mem_fputc(int ch, th_ioctx *ctx)
{
    // Check for EOF
    if (!th_mem_realloc(ctx, ctx->memOffset + 1))
        return EOF;

    ctx->memData[ctx->memOffset++] = ch;
    return ch;
}


static size_t th_mem_fwrite(const void *buf, size_t size, size_t nmemb, th_ioctx *ctx)
{
    size_t length = size * nmemb;

    // Check if we can write the whole chunk
    if (!th_mem_realloc(ctx, ctx->memOffset + length))
    {
        nmemb = (ctx->memSize - ctx->memOffset) / size;
        length = size * nmemb;
    }

    if (length > 0)
    {
        memcpy(ctx->memData + ctx->memOffset, buf, length);
        ctx->memOffset += length;
    }
    return nmemb;
}


const th_ioctx_ops th_mem_io_ops =
{
    "MemIO",

    th_mem_fopen,
    NULL,

    th_mem_freset,
    th_mem_ferror,
    th_mem_fseek,
    th_mem_fsize,
    th_mem_ftell,
    th_mem_feof,
    th_mem_fgetc,
    th_mem_fputc,
    th_mem_fread,
    th_mem_fwrite,

    NULL,
    NULL,
    NULL
};