Commit 1603731f authored by Lubomir Bulej's avatar Lubomir Bulej

Simple implementation of expandable byte buffer

parent 7f5c78eb
#include "bufferbase.h"
#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) {
assert (capacity > 0);
uint8_t * bytes = (uint8_t *) malloc (capacity);
check_error (
bytes != NULL,
"failed to allocate buffer of %u bytes", capacity
);
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);
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;
}
/**
* 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.
*/
size_t
buffer_ensure_capacity (buffer_t * restrict buffer, const size_t bytes_needed) {
assert (buffer != NULL);
//
// 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.
//
if (buffer_remaining (buffer) < bytes_needed) {
size_t new_capacity = __calculate_capacity (
buffer_capacity (buffer), buffer_position (buffer),
bytes_needed
);
uint8_t * restrict new_bytes = (uint8_t *) malloc (new_capacity);
check_error (
new_bytes == NULL,
"failed to expand buffer from %u to %u bytes",
buffer_capacity (buffer), new_capacity
);
uint8_t * restrict old_bytes = buffer->bytes;
memcpy (new_bytes, old_bytes, buffer_position (buffer));
buffer->bytes = new_bytes;
buffer->capacity = new_capacity;
free (old_bytes);
}
return buffer_capacity (buffer);
}
static inline uint8_t *
__buffer_ptr (const buffer_t * restrict buffer, size_t position) {
return buffer->bytes + position;
}
/**
* 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.
*/
uint8_t *
buffer_ptr (const buffer_t * restrict buffer, size_t position, size_t size) {
assert (buffer != NULL);
assert (size > 0);
const size_t end_position = position + size;
check_error (
end_position > buffer_position (buffer),
"trying to get pointer to %u bytes at %u with only %u bytes present",
size, position, buffer_position (buffer)
);
return __buffer_ptr (buffer, position);
}
/**
* Returns a pointer to a block of memory of given size within
* the buffer, starting at the current position. If the buffer
* does not have enough remaining space, it is expanded to
* accomodate the requirement.
*/
uint8_t *
buffer_reserve (buffer_t * buffer, size_t size) {
assert (buffer != NULL);
assert (size > 0);
buffer_ensure_capacity (buffer, size);
size_t position = buffer_position (buffer);
uint8_t * result = __buffer_ptr (buffer, position);
buffer->position += size;
return result;
}
/**
* Copies a bytes from the buffer. The given position and number
* of bytes to copy must fit within the occupied space of the
* buffer. The destination buffer must have enough space and
* MUST NOT overlap with the given buffer's data space.
*/
void
buffer_get_bytes_at (
const buffer_t * restrict buffer, size_t position,
void * restrict dest, size_t count
) {
uint8_t * src = buffer_ptr (buffer, position, count);
memcpy (dest, (void * restrict) src, count);
}
#ifndef _BUFFERBASE_H_
#define _BUFFERBASE_H_
#include <stdlib.h>
#include <stdint.h>
typedef struct {
uint8_t * restrict bytes;
size_t position;
size_t capacity;
} buffer_t;
//
void buffer_init (buffer_t * restrict buffer, const size_t capacity);
void buffer_destroy (buffer_t * restrict buffer);
inline size_t
buffer_capacity (const buffer_t * restrict buffer) {
return buffer->capacity;
}
size_t buffer_ensure_capacity (buffer_t * restrict buffer, size_t capacity);
uint8_t * buffer_reserve (buffer_t * restrict buffer, size_t size);
inline size_t
buffer_position (const buffer_t * restrict buffer) {
return buffer->position;
}
inline size_t
buffer_remaining (const buffer_t * restrict buffer) {
// Capacity is always greater than position
return buffer->capacity - buffer->position;
}
inline void
buffer_clear (buffer_t * restrict buffer) {
buffer->position = 0;
}
uint8_t * buffer_ptr (
const buffer_t * restrict buffer, size_t position, size_t size
);
#endif /* _BUFFERBASE_H_ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment