1/*
2 * buffer.c -- generic memory buffer .
3 *
4 * Copyright (c) 2001-2008, NLnet Labs. All rights reserved.
5 *
6 * See LICENSE for the license.
7 *
8 */
9/**
10 * \file
11 *
12 * This file contains the definition of sldns_buffer, and functions to manipulate those.
13 */
14#include "config.h"
15#include "sldns/sbuffer.h"
16#include <stdarg.h>
17
18sldns_buffer *
19sldns_buffer_new(size_t capacity)
20{
21	sldns_buffer *buffer = (sldns_buffer*)malloc(sizeof(sldns_buffer));
22
23	if (!buffer) {
24		return NULL;
25	}
26
27	buffer->_data = (uint8_t *) malloc(capacity);
28	if (!buffer->_data) {
29		free(buffer);
30		return NULL;
31	}
32
33	buffer->_position = 0;
34	buffer->_limit = buffer->_capacity = capacity;
35	buffer->_fixed = 0;
36	buffer->_status_err = 0;
37
38	sldns_buffer_invariant(buffer);
39
40	return buffer;
41}
42
43void
44sldns_buffer_new_frm_data(sldns_buffer *buffer, void *data, size_t size)
45{
46	assert(data != NULL);
47
48	buffer->_position = 0;
49	buffer->_limit = buffer->_capacity = size;
50	buffer->_fixed = 0;
51	buffer->_data = malloc(size);
52	if(!buffer->_data) {
53		buffer->_status_err = 1;
54		return;
55	}
56	memcpy(buffer->_data, data, size);
57	buffer->_status_err = 0;
58
59	sldns_buffer_invariant(buffer);
60}
61
62void
63sldns_buffer_init_frm_data(sldns_buffer *buffer, void *data, size_t size)
64{
65	memset(buffer, 0, sizeof(*buffer));
66	buffer->_data = data;
67	buffer->_capacity = buffer->_limit = size;
68	buffer->_fixed = 1;
69}
70
71int
72sldns_buffer_set_capacity(sldns_buffer *buffer, size_t capacity)
73{
74	void *data;
75
76	sldns_buffer_invariant(buffer);
77	assert(buffer->_position <= capacity);
78
79	data = (uint8_t *) realloc(buffer->_data, capacity);
80	if (!data) {
81		buffer->_status_err = 1;
82		return 0;
83	} else {
84		buffer->_data = data;
85		buffer->_limit = buffer->_capacity = capacity;
86		return 1;
87	}
88}
89
90int
91sldns_buffer_reserve(sldns_buffer *buffer, size_t amount)
92{
93	sldns_buffer_invariant(buffer);
94	assert(!buffer->_fixed);
95	if (buffer->_capacity < buffer->_position + amount) {
96		size_t new_capacity = buffer->_capacity * 3 / 2;
97
98		if (new_capacity < buffer->_position + amount) {
99			new_capacity = buffer->_position + amount;
100		}
101		if (!sldns_buffer_set_capacity(buffer, new_capacity)) {
102			buffer->_status_err = 1;
103			return 0;
104		}
105	}
106	buffer->_limit = buffer->_capacity;
107	return 1;
108}
109
110int
111sldns_buffer_printf(sldns_buffer *buffer, const char *format, ...)
112{
113	va_list args;
114	int written = 0;
115	size_t remaining;
116
117	if (sldns_buffer_status_ok(buffer)) {
118		sldns_buffer_invariant(buffer);
119		assert(buffer->_limit == buffer->_capacity);
120
121		remaining = sldns_buffer_remaining(buffer);
122		va_start(args, format);
123		written = vsnprintf((char *) sldns_buffer_current(buffer), remaining,
124				    format, args);
125		va_end(args);
126		if (written == -1) {
127			buffer->_status_err = 1;
128			return -1;
129		} else if ((size_t) written >= remaining) {
130			if (!sldns_buffer_reserve(buffer, (size_t) written + 1)) {
131				buffer->_status_err = 1;
132				return -1;
133			}
134			va_start(args, format);
135			written = vsnprintf((char *) sldns_buffer_current(buffer),
136			    sldns_buffer_remaining(buffer), format, args);
137			va_end(args);
138			if (written == -1) {
139				buffer->_status_err = 1;
140				return -1;
141			}
142		}
143		buffer->_position += written;
144	}
145	return written;
146}
147
148void
149sldns_buffer_free(sldns_buffer *buffer)
150{
151	if (!buffer) {
152		return;
153	}
154
155	if (!buffer->_fixed)
156		free(buffer->_data);
157
158	free(buffer);
159}
160
161void *
162sldns_buffer_export(sldns_buffer *buffer)
163{
164	buffer->_fixed = 1;
165	return buffer->_data;
166}
167
168void
169sldns_buffer_copy(sldns_buffer* result, sldns_buffer* from)
170{
171	size_t tocopy = sldns_buffer_limit(from);
172
173	if(tocopy > sldns_buffer_capacity(result))
174		tocopy = sldns_buffer_capacity(result);
175	sldns_buffer_clear(result);
176	sldns_buffer_write(result, sldns_buffer_begin(from), tocopy);
177	sldns_buffer_flip(result);
178}
179