文件circular_buffer.h
#ifndef CIRCULAR_BUFFER_H_
#define CIRCULAR_BUFFER_H_
/// Opaque circular buffer structure
typedef struct circular_buf_t circular_buf_t;
/// Handle type, the way users interact with the API
typedef circular_buf_t* cbuf_handle_t;
/// Pass in a storage buffer and size, returns a circular buffer handle
/// Requires: buffer is not NULL, size > 0
/// Ensures: cbuf has been created and is returned in an empty state
cbuf_handle_t circular_buf_init(uint8_t* buffer, size_t size);
/// Free a circular buffer structure
/// Requires: cbuf is valid and created by circular_buf_init
/// Does not free data buffer; owner is responsible for that
void circular_buf_free(cbuf_handle_t cbuf);
/// Reset the circular buffer to empty, head == tail. Data not cleared
/// Requires: cbuf is valid and created by circular_buf_init
void circular_buf_reset(cbuf_handle_t cbuf);
/// Put version 1 continues to add data if the buffer is full
/// Old data is overwritten
/// Requires: cbuf is valid and created by circular_buf_init
void circular_buf_put(cbuf_handle_t cbuf, uint8_t data);
/// Put Version 2 rejects new data if the buffer is full
/// Requires: cbuf is valid and created by circular_buf_init
/// Returns 0 on success, -1 if buffer is full
int circular_buf_put2(cbuf_handle_t cbuf, uint8_t data);
/// Retrieve a value from the buffer
/// Requires: cbuf is valid and created by circular_buf_init
/// Returns 0 on success, -1 if the buffer is empty
int circular_buf_get(cbuf_handle_t cbuf, uint8_t * data);
/// CHecks if the buffer is empty
/// Requires: cbuf is valid and created by circular_buf_init
/// Returns true if the buffer is empty
bool circular_buf_empty(cbuf_handle_t cbuf);
/// Checks if the buffer is full
/// Requires: cbuf is valid and created by circular_buf_init
/// Returns true if the buffer is full
bool circular_buf_full(cbuf_handle_t cbuf);
/// Check the capacity of the buffer
/// Requires: cbuf is valid and created by circular_buf_init
/// Returns the maximum capacity of the buffer
size_t circular_buf_capacity(cbuf_handle_t cbuf);
/// Check the number of elements stored in the buffer
/// Requires: cbuf is valid and created by circular_buf_init
/// Returns the current number of elements in the buffer
size_t circular_buf_size(cbuf_handle_t cbuf);
//TODO: int circular_buf_get_range(circular_buf_t cbuf, uint8_t *data, size_t len);
//TODO: int circular_buf_put_range(circular_buf_t cbuf, uint8_t * data, size_t len);
#endif //CIRCULAR_BUFFER_H_
文件circular_buffer.c
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <assert.h>
#include "circular_buffer.h"
// The definition of our circular buffer structure is hidden from the user
struct circular_buf_t {
uint8_t * buffer;
size_t head;
size_t tail;
size_t max; //of the buffer
bool full;
};
#pragma mark - Private Functions -
static void advance_pointer(cbuf_handle_t cbuf)
{
assert(cbuf);
if(cbuf->full)
{
cbuf->tail = (cbuf->tail + 1) % cbuf->max;
}
cbuf->head = (cbuf->head + 1) % cbuf->max;
// We mark full because we will advance tail on the next time around
cbuf->full = (cbuf->head == cbuf->tail);
}
static void retreat_pointer(cbuf_handle_t cbuf)
{
assert(cbuf);
cbuf->full = false;
cbuf->tail = (cbuf->tail + 1) % cbuf->max;
}
#pragma mark - APIs -
cbuf_handle_t circular_buf_init(uint8_t* buffer, size_t size)
{
assert(buffer && size);
cbuf_handle_t cbuf = malloc(sizeof(circular_buf_t));
assert(cbuf);
cbuf->buffer = buffer;
cbuf->max = size;
circular_buf_reset(cbuf);
assert(circular_buf_empty(cbuf));
return cbuf;
}
void circular_buf_free(cbuf_handle_t cbuf)
{
assert(cbuf);
free(cbuf);
}
void circular_buf_reset(cbuf_handle_t cbuf)
{
assert(cbuf);
cbuf->head = 0;
cbuf->tail = 0;
cbuf->full = false;
}
size_t circular_buf_size(cbuf_handle_t cbuf)
{
assert(cbuf);
size_t size = cbuf->max;
if(!cbuf->full)
{
if(cbuf->head >= cbuf->tail)
{
size = (cbuf->head - cbuf->tail);
}
else
{
size = (cbuf->max + cbuf->head - cbuf->tail);
}
}
return size;
}
size_t circular_buf_capacity(cbuf_handle_t cbuf)
{
assert(cbuf);
return cbuf->max;
}
void circular_buf_put(cbuf_handle_t cbuf, uint8_t data)
{
assert(cbuf && cbuf->buffer);
cbuf->buffer[cbuf->head] = data;
advance_pointer(cbuf);
}
int circular_buf_put2(cbuf_handle_t cbuf, uint8_t data)
{
int r = -1;
assert(cbuf && cbuf->buffer);
if(!circular_buf_full(cbuf))
{
cbuf->buffer[cbuf->head] = data;
advance_pointer(cbuf);
r = 0;
}
return r;
}
int circular_buf_get(cbuf_handle_t cbuf, uint8_t * data)
{
assert(cbuf && data && cbuf->buffer);
int r = -1;
if(!circular_buf_empty(cbuf))
{
*data = cbuf->buffer[cbuf->tail];
retreat_pointer(cbuf);
r = 0;
}
return r;
}
bool circular_buf_empty(cbuf_handle_t cbuf)
{
assert(cbuf);
return (!cbuf->full && (cbuf->head == cbuf->tail));
}
bool circular_buf_full(cbuf_handle_t cbuf)
{
assert(cbuf);
return cbuf->full;
}
C++版本:
#include <cstdio>
#include <memory>
#include <mutex>
template <class T>
class circular_buffer {
public:
explicit circular_buffer(size_t size) :
buf_(std::unique_ptr<T[]>(new T[size])),
max_size_(size)
{
}
void put(T item)
{
std::lock_guard<std::mutex> lock(mutex_);
buf_[head_] = item;
if(full_)
{
tail_ = (tail_ + 1) % max_size_;
}
head_ = (head_ + 1) % max_size_;
full_ = head_ == tail_;
}
T get()
{
std::lock_guard<std::mutex> lock(mutex_);
if(empty())
{
return T();
}
//Read data and advance the tail (we now have a free space)
auto val = buf_[tail_];
full_ = false;
tail_ = (tail_ + 1) % max_size_;
return val;
}
void reset()
{
std::lock_guard<std::mutex> lock(mutex_);
head_ = tail_;
full_ = false;
}
bool empty() const
{
//if head and tail are equal, we are empty
return (!full_ && (head_ == tail_));
}
bool full() const
{
//If tail is ahead the head by 1, we are full
return full_;
}
size_t capacity() const
{
return max_size_;
}
size_t size() const
{
size_t size = max_size_;
if(!full_)
{
if(head_ >= tail_)
{
size = head_ - tail_;
}
else
{
size = max_size_ + head_ - tail_;
}
}
return size;
}
private:
std::mutex mutex_;
std::unique_ptr<T[]> buf_;
size_t head_ = 0;
size_t tail_ = 0;
const size_t max_size_;
bool full_ = 0;
};
int main(void)
{
circular_buffer<uint32_t> circle(10);
printf("\n === CPP Circular buffer check ===\n");
printf("Size: %zu, Capacity: %zu\n", circle.size(), circle.capacity());
uint32_t x = 1;
printf("Put 1, val: %d\n", x);
circle.put(x);
x = circle.get();
printf("Popped: %d\n", x);
printf("Empty: %d\n", circle.empty());
printf("Adding %zu values\n", circle.capacity() - 1);
for(uint32_t i = 0; i < circle.capacity() - 1; i++)
{
circle.put(i);
}
circle.reset();
printf("Full: %d\n", circle.full());
printf("Adding %zu values\n", circle.capacity());
for(uint32_t i = 0; i < circle.capacity(); i++)
{
circle.put(i);
}
printf("Full: %d\n", circle.full());
printf("Reading back values: ");
while(!circle.empty())
{
printf("%u ", circle.get());
}
printf("\n");
printf("Adding 15 values\n");
for(uint32_t i = 0; i < circle.size() + 5; i++)
{
circle.put(i);
}
printf("Full: %d\n", circle.full());
printf("Reading back values: ");
while(!circle.empty())
{
printf("%u ", circle.get());
}
printf("\n");
printf("Empty: %d\n", circle.empty());
printf("Full: %d\n", circle.full());
return 0;
}
参考文献:
https://embeddedartistry.com/blog/2017/4/6/circular-buffers-in-cc
https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/circular_buffer.cpp