/* $Id: buffer.c,v 1.6 1999/06/25 06:15:18 levitte Exp $ */

#include "gnu_extras.h"

#include <string.h>
#include "util.h"
#include "buffer.h"

general_buffer *buf_init(unsigned int n)
{
    general_buffer *buf = xmalloc(sizeof(struct genbuf));
    buf->bytes = 0;
    buf->amount = 0;
    buf->space = 0;
    buf->is_static = 0;

    buf_expand_by(buf, n);
    return buf;
}

general_buffer *buf_init_static(general_buffer *buf, void *b, unsigned int n)
{
    buf->bytes = b;
    buf->amount = 0;
    buf->space = n;
    buf->is_static = 1;

    return buf;
}

void buf_destroy(general_buffer *buf)
{
    if (buf->is_static)
	return;
    if (buf->bytes) {
	memset(buf->bytes, 0, buf->amount);
	xfree(buf->bytes);
    }
    xfree(buf);
}

/* I cheat a little and expand in chunks of 1024 bytes.
   That should be enough */

int buf_expand_to(general_buffer *buf, unsigned int n)
{
    unsigned int i = 0;
    
    if (buf->is_static)
	return 0;

    while (n > buf->space + i)
	i += 1024;

    if (i > 0)
	if (buf->bytes == 0)
	    buf->bytes = xmalloc(buf->space + i);
	else
	    buf->bytes = xrealloc(buf->bytes, buf->space + i);
    if (buf->bytes == 0)
	return 0;

    buf->space += i;

    memset((unsigned char *)(buf->bytes) + buf->amount, 0,
	   buf->space - buf->amount);

    return 1;
}

int buf_expand_by(general_buffer *buf, unsigned int n)
{
    return buf_expand_to(buf, buf->amount + n);
}

/* We may want to adjust the amount of bytes */
void buf_adjust_amount_by(general_buffer *buf, unsigned int n)
{
    buf->amount += n;
}

void buf_adjust_amount_to(general_buffer *buf, unsigned int n)
{
    buf->amount = n;
}

/* Fetch internal data */
unsigned char *buf_chars_noadjust(general_buffer *buf)
{
    return buf->bytes;
}

char *buf_chars(general_buffer *buf)
{
    unsigned char *b;

    /* Let's make sure there's a NUL at the end */
    buf_expand_by(buf, 1);
    b = buf_chars_noadjust(buf);
    b[buf->amount] = 0;

    return (char *)b;
}

void *buf_bytes(general_buffer *buf)
{
    return buf->bytes;
}

unsigned int buf_amount(general_buffer *buf)
{
    return buf->amount;
}

unsigned int buf_remaining(general_buffer *buf)
{
    return buf->space - buf->amount;
}

/* Append any kind of bytes to the buffer */
int buf_append_bytes(general_buffer *buf, void *b, int n)
{
    if (!buf_expand_by(buf, n))
	return 0;
    memmove(buf_chars_noadjust(buf)+buf->amount, b, n);
    buf->amount += n;
    return 1;
}

/* Append a NUL-terminated string to the buffer */
int buf_append_chars(general_buffer *buf, char *b)
{
    unsigned int n = strlen(b);
    if (!buf_append_int(buf, n))
	return 0;
    if (!buf_append_bytes(buf, b, n))
	return 0;

    return 1;
}

int buf_append_chars_nocount(general_buffer *buf, char *b)
{
    unsigned int n = strlen(b);
    if (!buf_append_bytes(buf, b, n+1))
	return 0;
    buf->amount--;

    return 1;
}

/* Append an integer (longword) */
int buf_append_int(general_buffer *buf, unsigned long b)
{
    unsigned int i = sizeof b;
    if (!buf_expand_by(buf, i))
	return 0;
    while (i-- > 0) {
	buf_chars_noadjust(buf)[buf->amount + i] = b & 0xff; b >>= 8;
    }
    buf->amount += sizeof b;
    return 1;
}

/* Append an integer (word) */
int buf_append_word(general_buffer *buf, unsigned short b)
{
    unsigned int i = sizeof b;
    if (!buf_expand_by(buf, i))
	return 0;
    while (i-- > 0) {
	buf_chars_noadjust(buf)[buf->amount + i] = b & 0xff; b >>= 8;
    }
    buf->amount += sizeof b;
    return 1;
}

/* Append a bignum */
int buf_append_bn(general_buffer *buf, BIGNUM *b)
{
    unsigned int len = BN_num_bits(b);
    short len2 = (short)((len + 7) / 8);
    if (!buf_append_word(buf, len))
	return 0;
    if (!buf_expand_by(buf, len2))
	return 0;
    BN_bn2bin(b, (unsigned char *)buf->bytes+buf->amount);
    buf->amount += len2;
    return 1;
}

int buf_chop_at(general_buffer *buf, unsigned int n)
{
    if (buf_amount(buf) > n)
	buf->amount = n;
    return 1;
}



general_buffer_reader *bufread_init(general_buffer *buf)
{
    general_buffer_reader *br = xmalloc(sizeof(struct gbread));
    br->buf = buf;
    br->pos = 0;
    br->is_static = 0;

    return br;
}
    
general_buffer_reader *bufread_init_static(general_buffer_reader *br,
					   general_buffer *buf)
{
    br->buf = buf;
    br->pos = 0;
    br->is_static = 1;

    return br;
}
    
void bufread_destroy(general_buffer_reader *br)
{
    if (!br->is_static)
	xfree(br);
}

general_buffer *bufread_buffer(general_buffer_reader *br)
{
    return br->buf;
}

unsigned int bufread_position(general_buffer_reader *br)
{
    return br->pos;
}

unsigned char *bufread_chars_noadjust(general_buffer_reader *br)
{
    return buf_chars_noadjust(bufread_buffer(br)) + bufread_position(br);
}

unsigned int bufread_remaining(general_buffer_reader *br)
{
    return buf_amount(br->buf) - bufread_position(br);
}

int bufread_int(general_buffer_reader *br, unsigned long *l)
{
    unsigned int i = sizeof(long);

    *l = 0;
    if (bufread_remaining(br) < i)
	return 0;
    while(i-- > 0) {
	*l = (*l << 8) | *bufread_chars_noadjust(br);
	br->pos++;
    }
    return 1;
}

int bufread_word(general_buffer_reader *br, unsigned short *l)
{
    unsigned int i = sizeof(short);

    *l = 0;
    if (bufread_remaining(br) < i)
	return 0;
    while(i-- > 0) {
	*l = (*l << 8) | *bufread_chars_noadjust(br);
	br->pos++;
    }
    return 1;
}

int bufread_bytes(general_buffer_reader *br, void *b, int n)
{
    if (bufread_remaining(br) < n)
	return 0;
    memmove(b, bufread_chars_noadjust(br), n);
    br->pos += n;
    return 1;
}

int bufread_strdup(general_buffer_reader *br, char **result)
{
    unsigned long l;

    if (!bufread_int(br, &l))
	return 0;
    *result = xmalloc(l+1);
    if (*result == 0)
	return 0;
    if (!bufread_bytes(br, *result, l)) {
	xfree(*result);
	return 0;
    }
    (*result)[l] = '\0';
    return 1;
}

int bufread_bn(general_buffer_reader *br, BIGNUM **bn)
{
    unsigned short l, l2;
    if (!bufread_word(br, &l))
	return 0;
    l2 = (l+7)/8;
    if (bufread_remaining(br) < l2)
	return 0;
    *bn = BN_bin2bn(bufread_chars_noadjust(br), l2, NULL);
    if (! *bn)
	return 0;
    br->pos += l2;
    return 1;
}

int bufread_glob(general_buffer_reader *br, unsigned int n)
{
    if (bufread_remaining(br) < n)
	return 0;
    br->pos += n;
    return 1;
}
/* Emacs local variables

Local variables:
eval: (set-c-style "BSD")
end:

*/
