1 /*	$OpenBSD: bytebuf.c,v 1.7 2015/01/19 01:48:59 deraadt Exp $ */
2 /*-
3  * Copyright (c) 2009 Internet Initiative Japan Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 /**@file
28  * bytebuffer provides 'byte buffer' helper methods.
29  *
30  * Example:<pre>
31  *	bytebuffer *buf = bytebuffer_create(BUFSIZ);
32  *	int sz = read(STDIN_FILENO, bytebuffer_pointer(buf),
33  *	    bytebuffer_remaining(buf));
34  *	if (sz > 0) {
35  *	    bytebuffer_put(buf, BYTEBUFFER_PUT_DIRECT, sz);
36  *	    bytebuffer_flip(buf);
37  *
38  *	    sz = write(STDOUT_FILENO, bytebuffer_pointer(buf),
39  *		bytebuffer_remaining(buf));
40  *	    bytebuffer_compact(buf);
41  *	}</pre>
42  *
43  * @author Yasuoka Masahiko
44  * $Id: bytebuf.c,v 1.7 2015/01/19 01:48:59 deraadt Exp $
45  */
46 #include <stdlib.h>
47 #include <string.h>
48 #include <errno.h>
49 
50 #ifdef	BYTEBUF_DEBUG
51 #include <stdio.h>
52 #endif
53 
54 #ifndef	BYTEBUF_ASSERT
55 #ifdef	BYTEBUF_DEBUG
56 #define	BYTEBUF_ASSERT(cond)						\
57 	if (!(cond)) {							\
58 		fprintf(stderr,						\
59 		    "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"	\
60 		    , __func__, __FILE__, __LINE__);			\
61 		abort();						\
62 	}
63 #else
64 #define	BYTEBUF_ASSERT(cond)
65 #endif
66 #endif
67 
68 #include "bytebuf.h"
69 
70 struct _bytebuffer {
71 	/** current position */
72 	int	position;
73 	/** current limit */
74 	int	limit;
75 	/** position mark*/
76 	int	mark;
77 	/** capacity of buffer */
78 	size_t	capacity;
79 	/** allocated memory area */
80 	void	*data;
81 };
82 
83 /**
84  * Create a bytebuffer and allocate memory area.
85  *
86  * @param	capacity	Capacity of allocating memory.  If zero or
87  *		    negative value is specified, this function don't allocate
88  *		    memory.
89  */
90 bytebuffer *
bytebuffer_create(size_t capacity)91 bytebuffer_create(size_t capacity)
92 {
93 	bytebuffer *_this = NULL;
94 
95 	if ((_this = calloc(1, sizeof(bytebuffer))) == NULL)
96 		return NULL;
97 
98 	if (capacity > 0) {
99 		if ((_this->data = calloc(1, capacity)) == NULL)
100 			goto fail;
101 		_this->capacity = capacity;
102 	} else
103 		_this->capacity = 0;
104 
105 	_this->limit = _this->capacity;
106 	_this->mark = -1;
107 	return _this;
108 fail:
109 	if (_this != NULL)
110 		free(_this);
111 	return NULL;
112 }
113 
114 /**
115  * Create a bytebuffer using existing memory area.  This memory area will
116  * be freed by bytebuffer's destructor.
117  *
118  * @data		the pointer to existing memory area
119  * @param capacity	capacity of 'data'.
120  */
121 bytebuffer *
bytebuffer_wrap(void * data,size_t capacity)122 bytebuffer_wrap(void *data, size_t capacity)
123 {
124 	bytebuffer *_this;
125 
126 	if ((_this = bytebuffer_create(0)) == NULL)
127 		return NULL;
128 
129 	_this->data = data;
130 	_this->capacity = capacity;
131 	_this->mark = -1;
132 
133 	return _this;
134 }
135 
136 /**
137  * Unwrap memory from bytebuffer.
138  *
139  * @param _this		the bytebuffer object.
140  */
141 void *
bytebuffer_unwrap(bytebuffer * _this)142 bytebuffer_unwrap(bytebuffer *_this)
143 {
144 	void *rval;
145 
146 	rval = _this->data;
147 	_this->data = NULL;
148 	_this->capacity = 0;
149 	_this->position = 0;
150 	_this->limit = 0;
151 	_this->mark = -1;
152 
153 	return rval;
154 }
155 
156 /**
157  * Change capacity of this buffer.
158  *
159  * @param _this		the bytebuffer object.
160  * @param capacity	new capacity.
161  */
162 int
bytebuffer_realloc(bytebuffer * _this,size_t capacity)163 bytebuffer_realloc(bytebuffer *_this, size_t capacity)
164 {
165 	void *new_data;
166 
167 	BYTEBUF_ASSERT(_this->limit <= capacity);
168 
169 	if ((size_t)_this->limit > capacity) {
170 		errno = EINVAL;
171 		return -1;
172 	}
173 
174 	if ((new_data = realloc(_this->data, capacity)) == NULL)
175 		return -1;
176 
177 	_this->data = new_data;
178 	if ((size_t)_this->limit == _this->capacity)
179 		_this->limit = capacity;
180 	_this->capacity = capacity;
181 
182 	return 0;
183 }
184 
185 /**
186  * Compact this buffer.  the bytes between position and limit are copied
187  * to the beginning of the buffer.
188  *
189  * @param _this		the bytebuffer object.
190  */
191 void
bytebuffer_compact(bytebuffer * _this)192 bytebuffer_compact(bytebuffer *_this)
193 {
194 	int len;
195 
196 	len = bytebuffer_remaining(_this);
197 
198 	if (len <= 0)
199 		len = 0;
200 	else if (_this->position != 0)
201 		memmove(_this->data,
202 		    (const char *)_this->data + _this->position, (size_t)len);
203 
204 	_this->position = len;
205 	_this->limit = _this->capacity;
206 	_this->mark = -1;
207 }
208 
209 static int bytebuffer_direct0;
210 /**
211  * BYTEBUFFER_PUT_DIRECT specifies the data that has been written already by
212  * direct access.
213  */
214 const void * BYTEBUFFER_PUT_DIRECT = &bytebuffer_direct0;
215 
216 /**
217  * BYTEBUFFER_GET_DIRECT specifies the data that has been read already by
218  * direct access.
219  */
220 void * BYTEBUFFER_GET_DIRECT = &bytebuffer_direct0;
221 
222 /**
223  * Write the given data to the buffer.
224  * If buffer is too small, this function returns <code>NULL</code> and
225  * <code>errno</code> is <code>ENOBUFS</code>
226  *
227  * @param _this		the bytebuffer object.
228  * @param src		source data.  To specify the data that has been
229  *			written already by direct access, use
230  *			{@link ::BYTEBUFFER_PUT_DIRECT} for putting the data.
231  *			NULL is the same {@link ::BYTEBUFFER_PUT_DIRECT}
232  *			at least on this version, but using NULL is deprecated.
233  * @param srclen	length of the source data.
234  * @see ::BYTEBUFFER_PUT_DIRECT
235  */
236 void *
bytebuffer_put(bytebuffer * _this,const void * src,size_t srclen)237 bytebuffer_put(bytebuffer *_this, const void *src, size_t srclen)
238 {
239 	void *rval;
240 
241 	BYTEBUF_ASSERT(_this != NULL);
242 	BYTEBUF_ASSERT(srclen > 0);
243 
244 	if (srclen > bytebuffer_remaining(_this)) {
245 		errno = ENOBUFS;
246 		return NULL;
247 	}
248 	rval = (char *)_this->data + _this->position;
249 
250 	if (src != NULL && src != BYTEBUFFER_PUT_DIRECT)
251 		memcpy(rval, src, srclen);
252 
253 	_this->position += srclen;
254 
255 	return rval;
256 }
257 
258 /*
259  * Transfer data from this buffer to the given destination memory.
260  * If the given buffer is too small, this function returns <code>NULL</code>
261  * and <code>errno</code> is <code>ENOBUFS</code>
262  *
263  * @param	dst	pointer of the destination memory.  Specify NULL
264  *			to skip transferring the data.
265  * @param	dstlen	memory size of the destination.
266  */
267 void *
bytebuffer_get(bytebuffer * _this,void * dst,size_t dstlen)268 bytebuffer_get(bytebuffer *_this, void *dst, size_t dstlen)
269 {
270 	BYTEBUF_ASSERT(_this != NULL);
271 	BYTEBUF_ASSERT(dstlen > 0);
272 
273 	if (dstlen > bytebuffer_remaining(_this)) {
274 		errno = ENOBUFS;
275 		return NULL;
276 	}
277 	if (dst != NULL && dst != BYTEBUFFER_GET_DIRECT)
278 		memcpy(dst, (char *)_this->data + _this->position, dstlen);
279 
280 	_this->position += dstlen;
281 
282 	return dst;
283 }
284 
285 /** Returns this buffer's position */
286 int
bytebuffer_position(bytebuffer * _this)287 bytebuffer_position(bytebuffer *_this)
288 {
289 	BYTEBUF_ASSERT(_this != NULL);
290 
291 	return _this->position;
292 }
293 
294 /** Returns this buffer's limit */
295 int
bytebuffer_limit(bytebuffer * _this)296 bytebuffer_limit(bytebuffer *_this)
297 {
298 	BYTEBUF_ASSERT(_this != NULL);
299 
300 	return _this->limit;
301 }
302 
303 /** Returns this buffer's capacity */
304 int
bytebuffer_capacity(bytebuffer * _this)305 bytebuffer_capacity(bytebuffer *_this)
306 {
307 	BYTEBUF_ASSERT(_this != NULL);
308 
309 	return _this->capacity;
310 }
311 
312 /** Returns a pointer to current position */
313 void *
bytebuffer_pointer(bytebuffer * _this)314 bytebuffer_pointer(bytebuffer *_this)
315 {
316 	BYTEBUF_ASSERT(_this != NULL);
317 
318 	return (char *)_this->data + _this->position;
319 }
320 
321 /** Returns the number of byte between current position and the limit*/
322 size_t
bytebuffer_remaining(bytebuffer * _this)323 bytebuffer_remaining(bytebuffer *_this)
324 {
325 	BYTEBUF_ASSERT(_this != NULL);
326 	BYTEBUF_ASSERT(_this->limit >= _this->position);
327 
328 	return _this->limit - _this->position;
329 }
330 
331 /** Returns whether there are data between current position and the limit */
332 int
bytebuffer_has_remaining(bytebuffer * _this)333 bytebuffer_has_remaining(bytebuffer *_this)
334 {
335 	BYTEBUF_ASSERT(_this != NULL);
336 
337 	return bytebuffer_remaining(_this) > 0;
338 }
339 
340 /**
341  * Flip this buffer.
342  * The limit is set to the position and the position is set zero.
343  */
344 void
bytebuffer_flip(bytebuffer * _this)345 bytebuffer_flip(bytebuffer *_this)
346 {
347 	BYTEBUF_ASSERT(_this != NULL);
348 
349 	_this->limit = _this->position;
350 	_this->position = 0;
351 	_this->mark = -1;
352 }
353 
354 /**
355  * Rewind this buffer.
356  * The position is set to zero.
357  */
358 void
bytebuffer_rewind(bytebuffer * _this)359 bytebuffer_rewind(bytebuffer *_this)
360 {
361 	BYTEBUF_ASSERT(_this != NULL);
362 
363 	_this->position = 0;
364 	_this->mark = -1;
365 }
366 
367 /**
368  * Clear this buffer.
369  * The position is set to zero.
370  */
371 void
bytebuffer_clear(bytebuffer * _this)372 bytebuffer_clear(bytebuffer *_this)
373 {
374 	BYTEBUF_ASSERT(_this != NULL);
375 
376 	_this->limit = _this->capacity;
377 	_this->position = 0;
378 	_this->mark = -1;
379 }
380 
381 /** mark the current position.  */
382 void
bytebuffer_mark(bytebuffer * _this)383 bytebuffer_mark(bytebuffer *_this)
384 {
385 	BYTEBUF_ASSERT(_this != NULL);
386 
387 	_this->mark = _this->position;
388 }
389 
390 /** reset the position to the mark.  */
391 void
bytebuffer_reset(bytebuffer * _this)392 bytebuffer_reset(bytebuffer *_this)
393 {
394 	BYTEBUF_ASSERT(_this != NULL);
395 	BYTEBUF_ASSERT(_this->mark >= 0);
396 
397 	if (_this->mark >= 0)
398 		_this->position = _this->mark;
399 }
400 
401 /**
402  * Destroy bytebuffer object.
403  */
404 void
bytebuffer_destroy(bytebuffer * _this)405 bytebuffer_destroy(bytebuffer *_this)
406 {
407 	BYTEBUF_ASSERT(_this != NULL);
408 
409 	if (_this != NULL) {
410 		if (_this->data != NULL)
411 			free(_this->data);
412 		free(_this);
413 	}
414 }
415