1238104Sdes/*
2238104Sdes * buffer.c -- generic memory buffer .
3238104Sdes *
4238104Sdes * Copyright (c) 2001-2008, NLnet Labs. All rights reserved.
5238104Sdes *
6238104Sdes * See LICENSE for the license.
7238104Sdes *
8238104Sdes */
9238104Sdes
10238104Sdes#include <ldns/config.h>
11238104Sdes
12238104Sdes#include <ldns/ldns.h>
13238104Sdes#include <ldns/buffer.h>
14238104Sdes
15238104Sdesldns_buffer *
16238104Sdesldns_buffer_new(size_t capacity)
17238104Sdes{
18238104Sdes	ldns_buffer *buffer = LDNS_MALLOC(ldns_buffer);
19238104Sdes
20238104Sdes	if (!buffer) {
21238104Sdes		return NULL;
22238104Sdes	}
23238104Sdes
24238104Sdes	buffer->_data = (uint8_t *) LDNS_XMALLOC(uint8_t, capacity);
25238104Sdes	if (!buffer->_data) {
26238104Sdes		LDNS_FREE(buffer);
27238104Sdes		return NULL;
28238104Sdes	}
29238104Sdes
30238104Sdes	buffer->_position = 0;
31238104Sdes	buffer->_limit = buffer->_capacity = capacity;
32238104Sdes	buffer->_fixed = 0;
33238104Sdes	buffer->_status = LDNS_STATUS_OK;
34238104Sdes
35238104Sdes	ldns_buffer_invariant(buffer);
36238104Sdes
37238104Sdes	return buffer;
38238104Sdes}
39238104Sdes
40238104Sdesvoid
41238104Sdesldns_buffer_new_frm_data(ldns_buffer *buffer, void *data, size_t size)
42238104Sdes{
43238104Sdes	assert(data != NULL);
44238104Sdes
45238104Sdes	buffer->_position = 0;
46238104Sdes	buffer->_limit = buffer->_capacity = size;
47238104Sdes	buffer->_fixed = 0;
48238104Sdes	buffer->_data = LDNS_XMALLOC(uint8_t, size);
49238104Sdes	if(!buffer->_data) {
50238104Sdes		buffer->_status = LDNS_STATUS_MEM_ERR;
51238104Sdes		return;
52238104Sdes	}
53238104Sdes	memcpy(buffer->_data, data, size);
54238104Sdes	buffer->_status = LDNS_STATUS_OK;
55238104Sdes
56238104Sdes	ldns_buffer_invariant(buffer);
57238104Sdes}
58238104Sdes
59238104Sdesbool
60238104Sdesldns_buffer_set_capacity(ldns_buffer *buffer, size_t capacity)
61238104Sdes{
62238104Sdes	void *data;
63238104Sdes
64238104Sdes	ldns_buffer_invariant(buffer);
65238104Sdes	assert(buffer->_position <= capacity);
66238104Sdes
67238104Sdes	data = (uint8_t *) LDNS_XREALLOC(buffer->_data, uint8_t, capacity);
68238104Sdes	if (!data) {
69238104Sdes		buffer->_status = LDNS_STATUS_MEM_ERR;
70238104Sdes		return false;
71238104Sdes	} else {
72238104Sdes		buffer->_data = data;
73238104Sdes		buffer->_limit = buffer->_capacity = capacity;
74238104Sdes		return true;
75238104Sdes	}
76238104Sdes}
77238104Sdes
78238104Sdesbool
79238104Sdesldns_buffer_reserve(ldns_buffer *buffer, size_t amount)
80238104Sdes{
81238104Sdes	ldns_buffer_invariant(buffer);
82238104Sdes	assert(!buffer->_fixed);
83238104Sdes	if (buffer->_capacity < buffer->_position + amount) {
84238104Sdes		size_t new_capacity = buffer->_capacity * 3 / 2;
85238104Sdes
86238104Sdes		if (new_capacity < buffer->_position + amount) {
87238104Sdes			new_capacity = buffer->_position + amount;
88238104Sdes		}
89238104Sdes		if (!ldns_buffer_set_capacity(buffer, new_capacity)) {
90238104Sdes			buffer->_status = LDNS_STATUS_MEM_ERR;
91238104Sdes			return false;
92238104Sdes		}
93238104Sdes	}
94238104Sdes	buffer->_limit = buffer->_capacity;
95238104Sdes	return true;
96238104Sdes}
97238104Sdes
98238104Sdesint
99238104Sdesldns_buffer_printf(ldns_buffer *buffer, const char *format, ...)
100238104Sdes{
101238104Sdes	va_list args;
102238104Sdes	int written = 0;
103238104Sdes	size_t remaining;
104238104Sdes
105238104Sdes	if (ldns_buffer_status_ok(buffer)) {
106238104Sdes		ldns_buffer_invariant(buffer);
107238104Sdes		assert(buffer->_limit == buffer->_capacity);
108238104Sdes
109238104Sdes		remaining = ldns_buffer_remaining(buffer);
110238104Sdes		va_start(args, format);
111238104Sdes		written = vsnprintf((char *) ldns_buffer_current(buffer), remaining,
112238104Sdes				    format, args);
113238104Sdes		va_end(args);
114238104Sdes		if (written == -1) {
115238104Sdes			buffer->_status = LDNS_STATUS_INTERNAL_ERR;
116238104Sdes			return -1;
117238104Sdes		} else if ((size_t) written >= remaining) {
118238104Sdes			if (!ldns_buffer_reserve(buffer, (size_t) written + 1)) {
119238104Sdes				buffer->_status = LDNS_STATUS_MEM_ERR;
120238104Sdes				return -1;
121238104Sdes			}
122238104Sdes			va_start(args, format);
123238104Sdes			written = vsnprintf((char *) ldns_buffer_current(buffer),
124238104Sdes			    ldns_buffer_remaining(buffer), format, args);
125238104Sdes			va_end(args);
126238104Sdes			if (written == -1) {
127238104Sdes				buffer->_status = LDNS_STATUS_INTERNAL_ERR;
128238104Sdes				return -1;
129238104Sdes			}
130238104Sdes		}
131238104Sdes		buffer->_position += written;
132238104Sdes	}
133238104Sdes	return written;
134238104Sdes}
135238104Sdes
136238104Sdesvoid
137238104Sdesldns_buffer_free(ldns_buffer *buffer)
138238104Sdes{
139238104Sdes	if (!buffer) {
140238104Sdes		return;
141238104Sdes	}
142238104Sdes
143246854Sdes	if (!buffer->_fixed)
144246854Sdes		LDNS_FREE(buffer->_data);
145238104Sdes
146238104Sdes	LDNS_FREE(buffer);
147238104Sdes}
148238104Sdes
149238104Sdesvoid *
150238104Sdesldns_buffer_export(ldns_buffer *buffer)
151238104Sdes{
152238104Sdes	buffer->_fixed = 1;
153238104Sdes	return buffer->_data;
154238104Sdes}
155238104Sdes
156238104Sdesint
157238104Sdesldns_bgetc(ldns_buffer *buffer)
158238104Sdes{
159238104Sdes	if (!ldns_buffer_available_at(buffer, buffer->_position, sizeof(uint8_t))) {
160238104Sdes		ldns_buffer_set_position(buffer, ldns_buffer_limit(buffer));
161238104Sdes		/* ldns_buffer_rewind(buffer);*/
162238104Sdes		return EOF;
163238104Sdes	}
164238104Sdes	return (int)ldns_buffer_read_u8(buffer);
165238104Sdes}
166238104Sdes
167238104Sdesvoid
168238104Sdesldns_buffer_copy(ldns_buffer* result, ldns_buffer* from)
169238104Sdes{
170238104Sdes	size_t tocopy = ldns_buffer_limit(from);
171238104Sdes
172238104Sdes	if(tocopy > ldns_buffer_capacity(result))
173238104Sdes		tocopy = ldns_buffer_capacity(result);
174238104Sdes	ldns_buffer_clear(result);
175238104Sdes	ldns_buffer_write(result, ldns_buffer_begin(from), tocopy);
176238104Sdes	ldns_buffer_flip(result);
177238104Sdes}
178