getdns/src/gldns/gbuffer.c

180 lines
3.7 KiB
C

/*
* buffer.c -- generic memory buffer .
*
* Copyright (c) 2001-2008, NLnet Labs. All rights reserved.
*
* See LICENSE for the license.
*
*/
/**
* \file
*
* This file contains the definition of gldns_buffer, and functions to manipulate those.
*/
#include "config.h"
#include "gldns/gbuffer.h"
#include <stdarg.h>
gldns_buffer *
gldns_buffer_new(size_t capacity)
{
gldns_buffer *buffer = (gldns_buffer*)malloc(sizeof(gldns_buffer));
if (!buffer) {
return NULL;
}
buffer->_data = (uint8_t *) malloc(capacity);
if (!buffer->_data) {
free(buffer);
return NULL;
}
buffer->_position = 0;
buffer->_limit = buffer->_capacity = capacity;
buffer->_fixed = 0;
buffer->_status_err = 0;
gldns_buffer_invariant(buffer);
return buffer;
}
void
gldns_buffer_new_frm_data(gldns_buffer *buffer, void *data, size_t size)
{
assert(data != NULL);
buffer->_position = 0;
buffer->_limit = buffer->_capacity = size;
buffer->_fixed = 0;
buffer->_data = malloc(size);
if(!buffer->_data) {
buffer->_status_err = 1;
return;
}
memcpy(buffer->_data, data, size);
buffer->_status_err = 0;
gldns_buffer_invariant(buffer);
}
void
gldns_buffer_init_frm_data(gldns_buffer *buffer, void *data, size_t size)
{
memset(buffer, 0, sizeof(*buffer));
buffer->_data = data;
buffer->_capacity = buffer->_limit = size;
buffer->_fixed = 1;
}
int
gldns_buffer_set_capacity(gldns_buffer *buffer, size_t capacity)
{
void *data;
gldns_buffer_invariant(buffer);
assert(buffer->_position <= capacity && !buffer->_fixed);
data = (uint8_t *) realloc(buffer->_data, capacity);
if (!data) {
buffer->_status_err = 1;
return 0;
} else {
buffer->_data = data;
buffer->_limit = buffer->_capacity = capacity;
return 1;
}
}
int
gldns_buffer_reserve(gldns_buffer *buffer, size_t amount)
{
gldns_buffer_invariant(buffer);
assert(!buffer->_fixed);
if (buffer->_capacity < buffer->_position + amount) {
size_t new_capacity = buffer->_capacity * 3 / 2;
if (new_capacity < buffer->_position + amount) {
new_capacity = buffer->_position + amount;
}
if (!gldns_buffer_set_capacity(buffer, new_capacity)) {
buffer->_status_err = 1;
return 0;
}
}
buffer->_limit = buffer->_capacity;
return 1;
}
int
gldns_buffer_printf(gldns_buffer *buffer, const char *format, ...)
{
va_list args;
int written = 0;
size_t remaining;
if (gldns_buffer_status_ok(buffer)) {
gldns_buffer_invariant(buffer);
assert(buffer->_limit == buffer->_capacity);
remaining = gldns_buffer_remaining(buffer);
va_start(args, format);
written = _gldns_vsnprintf((char*)gldns_buffer_current(buffer),
remaining, format, args);
va_end(args);
if (written == -1) {
buffer->_status_err = 1;
return -1;
} else if (!buffer->_fixed && (size_t) written >= remaining) {
if (!gldns_buffer_reserve(buffer, (size_t) written + 1)) {
buffer->_status_err = 1;
return -1;
}
va_start(args, format);
written = _gldns_vsnprintf(
(char *) gldns_buffer_current(buffer),
gldns_buffer_remaining(buffer), format, args);
va_end(args);
if (written == -1) {
buffer->_status_err = 1;
return -1;
}
}
buffer->_position += written;
}
return written;
}
void
gldns_buffer_free(gldns_buffer *buffer)
{
if (!buffer) {
return;
}
if (!buffer->_fixed)
free(buffer->_data);
free(buffer);
}
void *
gldns_buffer_export(gldns_buffer *buffer)
{
buffer->_fixed = 1;
return buffer->_data;
}
void
gldns_buffer_copy(gldns_buffer* result, gldns_buffer* from)
{
size_t tocopy = gldns_buffer_limit(from);
if(tocopy > gldns_buffer_capacity(result))
tocopy = gldns_buffer_capacity(result);
gldns_buffer_clear(result);
gldns_buffer_write(result, gldns_buffer_begin(from), tocopy);
gldns_buffer_flip(result);
}