bytebuffer.c 4.16 KB
Newer Older
1
#include "bytebuffer.h"
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#include "../common.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>


// ****************************************************************************
// BYTE BUFFER
// ****************************************************************************

static inline void
__buffer_assign (
	buffer_t * restrict buffer, uint8_t * restrict bytes, 
	const size_t position, const size_t capacity
) {
	(*buffer) = (buffer_t) {
		.bytes = bytes,
		.position = position,
		.capacity = capacity
	};
}


static inline uint8_t *
__alloc_bytes (size_t capacity) {
	uint8_t * bytes = (uint8_t *) malloc (capacity);
29
	check_error (bytes == NULL, "failed to allocate %zu bytes", capacity);
30 31 32 33 34 35 36 37 38 39 40
	return bytes;
}


/**
 * Initializes the given byte buffer. Allocates a buffer of given capacity
 * for the data and sets the position in the buffer to 0.
 */
void
buffer_init (buffer_t * restrict buffer, const size_t capacity) {
	assert (buffer != NULL);
41
	assert (capacity > 0);
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

	uint8_t * bytes = __alloc_bytes (capacity);
	__buffer_assign (buffer, bytes, 0, capacity);
}


/**
 * Destroys the given byte buffer. Releases the buffer memory and
 * sets the position and capacity in the buffer to 0.
 */
void
buffer_destroy (buffer_t * restrict buffer) {
	assert (buffer != NULL);

	uint8_t * bytes = buffer->bytes;
	__buffer_assign (buffer, NULL, 0, 0);

	assert (bytes != NULL);
	free (bytes);
}


static inline size_t
__calculate_capacity (
	const size_t capacity, const size_t occupied, const size_t needed
) {
	size_t new_capacity = 2 * capacity;
	while (new_capacity - occupied < needed) {
		new_capacity *= 2;
	}

	return new_capacity;
}


77 78 79 80 81 82 83
static inline size_t
__buffer_remaining (const buffer_t * restrict buffer) {
	// Capacity is always greater than position
	return buffer->capacity - buffer->position;
}


84 85 86 87 88 89
/**
 * Ensure that the given buffer has enough remaining scape to
 * accomodate the given number of bytes. If necessary, the buffer
 * is expanded to accomodate the requirement. Returns the new
 * capacity of the buffer.
 */
90 91
static size_t
__buffer_ensure_remaining (buffer_t * restrict buffer, const size_t bytes_needed) {
92 93 94 95 96
	//
	// If the buffer does not have enough space remaining to accomodate
	// the space needed, double the buffer capacity until it can do so.
	// Then copy the buffer and update the capacity.
	//
97
	if (__buffer_remaining (buffer) < bytes_needed) {
98
		size_t new_capacity = __calculate_capacity (
99
			buffer->capacity, buffer_length (buffer), bytes_needed
100 101
		);

102
		uint8_t * restrict new_bytes = __alloc_bytes (new_capacity);
103
		uint8_t * restrict old_bytes = buffer->bytes;
104
		memcpy (new_bytes, old_bytes, buffer_length (buffer));
105 106 107 108 109 110 111

		buffer->bytes = new_bytes;
		buffer->capacity = new_capacity;

		free (old_bytes);
	}

112
	return buffer->capacity;
113 114 115 116
}


static inline uint8_t *
117
__buffer_ptr_at (const buffer_t * restrict buffer, size_t position) {
118 119 120 121
	return buffer->bytes + position;
}


122 123 124 125 126 127
static inline uint8_t *
__buffer_ptr (const buffer_t * restrict buffer) {
	return buffer->bytes + buffer->position;
}


128
/**
129 130 131
 * Returns a pointer to a block of memory at the given position in the buffer.
 * The block must be within the occupied part of the buffer and the block size
 * must be greater than 0.
132 133
 */
uint8_t *
134
buffer_ptr_block (const buffer_t * restrict buffer, size_t position, size_t size) {
135 136 137 138
	assert (buffer != NULL);
	assert (size > 0);

	const size_t end_position = position + size;
139
	const size_t length = buffer_length (buffer);
140 141

	check_error (
142 143 144 145
		end_position > length,
		"trying to get pointer to %zu bytes at "
		"position %zu in buffer with %zu bytes",
		size, position, length
146 147
	);

148
	return __buffer_ptr_at (buffer, position);
149 150 151 152
}


/**
153 154 155 156 157 158
 * Claims a block of given size starting at the current position in
 * the given buffer and returns a pointer to the block. If necessary,
 * the capacity of the buffer is expanded to accomodate the block.
 *
 * NOTE: The caller should never store pointers to the buffer between
 * operations that might enlarge the buffer.
159 160
 */
uint8_t *
161
buffer_claim (buffer_t * restrict buffer, const size_t size) {
162 163 164
	assert (buffer != NULL);
	assert (size > 0);

165
	__buffer_ensure_remaining (buffer, size);
166

167
	uint8_t * result = __buffer_ptr (buffer);
168 169 170
	buffer->position += size;
	return result;
}