/* Copyright (C) 2007-2008 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
*/
#include "cbuffer.h"
#include "android_utils.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>

#define  DEBUG  0

#if DEBUG
#  define  ASSERT(cond,fmt,...)  ({ if (!(cond)) { fprintf(stderr, fmt, __VA_ARGS__); assert(cond); } })
#else
#  define  ASSERT(cond,fmt,...)  ((void)0)
#endif

#if DEBUG
void
cbuffer_assert( CBuffer*  cb, const char*  file, long  lineno )
{
    const char*  reason = NULL;

    if (cb->rpos < 0 || cb->rpos >= cb->size) {
        reason = "rpos is out of bounds";
    }
    else if (cb->count < 0 || cb->count > cb->size) {
        reason = "count is incorrect";
    }
    if (!reason)
        return;

    fprintf(stderr, "assert:%s:%ld: assertion failed: %s (pos=%d count=%d size=%d)\n",
            file, lineno, reason, cb->rpos, cb->count, cb->size);
    assert(0);
}
#  define  CBUFFER_ASSERT(cb)  cbuffer_assert(cb,__FUNCTION__,__LINE__)
#else
#  define  CBUFFER_ASSERT(cb)  ((void)0)
#endif

int
cbuffer_write_peek( CBuffer*  cb, uint8_t*  *pbase )
{
    int  wpos  = cb->rpos + cb->count;
    int  avail = cb->size - cb->count;

    CBUFFER_ASSERT(cb);

    if (wpos >= cb->size)
        wpos -= cb->size;

    if (wpos + avail > cb->size)
        avail = cb->size - wpos;

    *pbase = cb->buff + wpos;
    return avail;
}

void
cbuffer_write_step( CBuffer*  cb, int  len )
{
    CBUFFER_ASSERT(cb);

    cb->count += len;
    if (cb->count > cb->size)
        cb->count = cb->size;
}


int
cbuffer_write( CBuffer*  cb, const void*  from, int  len )
{
    int  len2 = len;

    CBUFFER_ASSERT(cb);

    while (len2 > 0) {
        int  avail = cb->size - cb->count;
        int  wpos  = cb->rpos + cb->count;

        ASSERT(avail >= 0, "avail is negative: %d", avail);

        if (avail == 0)
            break;

        if (wpos >= cb->size)
            wpos -= cb->size;

        ASSERT( wpos >= 0 && wpos < cb->size, "wpos is out-of-bounds: %d (rpos=%d)", wpos, cb->rpos);

        if (wpos + avail > cb->size)
            avail = cb->size - wpos;

        if (avail > len2)
            avail = len2;

        memcpy( cb->buff + wpos, (const char*)from, avail );

        from  = (char*)from + avail;
        len2 -= avail;
        cb->count += avail;
    }
    return len - len2;
}

int
cbuffer_read( CBuffer*  cb, void*  to, int  len )
{
    int   len2 = len;

    CBUFFER_ASSERT(cb);

    while (len2 > 0) {
        int  avail = cb->count;
        int  rpos = cb->rpos;

        ASSERT(avail >= 0, "avail is negative: %d", avail);

        if (avail == 0)
            break;

        ASSERT((rpos >= 0 && rpos < cb->size), "rpos is out-of-bounds: %d", rpos);

        if (rpos+avail > cb->size)
            avail = cb->size - rpos;

        if (avail > len2)
            avail = len2;

        memcpy( (char*)to, (const char*)cb->buff + rpos, avail );
        to    = (char*)to + avail;
        len2 -= avail;
        cb->count -= avail;
        cb->rpos  += avail;
        if (cb->rpos >= cb->size)
            cb->rpos -= cb->size;
    }
    return len - len2;
}

int
cbuffer_read_peek( CBuffer*  cb, uint8_t*  *pbase )
{
    int   rpos  = cb->rpos;
    int   avail = cb->count;

    CBUFFER_ASSERT(cb);

    if (rpos + avail > cb->size)
        avail = cb->size - rpos;

    *pbase = cb->buff + rpos;
    return avail;
}


void
cbuffer_read_step( CBuffer*  cb, int  len )
{
    CBUFFER_ASSERT(cb);

    if (len > cb->count)
        len = cb->count;

    cb->rpos  += len;
    if (cb->rpos >= cb->size)
        cb->rpos -= cb->size;

    cb->count -= len;
}

const char*
cbuffer_quote( CBuffer*  cb )
{
    STRALLOC_DEFINE(s);
    char* q;

    stralloc_format( s, "cbuffer %p (pos=%d count=%d size=%d)",
                     cb, cb->rpos, cb->count, cb->size );

    q = tempstr_from_stralloc( s );
    stralloc_reset(s);

    return q;
}

const char*
cbuffer_quote_data( CBuffer*  cb )
{
    STRALLOC_DEFINE(s);
    int   len  = cb->count;
    int   rpos = cb->rpos;
    char* result;

    while (len > 0) {
        int  avail = len;

        if (rpos >= cb->size)
            rpos -= cb->size;

        if (rpos + avail > cb->size)
            avail = cb->size - rpos;

        stralloc_add_quote_bytes( s, cb->buff + rpos, avail );
        rpos += avail;
        len  -= avail;
    }

    result = tempstr_from_stralloc(s);
    stralloc_reset(s);

    return result;
}

void
cbuffer_print( CBuffer*  cb )
{
    /* print the content of a cbuffer */
    printf( "%s: %s", cbuffer_quote(cb), cbuffer_quote_data(cb) );
}

