pebble/src/libutil/circular_buffer.c
Josh Soref e3dd54fda9 spelling: contiguous
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-01-28 21:32:34 -05:00

186 lines
6.3 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "util/circular_buffer.h"
#include "util/assert.h"
#include "util/math.h"
#include <string.h>
static uint16_t get_write_length_available(CircularBuffer* buffer) {
return buffer->buffer_size - buffer->data_length;
}
static uint16_t get_write_index(CircularBuffer* buffer) {
return (buffer->read_index + buffer->data_length) % buffer->buffer_size;
}
void circular_buffer_init(CircularBuffer* buffer, uint8_t* storage, uint16_t storage_size) {
buffer->buffer = storage;
buffer->buffer_size = storage_size;
buffer->read_index = 0;
buffer->data_length = 0;
buffer->write_in_progress = false;
buffer->auto_reset = true;
}
void circular_buffer_init_ex(CircularBuffer* buffer, uint8_t* storage, uint16_t storage_size,
bool auto_reset) {
circular_buffer_init(buffer, storage, storage_size);
buffer->auto_reset = auto_reset;
}
bool circular_buffer_write(CircularBuffer* buffer, const void* data, uint16_t length) {
if (get_write_length_available(buffer) < length) {
return false;
}
// Now we know the message will fit, so no more checking against buffer_read_index required.
uint16_t write_index = get_write_index(buffer);
// Update the data_length member now before we muck with the length parameter
buffer->data_length += length;
const uint16_t remaining_length = buffer->buffer_size - write_index;
if (remaining_length < length) {
// Need to write the message in two chunks around the end of the buffer. Write the first chunk
memcpy(&buffer->buffer[write_index], data, remaining_length);
write_index = 0;
data = ((uint8_t*) data) + remaining_length;
length -= remaining_length;
}
// Write the last chunk
memcpy(&buffer->buffer[write_index], data, length);
return true;
}
uint16_t circular_buffer_write_prepare(CircularBuffer *buffer, uint8_t **data_out) {
if (!get_write_length_available(buffer) || buffer->write_in_progress) {
*data_out = NULL;
return 0;
}
buffer->write_in_progress = true;
const uint16_t write_index = get_write_index(buffer);
*data_out = buffer->buffer + write_index;
if (buffer->read_index > write_index) {
return buffer->read_index - write_index;
}
return buffer->buffer_size - write_index;
}
void circular_buffer_write_finish(CircularBuffer *buffer, uint16_t written_length) {
UTIL_ASSERT(buffer->data_length + written_length <= buffer->buffer_size);
buffer->data_length += written_length;
buffer->write_in_progress = false;
}
bool circular_buffer_read(const CircularBuffer* buffer, uint16_t length, const uint8_t** data_out, uint16_t* length_out) {
if (buffer->data_length < length) {
return false;
}
*data_out = &buffer->buffer[buffer->read_index];
const uint16_t remaining_length = buffer->buffer_size - buffer->read_index;
*length_out = MIN(remaining_length, length);
return true;
}
uint16_t circular_buffer_copy(const CircularBuffer* buffer,
void *data_out,
uint16_t length_to_copy) {
return circular_buffer_copy_offset(buffer, 0, data_out, length_to_copy);
}
uint16_t circular_buffer_copy_offset(const CircularBuffer* buffer, uint16_t start_offset,
uint8_t *data_out, uint16_t length_to_copy) {
if (buffer->data_length <= start_offset) {
return 0;
}
const uint16_t read_index = (buffer->read_index + start_offset) % buffer->buffer_size;
const uint16_t data_length = buffer->data_length - start_offset;
length_to_copy = MIN(length_to_copy, data_length);
// Number of total bytes after the read index to end of buffer:
const uint16_t total_length_to_end = buffer->buffer_size - read_index;
// Number of bytes of after the read index that need to be copied:
const uint16_t end_copy_length = MIN(total_length_to_end, length_to_copy);
memcpy(data_out, &buffer->buffer[read_index], end_copy_length);
// If length_to_copy > total_length_to_end, there is more data at the
// beginning of the circular buffer (wrapped around):
const uint16_t wrapped_length = length_to_copy - end_copy_length;
if (wrapped_length) {
memcpy(data_out + total_length_to_end, buffer->buffer, wrapped_length);
}
return length_to_copy;
}
bool circular_buffer_read_or_copy(const CircularBuffer* buffer, uint8_t **data_out, size_t length,
void *(*malloc_imp)(size_t), bool *caller_should_free) {
UTIL_ASSERT(buffer && malloc_imp && data_out && caller_should_free);
if (buffer->data_length < length) {
return false;
}
const uint16_t contiguous_length = (buffer->buffer_size - buffer->read_index);
const bool should_malloc_and_copy = (length > contiguous_length);
*caller_should_free = should_malloc_and_copy;
if (should_malloc_and_copy) {
*data_out = (uint8_t *) malloc_imp(length);
if (*data_out) {
circular_buffer_copy(buffer, *data_out, length);
} else {
*caller_should_free = false;
return false;
}
} else {
uint16_t length_out;
circular_buffer_read(buffer, length, (const uint8_t **)data_out, &length_out);
}
return true;
}
bool circular_buffer_consume(CircularBuffer* buffer, uint16_t length) {
if (buffer->data_length < length) {
return false;
}
buffer->read_index = (buffer->read_index + length) % buffer->buffer_size;
buffer->data_length -= length;
// Reset read_index if there's no more data, so any newly written data won't wrap
if (buffer->auto_reset && buffer->data_length == 0 && !buffer->write_in_progress) {
buffer->read_index = 0;
}
return true;
}
uint16_t circular_buffer_get_write_space_remaining(const CircularBuffer* buffer) {
return buffer->buffer_size - buffer->data_length;
}
uint16_t circular_buffer_get_read_space_remaining(const CircularBuffer* buffer) {
return buffer->data_length;
}