view src/dmgrowbuf.c @ 2298:b5abfff07ca9

Add new DMGrowBuf helper functions dmGrowBufCopyOffsSize() and dmGrowBufConstCopyOffsSize().
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 04 Jul 2019 10:54:16 +0300
parents 39b4e06785f5
children
line wrap: on
line source

/*
 * DMLib
 * -- Growable buffer implementation
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2018-2019 Tecnic Software productions (TNSP)
 */
#include "dmgrowbuf.h"

//#define DM_GROWBUF_DEBUG 1

#ifdef DM_GROWBUF_DEBUG
#    define DM_DBG(...) do { fprintf(stderr, __VA_ARGS__); } while (0)
#else
#    define DM_DBG(...) do { } while (0)
#endif


int dmGrowBufInit(DMGrowBuf *buf)
{
    DM_DBG("dmGrowBufInit(%p)\n", buf);

    if (buf == NULL)
        return DMERR_NULLPTR;

    dmMemset(buf, 0, sizeof(DMGrowBuf));

    return DMERR_OK;
}


int dmGrowBufAlloc(DMGrowBuf *buf, const size_t initial, const size_t mingrow)
{
    int res;

    DM_DBG("dmGrowBufAlloc(%p, %" DM_PRIu_SIZE_T ", %" DM_PRIu_SIZE_T ")\n",
        buf, initial, mingrow);

    if ((res = dmGrowBufInit(buf)) != DMERR_OK)
        return res;

    buf->len  = 0;
    buf->offs = 0;
    buf->size = initial;
    buf->mingrow = mingrow;
    buf->backwards = FALSE;
    buf->is_const  = FALSE;

    // Allocate the data
    if ((buf->data = dmMalloc0(initial)) == NULL)
        return DMERR_MALLOC;

    return DMERR_OK;
}


void dmGrowBufFree(DMGrowBuf *buf)
{
    DM_DBG("dmGrowBufFree(%p)\n", buf);
    if (buf != NULL)
    {
        DM_DBG(
            "      buf->data       = %p\n"
            "      buf->is_const   = %s\n",
            buf->data,
            buf->is_const ? "YES" : "NO");

        if (buf->is_const)
            return;

        dmFree(buf->data);
        dmMemset(buf, 0, sizeof(DMGrowBuf));
    }
}


DMGrowBuf * dmGrowBufCopy(DMGrowBuf *dst, const DMGrowBuf *src, const size_t enlarge)
{
    if (dst == NULL)
        return NULL;

    DM_DBG("dmGrowBufCopy(dst=%p, src=%p, enlarge=%" DM_PRIu_SIZE_T "):\n"
        "    data=%p, size=%" DM_PRIu_SIZE_T ", len=%" DM_PRIu_SIZE_T ", offs=%" DM_PRIu_SIZE_T "\n",
        dst, src, enlarge, src->data, src->size, src->len, src->offs);

    // Copy the struct here
    memcpy(dst, src, sizeof(DMGrowBuf));
    dst->size += enlarge;

    // Allocate new memory for the data
    if ((dst->data = dmMalloc(dst->size)) == NULL)
    {
        // If that fails, clear the struct
        dmMemset(dst, 0, sizeof(DMGrowBuf));
        return NULL;
    }

    // Copy data
    memcpy(dst->data, src->data, src->size);
    if (enlarge > 0)
        dmMemset(dst->data + src->size, 0, enlarge);

    // And reset some struct information
    dst->is_const = FALSE;

    return dst;
}


DMGrowBuf * dmGrowBufConstCopy(DMGrowBuf *dst, const DMGrowBuf *src)
{
    if (dst == NULL)
        return NULL;

    DM_DBG("dmGrowBufConstCopy(dst=%p, src=%p):\n"
        "    data=%p, size=%" DM_PRIu_SIZE_T ", len=%" DM_PRIu_SIZE_T ", offs=%" DM_PRIu_SIZE_T "\n",
        dst, src, src->data, src->size, src->len, src->offs);

    memcpy(dst, src, sizeof(DMGrowBuf));
    dst->is_const = TRUE;

    return dst;
}


DMGrowBuf * dmGrowBufConstCreateFrom(DMGrowBuf *dst, Uint8 *data, const size_t len)
{
    if (dmGrowBufInit(dst) != DMERR_OK)
        return NULL;

    DM_DBG("dmGrowBufConstCreateFrom(dst=%p, data=%p, len=%" DM_PRIu_SIZE_T ")\n",
        dst, data, len);

    dst->data = data;
    dst->len = dst->size = len;
    dst->is_const = TRUE;

    return dst;
}


DMGrowBuf * dmGrowBufConstCopyOffs(DMGrowBuf *dst, const DMGrowBuf *src, const size_t offs)
{
    return dmGrowBufConstCreateFrom(dst, src->data + offs, src->len - offs);
}


DMGrowBuf * dmGrowBufConstCopyOffsSize(DMGrowBuf *dst, const DMGrowBuf *src, const size_t offs, const size_t len)
{
    if (src->len < offs + len)
        return NULL;

    return dmGrowBufConstCreateFrom(dst, src->data + offs, len);
}


DMGrowBuf * dmGrowBufCopyOffs(DMGrowBuf *dst, const DMGrowBuf *src, const size_t offs, const size_t enlarge)
{
    DMGrowBuf tmp;
    return dmGrowBufCopy(dst,
        dmGrowBufConstCreateFrom(&tmp, src->data + offs, src->len - offs), enlarge);
}


DMGrowBuf * dmGrowBufCopyOffsSize(DMGrowBuf *dst, const DMGrowBuf *src, const size_t offs, const size_t len, const size_t enlarge)
{
    DMGrowBuf tmp;

    if (src->len < offs + len)
        return NULL;

    return dmGrowBufCopy(dst,
        dmGrowBufConstCreateFrom(&tmp, src->data + offs, len), enlarge);
}


static BOOL dmGrowBufRealloc(DMGrowBuf *buf, const size_t nsize, const BOOL clear)
{
    DM_DBG("dmGrowBufRealloc(%p):\n"
        "    size=%" DM_PRIu_SIZE_T ", nsize=%" DM_PRIu_SIZE_T
        ", offs=%" DM_PRIu_SIZE_T "\n",
        buf, buf->size, nsize, buf->offs);

    if (buf->is_const)
        return FALSE;

    // Can't be smaller than current size!
    if (nsize < buf->size)
        return FALSE;

    if ((buf->data = dmRealloc(buf->data, nsize)) == NULL)
        return FALSE;

    // For buffers growing backwards, we must move the
    // current data to the end of the buffer ..
    size_t clrsize = nsize - buf->size;
    DM_DBG("    clrsize=%" DM_PRIu_SIZE_T "\n", clrsize);
    if (buf->backwards)
    {
        memmove(buf->data + clrsize, buf->data, buf->size);
        buf->offs += clrsize;
    }

    // Check if we need to clear the newly allocated area?
    if (clear)
    {
        if (buf->backwards)
            dmMemset(buf->data, 0, clrsize);
        else
            dmMemset(buf->data + buf->size, 0, clrsize);
    }

    buf->size = nsize;

    return TRUE;
}


//
// Grow the buffer by "amount" bytes, but at least by buf->mingrow,
// if there is not enough space for at least that amount compared to
// current buffer "len".
//
BOOL dmGrowBufGrow(DMGrowBuf *buf, const size_t amount)
{
    size_t grow = (amount > buf->mingrow) ? amount : buf->mingrow;

    if (buf->is_const)
        return FALSE;

    if (buf->data == NULL ||
        (buf->backwards && amount >= buf->offs) ||
        (!buf->backwards && buf->offs + amount >= buf->size))
    {
        DM_DBG("dmGrowBufGrow(%p, amount=%" DM_PRIu_SIZE_T "): grow=%" DM_PRIu_SIZE_T "\n",
            buf, amount, grow);

        if (!dmGrowBufRealloc(buf, buf->size + grow, TRUE))
            return FALSE;
    }

    buf->len += amount;
    return TRUE;
}


//
// Grow the buffer if "nsize" is larger than the current buffer size.
// Buffer is enlarged to nsize + mingrow.
//
BOOL dmGrowBufCheckGrow(DMGrowBuf *buf, const size_t nsize)
{
    if (buf->is_const)
        return FALSE;

    if (buf->data == NULL || nsize > buf->size)
    {
        if (!dmGrowBufRealloc(buf, nsize + buf->mingrow, TRUE))
            return FALSE;
    }

    if (nsize > buf->len)
        buf->len = nsize;

    return TRUE;
}


BOOL dmGrowBufPut(DMGrowBuf *buf, const Uint8 *data, const size_t len)
{
    if (data == NULL)
        return FALSE;

    if (!dmGrowBufGrow(buf, len))
        return FALSE;

    if (buf->backwards)
    {
        if (buf->literal)
        {
            for (size_t n = 0; n < len; n++)
                buf->data[--buf->offs] = data[len - n];
        }
        else
        {
            for (size_t n = 0; n < len; n++)
                buf->data[--buf->offs] = data[n];
        }
    }
    else
    {
        memcpy(buf->data + buf->offs, data, len);
        buf->offs += len;
    }

    return TRUE;
}


BOOL dmGrowBufPutU8(DMGrowBuf *buf, const Uint8 value)
{
    if (!dmGrowBufGrow(buf, sizeof(Uint8)))
        return FALSE;

    if (buf->backwards)
        buf->data[--buf->offs] = value;
    else
        buf->data[buf->offs++] = value;

    return TRUE;
}


BOOL dmGrowBufPutU16BE(DMGrowBuf *buf, const Uint16 val)
{
    if (buf->literal && buf->backwards)
    {
        return
            dmGrowBufPutU8(buf, val & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 8) & 0xff);
    }
    else
    {
        return
            dmGrowBufPutU8(buf, (val >> 8) & 0xff) &&
            dmGrowBufPutU8(buf, val & 0xff);
    }
}


BOOL dmGrowBufPutU16LE(DMGrowBuf *buf, const Uint16 val)
{
    if (buf->literal && buf->backwards)
    {
        return
            dmGrowBufPutU8(buf, (val >> 8) & 0xff) &&
            dmGrowBufPutU8(buf, val & 0xff);
    }
    else
    {
        return
            dmGrowBufPutU8(buf, val & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 8) & 0xff);
    }
}


BOOL dmGrowBufPutU32BE(DMGrowBuf *buf, const Uint32 val)
{
    if (buf->literal && buf->backwards)
    {
        return
            dmGrowBufPutU8(buf, (val >> 24) & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 16) & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 8) & 0xff) &&
            dmGrowBufPutU8(buf, val & 0xff);
    }
    else
    {
        return
            dmGrowBufPutU8(buf, val & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 8) & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 16) & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 24) & 0xff);
    }
}


BOOL dmGrowBufPutU32LE(DMGrowBuf *buf, const Uint32 val)
{
    if (buf->literal && buf->backwards)
    {
        return
            dmGrowBufPutU8(buf, val & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 8) & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 16) & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 24) & 0xff);
    }
    else
    {
        return
            dmGrowBufPutU8(buf, (val >> 24) & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 16) & 0xff) &&
            dmGrowBufPutU8(buf, (val >> 8) & 0xff) &&
            dmGrowBufPutU8(buf, val & 0xff);
    }
}


BOOL dmGrowBufGetU8(DMGrowBuf *buf, Uint8 *value)
{
    if (buf->backwards)
    {
        if (buf->offs > 0)
            *value = buf->data[--buf->offs];
        else
            return FALSE;
    }
    else
    {
        if (buf->offs < buf->len)
            *value = buf->data[buf->offs++];
        else
            return FALSE;
    }

    return TRUE;
}