view src/dmgrowbuf.c @ 1785:86d10d5d4915

Fix case where DMGrowBuf is growing backwards and needs to be reallocated in dmGrowBufRealloc() and the data is moved to the "end" of the newly grown buffer. Previously we used clrsize as data size, but that is (in retrospect) obviously incorrect. Use old buffer size instead.
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 13 Jun 2018 01:39:06 +0300
parents a29d38862037
children 0801fd0e26cb
line wrap: on
line source

/*
 * DMLib
 * -- Growable buffer implementation
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2018 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;

    memset(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;

        dmFreeR(&buf->data);
    }
}


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
        memset(dst, 0, sizeof(DMGrowBuf));
        return NULL;
    }

    // Copy data
    memcpy(dst->data, src->data, src->size);
    if (enlarge > 0)
        memset(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, 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 * 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);
}


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)
            memset(buf->data, 0, clrsize);
        else
            memset(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;
}


static void dmGrowBufUpdate(DMGrowBuf *buf)
{
    if (buf->offs < buf->min_offs)
        buf->min_offs = buf->offs;

    if (buf->offs > buf->max_offs)
        buf->max_offs = buf->offs;
}


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)
        {
            buf->offs -= len;
            memcpy(buf->data + buf->offs, data, len);
        }
        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;
    }

    dmGrowBufUpdate(buf);

    return TRUE;
}


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

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

    dmGrowBufUpdate(buf);

    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;
}