1/*	$NetBSD: buffer.c,v 1.1 2024/02/18 20:57:48 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16/*! \file */
17
18#include <inttypes.h>
19#include <stdarg.h>
20#include <stdbool.h>
21
22#include <isc/buffer.h>
23#include <isc/mem.h>
24#include <isc/print.h>
25#include <isc/region.h>
26#include <isc/string.h>
27#include <isc/util.h>
28
29void
30isc__buffer_init(isc_buffer_t *b, void *base, unsigned int length) {
31	/*
32	 * Make 'b' refer to the 'length'-byte region starting at 'base'.
33	 * XXXDCL see the comment in buffer.h about base being const.
34	 */
35	ISC__BUFFER_INIT(b, base, length);
36}
37
38void
39isc__buffer_initnull(isc_buffer_t *b) {
40	/*
41	 * Initialize a new buffer which has no backing store.  This can
42	 * later be grown as needed and swapped in place.
43	 */
44	ISC__BUFFER_INIT(b, NULL, 0);
45}
46
47void
48isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length) {
49	/*
50	 * Re-initialize the buffer enough to reconfigure the base of the
51	 * buffer.  We will swap in the new buffer, after copying any
52	 * data we contain into the new buffer and adjusting all of our
53	 * internal pointers.
54	 *
55	 * The buffer must not be smaller than the length of the original
56	 * buffer.
57	 */
58	REQUIRE(b->length <= length);
59	REQUIRE(base != NULL);
60	REQUIRE(!b->autore);
61
62	if (b->length > 0U) {
63		(void)memmove(base, b->base, b->length);
64	}
65
66	b->base = base;
67	b->length = length;
68}
69
70void
71isc__buffer_invalidate(isc_buffer_t *b) {
72	/*
73	 * Make 'b' an invalid buffer.
74	 */
75	ISC__BUFFER_INVALIDATE(b);
76}
77
78void
79isc_buffer_setautorealloc(isc_buffer_t *b, bool enable) {
80	REQUIRE(ISC_BUFFER_VALID(b));
81	REQUIRE(b->mctx != NULL);
82	b->autore = enable;
83}
84
85void
86isc__buffer_region(isc_buffer_t *b, isc_region_t *r) {
87	/*
88	 * Make 'r' refer to the region of 'b'.
89	 */
90	ISC__BUFFER_REGION(b, r);
91}
92
93void
94isc__buffer_usedregion(const isc_buffer_t *b, isc_region_t *r) {
95	/*
96	 * Make 'r' refer to the used region of 'b'.
97	 */
98	ISC__BUFFER_USEDREGION(b, r);
99}
100
101void
102isc__buffer_availableregion(isc_buffer_t *b, isc_region_t *r) {
103	/*
104	 * Make 'r' refer to the available region of 'b'.
105	 */
106	ISC__BUFFER_AVAILABLEREGION(b, r);
107}
108
109void
110isc__buffer_add(isc_buffer_t *b, unsigned int n) {
111	/*
112	 * Increase the 'used' region of 'b' by 'n' bytes.
113	 */
114	ISC__BUFFER_ADD(b, n);
115}
116
117void
118isc__buffer_subtract(isc_buffer_t *b, unsigned int n) {
119	/*
120	 * Decrease the 'used' region of 'b' by 'n' bytes.
121	 */
122	ISC__BUFFER_SUBTRACT(b, n);
123}
124
125void
126isc__buffer_clear(isc_buffer_t *b) {
127	/*
128	 * Make the used region empty.
129	 */
130	ISC__BUFFER_CLEAR(b);
131}
132
133void
134isc__buffer_consumedregion(isc_buffer_t *b, isc_region_t *r) {
135	/*
136	 * Make 'r' refer to the consumed region of 'b'.
137	 */
138	ISC__BUFFER_CONSUMEDREGION(b, r);
139}
140
141void
142isc__buffer_remainingregion(isc_buffer_t *b, isc_region_t *r) {
143	/*
144	 * Make 'r' refer to the remaining region of 'b'.
145	 */
146	ISC__BUFFER_REMAININGREGION(b, r);
147}
148
149void
150isc__buffer_activeregion(isc_buffer_t *b, isc_region_t *r) {
151	/*
152	 * Make 'r' refer to the active region of 'b'.
153	 */
154	ISC__BUFFER_ACTIVEREGION(b, r);
155}
156
157void
158isc__buffer_setactive(isc_buffer_t *b, unsigned int n) {
159	/*
160	 * Sets the end of the active region 'n' bytes after current.
161	 */
162	ISC__BUFFER_SETACTIVE(b, n);
163}
164
165void
166isc__buffer_first(isc_buffer_t *b) {
167	/*
168	 * Make the consumed region empty.
169	 */
170	ISC__BUFFER_FIRST(b);
171}
172
173void
174isc__buffer_forward(isc_buffer_t *b, unsigned int n) {
175	/*
176	 * Increase the 'consumed' region of 'b' by 'n' bytes.
177	 */
178	ISC__BUFFER_FORWARD(b, n);
179}
180
181void
182isc__buffer_back(isc_buffer_t *b, unsigned int n) {
183	/*
184	 * Decrease the 'consumed' region of 'b' by 'n' bytes.
185	 */
186	ISC__BUFFER_BACK(b, n);
187}
188
189void
190isc_buffer_compact(isc_buffer_t *b) {
191	unsigned int length;
192	void *src;
193
194	/*
195	 * Compact the used region by moving the remaining region so it occurs
196	 * at the start of the buffer.  The used region is shrunk by the size
197	 * of the consumed region, and the consumed region is then made empty.
198	 */
199
200	REQUIRE(ISC_BUFFER_VALID(b));
201
202	src = isc_buffer_current(b);
203	length = isc_buffer_remaininglength(b);
204	if (length > 0U) {
205		(void)memmove(b->base, src, (size_t)length);
206	}
207
208	if (b->active > b->current) {
209		b->active -= b->current;
210	} else {
211		b->active = 0;
212	}
213	b->current = 0;
214	b->used = length;
215}
216
217uint8_t
218isc_buffer_getuint8(isc_buffer_t *b) {
219	unsigned char *cp;
220	uint8_t result;
221
222	/*
223	 * Read an unsigned 8-bit integer from 'b' and return it.
224	 */
225
226	REQUIRE(ISC_BUFFER_VALID(b));
227	REQUIRE(b->used - b->current >= 1);
228
229	cp = isc_buffer_current(b);
230	b->current += 1;
231	result = ((uint8_t)(cp[0]));
232
233	return (result);
234}
235
236void
237isc__buffer_putuint8(isc_buffer_t *b, uint8_t val) {
238	ISC__BUFFER_PUTUINT8(b, val);
239}
240
241uint16_t
242isc_buffer_getuint16(isc_buffer_t *b) {
243	unsigned char *cp;
244	uint16_t result;
245
246	/*
247	 * Read an unsigned 16-bit integer in network byte order from 'b',
248	 * convert it to host byte order, and return it.
249	 */
250
251	REQUIRE(ISC_BUFFER_VALID(b));
252	REQUIRE(b->used - b->current >= 2);
253
254	cp = isc_buffer_current(b);
255	b->current += 2;
256	result = ((unsigned int)(cp[0])) << 8;
257	result |= ((unsigned int)(cp[1]));
258
259	return (result);
260}
261
262void
263isc__buffer_putuint16(isc_buffer_t *b, uint16_t val) {
264	ISC__BUFFER_PUTUINT16(b, val);
265}
266
267void
268isc__buffer_putuint24(isc_buffer_t *b, uint32_t val) {
269	ISC__BUFFER_PUTUINT24(b, val);
270}
271
272uint32_t
273isc_buffer_getuint32(isc_buffer_t *b) {
274	unsigned char *cp;
275	uint32_t result;
276
277	/*
278	 * Read an unsigned 32-bit integer in network byte order from 'b',
279	 * convert it to host byte order, and return it.
280	 */
281
282	REQUIRE(ISC_BUFFER_VALID(b));
283	REQUIRE(b->used - b->current >= 4);
284
285	cp = isc_buffer_current(b);
286	b->current += 4;
287	result = ((unsigned int)(cp[0])) << 24;
288	result |= ((unsigned int)(cp[1])) << 16;
289	result |= ((unsigned int)(cp[2])) << 8;
290	result |= ((unsigned int)(cp[3]));
291
292	return (result);
293}
294
295void
296isc__buffer_putuint32(isc_buffer_t *b, uint32_t val) {
297	ISC__BUFFER_PUTUINT32(b, val);
298}
299
300uint64_t
301isc_buffer_getuint48(isc_buffer_t *b) {
302	unsigned char *cp;
303	uint64_t result;
304
305	/*
306	 * Read an unsigned 48-bit integer in network byte order from 'b',
307	 * convert it to host byte order, and return it.
308	 */
309
310	REQUIRE(ISC_BUFFER_VALID(b));
311	REQUIRE(b->used - b->current >= 6);
312
313	cp = isc_buffer_current(b);
314	b->current += 6;
315	result = ((int64_t)(cp[0])) << 40;
316	result |= ((int64_t)(cp[1])) << 32;
317	result |= ((int64_t)(cp[2])) << 24;
318	result |= ((int64_t)(cp[3])) << 16;
319	result |= ((int64_t)(cp[4])) << 8;
320	result |= ((int64_t)(cp[5]));
321
322	return (result);
323}
324
325void
326isc__buffer_putuint48(isc_buffer_t *b, uint64_t val) {
327	isc_result_t result;
328	uint16_t valhi;
329	uint32_t vallo;
330
331	REQUIRE(ISC_BUFFER_VALID(b));
332	if (ISC_UNLIKELY(b->autore)) {
333		result = isc_buffer_reserve(&b, 6);
334		REQUIRE(result == ISC_R_SUCCESS);
335	}
336	REQUIRE(isc_buffer_availablelength(b) >= 6);
337
338	valhi = (uint16_t)(val >> 32);
339	vallo = (uint32_t)(val & 0xFFFFFFFF);
340	ISC__BUFFER_PUTUINT16(b, valhi);
341	ISC__BUFFER_PUTUINT32(b, vallo);
342}
343
344void
345isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base,
346		   unsigned int length) {
347	ISC__BUFFER_PUTMEM(b, base, length);
348}
349
350void
351isc__buffer_putstr(isc_buffer_t *b, const char *source) {
352	ISC__BUFFER_PUTSTR(b, source);
353}
354
355void
356isc_buffer_putdecint(isc_buffer_t *b, int64_t v) {
357	unsigned int l = 0;
358	unsigned char *cp;
359	char buf[21];
360	isc_result_t result;
361
362	REQUIRE(ISC_BUFFER_VALID(b));
363
364	/* xxxwpk do it more low-level way ? */
365	l = snprintf(buf, 21, "%" PRId64, v);
366	RUNTIME_CHECK(l <= 21);
367	if (ISC_UNLIKELY(b->autore)) {
368		result = isc_buffer_reserve(&b, l);
369		REQUIRE(result == ISC_R_SUCCESS);
370	}
371	REQUIRE(isc_buffer_availablelength(b) >= l);
372
373	cp = isc_buffer_used(b);
374	memmove(cp, buf, l);
375	b->used += l;
376}
377
378isc_result_t
379isc_buffer_dup(isc_mem_t *mctx, isc_buffer_t **dstp, const isc_buffer_t *src) {
380	isc_buffer_t *dst = NULL;
381	isc_region_t region;
382	isc_result_t result;
383
384	REQUIRE(dstp != NULL && *dstp == NULL);
385	REQUIRE(ISC_BUFFER_VALID(src));
386
387	isc_buffer_usedregion(src, &region);
388
389	isc_buffer_allocate(mctx, &dst, region.length);
390
391	result = isc_buffer_copyregion(dst, &region);
392	RUNTIME_CHECK(result == ISC_R_SUCCESS); /* NOSPACE is impossible */
393	*dstp = dst;
394	return (ISC_R_SUCCESS);
395}
396
397isc_result_t
398isc_buffer_copyregion(isc_buffer_t *b, const isc_region_t *r) {
399	isc_result_t result;
400
401	REQUIRE(ISC_BUFFER_VALID(b));
402	REQUIRE(r != NULL);
403
404	if (ISC_UNLIKELY(b->autore)) {
405		result = isc_buffer_reserve(&b, r->length);
406		if (result != ISC_R_SUCCESS) {
407			return (result);
408		}
409	}
410
411	if (r->length > isc_buffer_availablelength(b)) {
412		return (ISC_R_NOSPACE);
413	}
414
415	if (r->length > 0U) {
416		memmove(isc_buffer_used(b), r->base, r->length);
417		b->used += r->length;
418	}
419
420	return (ISC_R_SUCCESS);
421}
422
423void
424isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer,
425		    unsigned int length) {
426	REQUIRE(dynbuffer != NULL && *dynbuffer == NULL);
427
428	isc_buffer_t *dbuf = isc_mem_get(mctx, sizeof(isc_buffer_t));
429	unsigned char *bdata = isc_mem_get(mctx, length);
430
431	isc_buffer_init(dbuf, bdata, length);
432
433	ENSURE(ISC_BUFFER_VALID(dbuf));
434
435	dbuf->mctx = mctx;
436
437	*dynbuffer = dbuf;
438}
439
440isc_result_t
441isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size) {
442	unsigned char *bdata;
443	uint64_t len;
444
445	REQUIRE(dynbuffer != NULL);
446	REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
447
448	len = (*dynbuffer)->length;
449	if ((len - (*dynbuffer)->used) >= size) {
450		return (ISC_R_SUCCESS);
451	}
452
453	if ((*dynbuffer)->mctx == NULL) {
454		return (ISC_R_NOSPACE);
455	}
456
457	/* Round to nearest buffer size increment */
458	len = size + (*dynbuffer)->used;
459	len = (len + ISC_BUFFER_INCR - 1 - ((len - 1) % ISC_BUFFER_INCR));
460
461	/* Cap at UINT_MAX */
462	if (len > UINT_MAX) {
463		len = UINT_MAX;
464	}
465
466	if ((len - (*dynbuffer)->used) < size) {
467		return (ISC_R_NOMEMORY);
468	}
469
470	/*
471	 * XXXMUKS: This is far more expensive than plain realloc() as
472	 * it doesn't remap pages, but does ordinary copy. So is
473	 * isc_mem_reallocate(), which has additional issues.
474	 */
475	bdata = isc_mem_get((*dynbuffer)->mctx, (unsigned int)len);
476
477	memmove(bdata, (*dynbuffer)->base, (*dynbuffer)->length);
478	isc_mem_put((*dynbuffer)->mctx, (*dynbuffer)->base,
479		    (*dynbuffer)->length);
480
481	(*dynbuffer)->base = bdata;
482	(*dynbuffer)->length = (unsigned int)len;
483
484	return (ISC_R_SUCCESS);
485}
486
487void
488isc_buffer_free(isc_buffer_t **dynbuffer) {
489	isc_buffer_t *dbuf;
490	isc_mem_t *mctx;
491
492	REQUIRE(dynbuffer != NULL);
493	REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
494	REQUIRE((*dynbuffer)->mctx != NULL);
495
496	dbuf = *dynbuffer;
497	*dynbuffer = NULL; /* destroy external reference */
498	mctx = dbuf->mctx;
499	dbuf->mctx = NULL;
500
501	isc_mem_put(mctx, dbuf->base, dbuf->length);
502	isc_buffer_invalidate(dbuf);
503	isc_mem_put(mctx, dbuf, sizeof(isc_buffer_t));
504}
505
506isc_result_t
507isc_buffer_printf(isc_buffer_t *b, const char *format, ...) {
508	va_list ap;
509	int n;
510	isc_result_t result;
511
512	REQUIRE(ISC_BUFFER_VALID(b));
513
514	va_start(ap, format);
515	n = vsnprintf(NULL, 0, format, ap);
516	va_end(ap);
517
518	if (n < 0) {
519		return (ISC_R_FAILURE);
520	}
521
522	if (ISC_UNLIKELY(b->autore)) {
523		result = isc_buffer_reserve(&b, n + 1);
524		if (result != ISC_R_SUCCESS) {
525			return (result);
526		}
527	}
528
529	if (isc_buffer_availablelength(b) < (unsigned int)n + 1) {
530		return (ISC_R_NOSPACE);
531	}
532
533	va_start(ap, format);
534	n = vsnprintf(isc_buffer_used(b), n + 1, format, ap);
535	va_end(ap);
536
537	if (n < 0) {
538		return (ISC_R_FAILURE);
539	}
540
541	b->used += n;
542
543	return (ISC_R_SUCCESS);
544}
545