What?
I have a reference counted dynamic byte array written in C. I'm currently using this implementation in a FIFO fashion. Particularly reading data from files into the arrays, then parsing the data in the array byte by byte.
I'm looking for feedback on anything! Even the decision to use a dynamic byte array versus using a linked list or any other data structure.
Edit: I'm trying to follow the Linux Kernel coding style, so I'm looking for feedback on how well I follow that convention.
Files
Header
#ifndef DATA_BYTE_ARRAY
#define DATA_BYTE_ARRAY
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "../memory.h"
/**
* Dynamic array that grows when it's full (given that
* the util functions in this module are used).
*
* For each grow it uses the current length doubled. See
* byte_array_push for more info.
*/
struct byte_array {
uint8_t refs;
uint32_t length;
uint32_t capability;
uint8_t *bytes;
};
/**
* Creates a byte array with the given length.
*
* Values of the returned struct:
* - 1 reference
* - All bytes will be set to 0
* - Current position will be set to 0
* - Capability will reflect how many bytes are allocated. Which is
* the same as given capability.
*/
struct byte_array *byte_array_create(uint32_t capability);
/**
* Adds the given input at the current position of the array.
*
* Note that if the pointer to the byte array is NULL an assertion will crash
* the program.
*
* If the byte array is full more memory will be allocated. The amount
* of new memory that will be allocated is the current amount doubled.
*/
void byte_array_push(struct byte_array *a, uint8_t input);
void byte_array_incref(struct byte_array *a);
void byte_array_decref(struct byte_array *a);
#endif
Source
#include "byte_array.h"
#define NEW_CAPABILITY a->capability * 2
static void zero_init_bytes(struct byte_array *a,
uint32_t offset,
uint32_t nmemb)
{
uint8_t *bytes = a->bytes;
bytes += offset;
memset(bytes, 0, nmemb);
}
struct byte_array *byte_array_create(uint32_t capability)
{
struct byte_array *result = assert_malloc(sizeof(*result));
result->refs = 1;
result->length = 0;
result->capability = capability;
result->bytes = assert_calloc(capability, sizeof(uint8_t));
return result;
}
void byte_array_push(struct byte_array *a, uint8_t input)
{
assert(a != NULL);
if (a->length == a->capability) {
void *tmp = assert_realloc(a->bytes, NEW_CAPABILITY);
a->bytes = tmp;
zero_init_bytes(
a,
a->capability,
NEW_CAPABILITY - a->capability
);
a->capability = NEW_CAPABILITY;
}
a->bytes[a->length++] = input;
}
void byte_array_incref(struct byte_array *a)
{
assert(a != NULL);
a->refs++;
}
void byte_array_decref(struct byte_array *a)
{
assert(a != NULL);
if (--a->refs != 0) return;
free(a->bytes);
free(a);
}
assert_malloc
andassert_calloc
, they are wrapper functions that raises an assertion ifmalloc
orcalloc
fails. \$\endgroup\$assert
macro. \$\endgroup\$