1266077Sdes/*
2266077Sdes * buffer.c -- generic memory buffer .
3266077Sdes *
4266077Sdes * Copyright (c) 2001-2008, NLnet Labs. All rights reserved.
5266077Sdes *
6266077Sdes * See LICENSE for the license.
7266077Sdes *
8266077Sdes */
9266077Sdes/**
10266077Sdes * \file
11266077Sdes *
12266077Sdes * This file contains the definition of sldns_buffer, and functions to manipulate those.
13266077Sdes */
14266077Sdes#include "config.h"
15287915Sdes#include "sldns/sbuffer.h"
16266077Sdes#include <stdarg.h>
17266077Sdes
18266077Sdessldns_buffer *
19266077Sdessldns_buffer_new(size_t capacity)
20266077Sdes{
21266077Sdes	sldns_buffer *buffer = (sldns_buffer*)malloc(sizeof(sldns_buffer));
22266077Sdes
23266077Sdes	if (!buffer) {
24266077Sdes		return NULL;
25266077Sdes	}
26266077Sdes
27266077Sdes	buffer->_data = (uint8_t *) malloc(capacity);
28266077Sdes	if (!buffer->_data) {
29266077Sdes		free(buffer);
30266077Sdes		return NULL;
31266077Sdes	}
32266077Sdes
33266077Sdes	buffer->_position = 0;
34266077Sdes	buffer->_limit = buffer->_capacity = capacity;
35266077Sdes	buffer->_fixed = 0;
36266077Sdes	buffer->_status_err = 0;
37266077Sdes
38266077Sdes	sldns_buffer_invariant(buffer);
39266077Sdes
40266077Sdes	return buffer;
41266077Sdes}
42266077Sdes
43266077Sdesvoid
44266077Sdessldns_buffer_new_frm_data(sldns_buffer *buffer, void *data, size_t size)
45266077Sdes{
46266077Sdes	assert(data != NULL);
47266077Sdes
48266077Sdes	buffer->_position = 0;
49266077Sdes	buffer->_limit = buffer->_capacity = size;
50266077Sdes	buffer->_fixed = 0;
51266077Sdes	buffer->_data = malloc(size);
52266077Sdes	if(!buffer->_data) {
53266077Sdes		buffer->_status_err = 1;
54266077Sdes		return;
55266077Sdes	}
56266077Sdes	memcpy(buffer->_data, data, size);
57266077Sdes	buffer->_status_err = 0;
58266077Sdes
59266077Sdes	sldns_buffer_invariant(buffer);
60266077Sdes}
61266077Sdes
62266077Sdesvoid
63266077Sdessldns_buffer_init_frm_data(sldns_buffer *buffer, void *data, size_t size)
64266077Sdes{
65266077Sdes	memset(buffer, 0, sizeof(*buffer));
66266077Sdes	buffer->_data = data;
67266077Sdes	buffer->_capacity = buffer->_limit = size;
68266077Sdes	buffer->_fixed = 1;
69266077Sdes}
70266077Sdes
71266077Sdesint
72266077Sdessldns_buffer_set_capacity(sldns_buffer *buffer, size_t capacity)
73266077Sdes{
74266077Sdes	void *data;
75266077Sdes
76266077Sdes	sldns_buffer_invariant(buffer);
77266077Sdes	assert(buffer->_position <= capacity);
78266077Sdes
79266077Sdes	data = (uint8_t *) realloc(buffer->_data, capacity);
80266077Sdes	if (!data) {
81266077Sdes		buffer->_status_err = 1;
82266077Sdes		return 0;
83266077Sdes	} else {
84266077Sdes		buffer->_data = data;
85266077Sdes		buffer->_limit = buffer->_capacity = capacity;
86266077Sdes		return 1;
87266077Sdes	}
88266077Sdes}
89266077Sdes
90266077Sdesint
91266077Sdessldns_buffer_reserve(sldns_buffer *buffer, size_t amount)
92266077Sdes{
93266077Sdes	sldns_buffer_invariant(buffer);
94266077Sdes	assert(!buffer->_fixed);
95266077Sdes	if (buffer->_capacity < buffer->_position + amount) {
96266077Sdes		size_t new_capacity = buffer->_capacity * 3 / 2;
97266077Sdes
98266077Sdes		if (new_capacity < buffer->_position + amount) {
99266077Sdes			new_capacity = buffer->_position + amount;
100266077Sdes		}
101266077Sdes		if (!sldns_buffer_set_capacity(buffer, new_capacity)) {
102266077Sdes			buffer->_status_err = 1;
103266077Sdes			return 0;
104266077Sdes		}
105266077Sdes	}
106266077Sdes	buffer->_limit = buffer->_capacity;
107266077Sdes	return 1;
108266077Sdes}
109266077Sdes
110266077Sdesint
111266077Sdessldns_buffer_printf(sldns_buffer *buffer, const char *format, ...)
112266077Sdes{
113266077Sdes	va_list args;
114266077Sdes	int written = 0;
115266077Sdes	size_t remaining;
116266077Sdes
117266077Sdes	if (sldns_buffer_status_ok(buffer)) {
118266077Sdes		sldns_buffer_invariant(buffer);
119266077Sdes		assert(buffer->_limit == buffer->_capacity);
120266077Sdes
121266077Sdes		remaining = sldns_buffer_remaining(buffer);
122266077Sdes		va_start(args, format);
123266077Sdes		written = vsnprintf((char *) sldns_buffer_current(buffer), remaining,
124266077Sdes				    format, args);
125266077Sdes		va_end(args);
126266077Sdes		if (written == -1) {
127266077Sdes			buffer->_status_err = 1;
128266077Sdes			return -1;
129266077Sdes		} else if ((size_t) written >= remaining) {
130266077Sdes			if (!sldns_buffer_reserve(buffer, (size_t) written + 1)) {
131266077Sdes				buffer->_status_err = 1;
132266077Sdes				return -1;
133266077Sdes			}
134266077Sdes			va_start(args, format);
135266077Sdes			written = vsnprintf((char *) sldns_buffer_current(buffer),
136266077Sdes			    sldns_buffer_remaining(buffer), format, args);
137266077Sdes			va_end(args);
138266077Sdes			if (written == -1) {
139266077Sdes				buffer->_status_err = 1;
140266077Sdes				return -1;
141266077Sdes			}
142266077Sdes		}
143266077Sdes		buffer->_position += written;
144266077Sdes	}
145266077Sdes	return written;
146266077Sdes}
147266077Sdes
148266077Sdesvoid
149266077Sdessldns_buffer_free(sldns_buffer *buffer)
150266077Sdes{
151266077Sdes	if (!buffer) {
152266077Sdes		return;
153266077Sdes	}
154266077Sdes
155266077Sdes	if (!buffer->_fixed)
156266077Sdes		free(buffer->_data);
157266077Sdes
158266077Sdes	free(buffer);
159266077Sdes}
160266077Sdes
161266077Sdesvoid *
162266077Sdessldns_buffer_export(sldns_buffer *buffer)
163266077Sdes{
164266077Sdes	buffer->_fixed = 1;
165266077Sdes	return buffer->_data;
166266077Sdes}
167266077Sdes
168266077Sdesvoid
169266077Sdessldns_buffer_copy(sldns_buffer* result, sldns_buffer* from)
170266077Sdes{
171266077Sdes	size_t tocopy = sldns_buffer_limit(from);
172266077Sdes
173266077Sdes	if(tocopy > sldns_buffer_capacity(result))
174266077Sdes		tocopy = sldns_buffer_capacity(result);
175266077Sdes	sldns_buffer_clear(result);
176266077Sdes	sldns_buffer_write(result, sldns_buffer_begin(from), tocopy);
177266077Sdes	sldns_buffer_flip(result);
178266077Sdes}
179