Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/c-cpp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: C/C++ CI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Create build directory
run: mkdir -p build

- name: Run CMake
working-directory: build
run: cmake .. -DC_BUFFER_TEST=ON

- name: Build the project
working-directory: build
run: make

- name: Run tests
working-directory: build
run: ./test_c_buffer
29 changes: 29 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.13)

project(c_buffer C ASM)

add_library(c_buffer INTERFACE)

target_sources(c_buffer INTERFACE
src/c_buffer.c
)

target_include_directories(c_buffer INTERFACE
src
)

# Option to build standalone executable for testing
option(C_BUFFER_TEST "Build standalone executable for c_buffer" OFF)

if(C_BUFFER_TEST)
set(CMAKE_C_COMPILER gcc)

# Add standalone executable for testing c_buffer
add_executable(test_c_buffer test/test_c_buffer.c)

# Link the c_buffer library to the standalone executable
target_link_libraries(test_c_buffer PRIVATE c_buffer)

# Optionally, add any specific compiler options for testing
target_compile_options(test_c_buffer PRIVATE -Wall -Wextra -pedantic)
endif()
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Module used to create a circular buffer for bytes
Use this module to store data in a circular buffer

## Build this module standalone for testing
mkdir build
cd build
cmake .. -DC_BUFFER_TEST=ON
make
92 changes: 86 additions & 6 deletions src/c_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@
* SOFTWARE.
*/
#include "c_buffer.h"
#include "common.h"
#include "string.h"
#include <stdio.h>

#define MODULO_INC(value, increment, modulus) (((value) + (increment)) % (modulus))
#ifndef LOG
#define LOG(f_, ...) printf((f_), ##__VA_ARGS__)
#endif

static inline size_t MODULO_DEC(size_t value, size_t decrement, size_t modulus)
{
Expand Down Expand Up @@ -106,7 +109,7 @@ int32_t cBufferPrepend(cBuffer_t *inst, uint8_t *data, size_t data_size) {

// This cast is safe as the inst null check is allready done
if ((size_t)cBufferAvailableForWrite(inst) < data_size) {
return C_BUFFER_INSUFFICENT;
return C_BUFFER_INSUFFICIENT;
}

// Look for the special case were the buffer is empty
Expand Down Expand Up @@ -189,7 +192,7 @@ int32_t cBufferPrependByte(cBuffer_t *inst, uint8_t data) {

// This cast is safe as the inst null check is allready done
if ((size_t)cBufferAvailableForWrite(inst) < 1) {
return C_BUFFER_INSUFFICENT;
return C_BUFFER_INSUFFICIENT;
}

// Look for the special case were the buffer is empty
Expand Down Expand Up @@ -225,7 +228,7 @@ int32_t cBufferAppend(cBuffer_t *inst, uint8_t *data, size_t data_size) {

// This cast is safe as the inst null check is allready done
if ((size_t)cBufferAvailableForWrite(inst) < data_size) {
return C_BUFFER_INSUFFICENT;
return C_BUFFER_INSUFFICIENT;
}

// Check if we need to do a wrap copy
Expand Down Expand Up @@ -277,8 +280,12 @@ int32_t cBufferReadAll(cBuffer_t *inst, uint8_t *data, size_t max_read_size) {

int32_t num_bytes_in_buffer = cBufferAvailableForRead(inst);

if (num_bytes_in_buffer > max_read_size) {
return C_BUFFER_INSUFFICENT;
if (num_bytes_in_buffer < C_BUFFER_SUCCESS) {
return num_bytes_in_buffer;
}

if ((size_t)num_bytes_in_buffer > max_read_size) {
return C_BUFFER_INSUFFICIENT;
}

// Check if there is a wrap in buffer
Expand Down Expand Up @@ -344,6 +351,79 @@ uint8_t cBufferReadByte(cBuffer_t *inst) {
return data;
}

int32_t cBufferReadBytes(cBuffer_t *inst, uint8_t *data, size_t read_size) {
if (inst == NULL || data == NULL) {
return C_BUFFER_NULL_ERROR;
}

int32_t num_bytes_in_buffer = cBufferAvailableForRead(inst);

if (num_bytes_in_buffer < C_BUFFER_SUCCESS) {
return num_bytes_in_buffer;
}

if (read_size > (size_t)num_bytes_in_buffer) {
return C_BUFFER_MISMATCH;
}


// Check if there is a wrap in buffer
if (inst->head < inst->tail) {
size_t bytes_in_first = inst->size - inst->tail;
if (read_size <= bytes_in_first) {
// All requested data is in the first block.
// First read the data up to the wrap
#ifdef NO_MEMCPY
size_t data_ind = 0;
for (size_t ind = inst->tail; ind < inst->size; ind++) {
data[data_ind] = inst->data[ind];
data_ind++;
}
#else
memcpy(data, inst->data + inst->tail, bytes_in_first);
#endif
} else {
// Data is divided before and after wrap
#ifdef NO_MEMCPY
size_t data_ind = 0;
for (size_t ind = inst->tail; ind < inst->size; ind++) {
data[data_ind] = inst->data[ind];
data_ind++;
}
#else
memcpy(data, inst->data + inst->tail, bytes_in_first);
#endif

// Then read the remaining data after the wrap
#ifdef NO_MEMCPY
for (size_t ind = 0; ind < read_size - bytes_in_first; ind++) {
data[data_ind] = inst->data[ind];
data_ind++;
}
#else
memcpy(data + bytes_in_first, inst->data, read_size - bytes_in_first);
#endif
}
} else {
// No data wrap, just read the data into the buffer
#ifdef NO_MEMCPY
size_t buffer_ind = inst->tail;
for (size_t ind = 0; ind < read_size ; ind++) {
data[ind] = inst->data[buffer_ind];
buffer_ind++;
}
#else
// Faster memcpy version
memcpy(data, inst->data + inst->tail, read_size);
#endif
}


inst->tail = MODULO_INC(inst->tail, read_size, inst->size);

return read_size;
}

int32_t cBufferClear(cBuffer_t *inst) {
if (inst == NULL) {
return C_BUFFER_NULL_ERROR;
Expand Down
27 changes: 19 additions & 8 deletions src/c_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
#ifndef C_BUFFER_H
#define C_BUFFER_H

#include "pico/stdlib.h"
#include <stdint.h>
#include <stddef.h>

// The available number of bytes in the C buffer will be one less than the size of the array
#define C_BUFFER_ARRAY_OVERHEAD 1
Expand All @@ -40,9 +41,9 @@

typedef enum {
C_BUFFER_SUCCESS,
C_BUFFER_NULL_ERROR = -301,
C_BUFFER_INSUFFICENT = -302,
C_BUFFER_MISSMATCH = -303,
C_BUFFER_NULL_ERROR = -301,
C_BUFFER_INSUFFICIENT = -302,
C_BUFFER_MISMATCH = -303,
} cBufferErr_t;

typedef struct {
Expand Down Expand Up @@ -133,6 +134,16 @@ int32_t cBufferReadAll(cBuffer_t *inst, uint8_t *data, size_t max_read_size);
*/
uint8_t cBufferReadByte(cBuffer_t *inst);

/**
* Read the next byte from the buffer
* Note: Using this function on emtpy buffers will return 0
* Input: Pointer to buffer instance
* Input: Pointer to data to read into
* Input: Number of bytes to read
* Returns: cBufferErr_t or num of bytes read
*/
int32_t cBufferReadBytes(cBuffer_t *inst, uint8_t *data, size_t read_size);

/**
* Clear a buffer, this resets the head and tail to first element of buffer
* Input: Pointer to buffer instance
Expand All @@ -142,30 +153,30 @@ int32_t cBufferClear(cBuffer_t *inst);

/**
* Rotate the buffer to make sure the data is available in continous memory
* Input: Pointer to buffe instance
* Input: Pointer to buffer instance
* Returns: cBufferErr_t
*/
int32_t cBufferContiguate(cBuffer_t* inst);

/**
* Get pointer to the current first element in the buffer
* Input: Pointer to buffe instance
* Input: Pointer to buffer instance
* Returns: Pointer to element or null if invalid or wrap in buffer
*/
uint8_t *cBufferGetReadPointer(cBuffer_t* inst);

/**
* Get pointer to the current next write element in the buffer
* Note: There is no garantuee that the data is continous
* Input: Pointer to buffe instance
* Input: Pointer to buffer instance
* Returns: Pointer to element
*/
uint8_t *cBufferGetWritePointer(cBuffer_t* inst);

/**
* Increment the amount of data in the buffer without writing anything to the buffer
* Note: There is no garantuee that the data is continous
* Input: Pointer to buffe instance
* Input: Pointer to buffer instance
* Input: Number of bytes to append to tail
* Returns: Pointer to element
*/
Expand Down
Loading