xref: /openbsd/usr.sbin/npppd/common/bytebuf.c (revision 90023272)
1 /*	$OpenBSD: bytebuf.c,v 1.8 2015/12/05 18:43:36 mmcc 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.8 2015/12/05 18:43:36 mmcc 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 	free(_this);
110 	return NULL;
111 }
112 
113 /**
114  * Create a bytebuffer using existing memory area.  This memory area will
115  * be freed by bytebuffer's destructor.
116  *
117  * @data		the pointer to existing memory area
118  * @param capacity	capacity of 'data'.
119  */
120 bytebuffer *
bytebuffer_wrap(void * data,size_t capacity)121 bytebuffer_wrap(void *data, size_t capacity)
122 {
123 	bytebuffer *_this;
124 
125 	if ((_this = bytebuffer_create(0)) == NULL)
126 		return NULL;
127 
128 	_this->data = data;
129 	_this->capacity = capacity;
130 	_this->mark = -1;
131 
132 	return _this;
133 }
134 
135 /**
136  * Unwrap memory from bytebuffer.
137  *
138  * @param _this		the bytebuffer object.
139  */
140 void *
bytebuffer_unwrap(bytebuffer * _this)141 bytebuffer_unwrap(bytebuffer *_this)
142 {
143 	void *rval;
144 
145 	rval = _this->data;
146 	_this->data = NULL;
147 	_this->capacity = 0;
148 	_this->position = 0;
149 	_this->limit = 0;
150 	_this->mark = -1;
151 
152 	return rval;
153 }
154 
155 /**
156  * Change capacity of this buffer.
157  *
158  * @param _this		the bytebuffer object.
159  * @param capacity	new capacity.
160  */
161 int
bytebuffer_realloc(bytebuffer * _this,size_t capacity)162 bytebuffer_realloc(bytebuffer *_this, size_t capacity)
163 {
164 	void *new_data;
165 
166 	BYTEBUF_ASSERT(_this->limit <= capacity);
167 
168 	if (_this->limit > capacity) {
169 		errno = EINVAL;
170 		return -1;
171 	}
172 
173 	if ((new_data = realloc(_this->data, capacity)) == NULL)
174 		return -1;
175 
176 	_this->data = new_data;
177 	if (_this->limit == _this->capacity)
178 		_this->limit = capacity;
179 	_this->capacity = capacity;
180 
181 	return 0;
182 }
183 
184 /**
185  * Compact this buffer.  the bytes between position and limit are copied
186  * to the beginning of the buffer.
187  *
188  * @param _this		the bytebuffer object.
189  */
190 void
bytebuffer_compact(bytebuffer * _this)191 bytebuffer_compact(bytebuffer *_this)
192 {
193 	int len;
194 
195 	len = bytebuffer_remaining(_this);
196 
197 	if (len <= 0)
198 		len = 0;
199 	else if (_this->position != 0)
200 		memmove(_this->data,
201 		    (const char *)_this->data + _this->position, (size_t)len);
202 
203 	_this->position = len;
204 	_this->limit = _this->capacity;
205 	_this->mark = -1;
206 }
207 
208 static int bytebuffer_direct0;
209 /**
210  * BYTEBUFFER_PUT_DIRECT specifies the data that has been written already by
211  * direct access.
212  */
213 const void * BYTEBUFFER_PUT_DIRECT = &bytebuffer_direct0;
214 
215 /**
216  * BYTEBUFFER_GET_DIRECT specifies the data that has been read already by
217  * direct access.
218  */
219 void * BYTEBUFFER_GET_DIRECT = &bytebuffer_direct0;
220 
221 /**
222  * Write the given data to the buffer.
223  * If buffer is too small, this function returns <code>NULL</code> and
224  * <code>errno</code> is <code>ENOBUFS</code>
225  *
226  * @param _this		the bytebuffer object.
227  * @param src		source data.  To specify the data that has been
228  *			written already by direct access, use
229  *			{@link ::BYTEBUFFER_PUT_DIRECT} for putting the data.
230  *			NULL is the same {@link ::BYTEBUFFER_PUT_DIRECT}
231  *			at least on this version, but using NULL is deprecated.
232  * @param srclen	length of the source data.
233  * @see ::BYTEBUFFER_PUT_DIRECT
234  */
235 void *
bytebuffer_put(bytebuffer * _this,const void * src,size_t srclen)236 bytebuffer_put(bytebuffer *_this, const void *src, size_t srclen)
237 {
238 	void *rval;
239 
240 	BYTEBUF_ASSERT(_this != NULL);
241 	BYTEBUF_ASSERT(srclen > 0);
242 
243 	if (srclen > bytebuffer_remaining(_this)) {
244 		errno = ENOBUFS;
245 		return NULL;
246 	}
247 	rval = (char *)_this->data + _this->position;
248 
249 	if (src != NULL && src != BYTEBUFFER_PUT_DIRECT)
250 		memcpy(rval, src, srclen);
251 
252 	_this->position += srclen;
253 
254 	return rval;
255 }
256 
257 /*
258  * Transfer data from this buffer to the given destination memory.
259  * If the given buffer is too small, this function returns <code>NULL</code>
260  * and <code>errno</code> is <code>ENOBUFS</code>
261  *
262  * @param	dst	pointer of the destination memory.  Specify NULL
263  *			to skip transferring the data.
264  * @param	dstlne	memory size of the destination.
265  */
266 void *
bytebuffer_get(bytebuffer * _this,void * dst,size_t dstlen)267 bytebuffer_get(bytebuffer *_this, void *dst, size_t dstlen)
268 {
269 	BYTEBUF_ASSERT(_this != NULL);
270 	BYTEBUF_ASSERT(dstlen > 0);
271 
272 	if (dstlen > bytebuffer_remaining(_this)) {
273 		errno = ENOBUFS;
274 		return NULL;
275 	}
276 	if (dst != NULL && dst != BYTEBUFFER_GET_DIRECT)
277 		memcpy(dst, (char *)_this->data + _this->position, dstlen);
278 
279 	_this->position += dstlen;
280 
281 	return dst;
282 }
283 
284 /** Returns this buffer's position */
285 int
bytebuffer_position(bytebuffer * _this)286 bytebuffer_position(bytebuffer *_this)
287 {
288 	BYTEBUF_ASSERT(_this != NULL);
289 
290 	return _this->position;
291 }
292 
293 /** Returns this buffer's limit */
294 int
bytebuffer_limit(bytebuffer * _this)295 bytebuffer_limit(bytebuffer *_this)
296 {
297 	BYTEBUF_ASSERT(_this != NULL);
298 
299 	return _this->limit;
300 }
301 
302 /** Returns this buffer's capacity */
303 int
bytebuffer_capacity(bytebuffer * _this)304 bytebuffer_capacity(bytebuffer *_this)
305 {
306 	BYTEBUF_ASSERT(_this != NULL);
307 
308 	return _this->capacity;
309 }
310 
311 /** Returns a pointer to current position */
312 void *
bytebuffer_pointer(bytebuffer * _this)313 bytebuffer_pointer(bytebuffer *_this)
314 {
315 	BYTEBUF_ASSERT(_this != NULL);
316 
317 	return (char *)_this->data + _this->position;
318 }
319 
320 /** Returns the number of byte between current position and the limit*/
321 size_t
bytebuffer_remaining(bytebuffer * _this)322 bytebuffer_remaining(bytebuffer *_this)
323 {
324 	BYTEBUF_ASSERT(_this != NULL);
325 	BYTEBUF_ASSERT(_this->limit >= _this->position);
326 
327 	return _this->limit - _this->position;
328 }
329 
330 /** Returns whether there are data between current position and the limit */
331 int
bytebuffer_has_remaining(bytebuffer * _this)332 bytebuffer_has_remaining(bytebuffer *_this)
333 {
334 	BYTEBUF_ASSERT(_this != NULL);
335 
336 	return bytebuffer_remaining(_this) > 0;
337 }
338 
339 /**
340  * Flip this buffer.
341  * The limit is set to the position and the position is set zero.
342  */
343 void
bytebuffer_flip(bytebuffer * _this)344 bytebuffer_flip(bytebuffer *_this)
345 {
346 	BYTEBUF_ASSERT(_this != NULL);
347 
348 	_this->limit = _this->position;
349 	_this->position = 0;
350 	_this->mark = -1;
351 }
352 
353 /**
354  * Rewind this buffer.
355  * The position is set to zero.
356  */
357 void
bytebuffer_rewind(bytebuffer * _this)358 bytebuffer_rewind(bytebuffer *_this)
359 {
360 	BYTEBUF_ASSERT(_this != NULL);
361 
362 	_this->position = 0;
363 	_this->mark = -1;
364 }
365 
366 /**
367  * Clear this buffer.
368  * The position is set to zero.
369  */
370 void
bytebuffer_clear(bytebuffer * _this)371 bytebuffer_clear(bytebuffer *_this)
372 {
373 	BYTEBUF_ASSERT(_this != NULL);
374 
375 	_this->limit = _this->capacity;
376 	_this->position = 0;
377 	_this->mark = -1;
378 }
379 
380 /** mark the current position.  */
381 void
bytebuffer_mark(bytebuffer * _this)382 bytebuffer_mark(bytebuffer *_this)
383 {
384 	BYTEBUF_ASSERT(_this != NULL);
385 
386 	_this->mark = _this->position;
387 }
388 
389 /** reset the position to the mark.  */
390 void
bytebuffer_reset(bytebuffer * _this)391 bytebuffer_reset(bytebuffer *_this)
392 {
393 	BYTEBUF_ASSERT(_this != NULL);
394 	BYTEBUF_ASSERT(_this->mark >= 0);
395 
396 	if (_this->mark >= 0)
397 		_this->position = _this->mark;
398 }
399 
400 /**
401  * Destroy bytebuffer object.
402  */
403 void
bytebuffer_destroy(bytebuffer * _this)404 bytebuffer_destroy(bytebuffer *_this)
405 {
406 	BYTEBUF_ASSERT(_this != NULL);
407 
408 	if (_this != NULL) {
409 		free(_this->data);
410 		free(_this);
411 	}
412 }
413