mirror of
https://github.com/google/pebble.git
synced 2025-05-14 08:12:19 +00:00
186 lines
6.3 KiB
C
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;
|
|
}
|