Files
neovim/src/nvim/rbuffer.c

228 lines
5.8 KiB
C

// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include "nvim/memory.h"
#include "nvim/vim.h"
#include "nvim/rbuffer.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "rbuffer.c.generated.h"
#endif
/// Creates a new `RBuffer` instance.
RBuffer *rbuffer_new(size_t capacity)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
{
if (!capacity) {
capacity = 0x10000;
}
RBuffer *rv = xcalloc(1, sizeof(RBuffer) + capacity);
rv->full_cb = rv->nonfull_cb = NULL;
rv->data = NULL;
rv->size = 0;
rv->write_ptr = rv->read_ptr = rv->start_ptr;
rv->end_ptr = rv->start_ptr + capacity;
rv->temp = NULL;
return rv;
}
void rbuffer_free(RBuffer *buf)
{
xfree(buf->temp);
xfree(buf);
}
size_t rbuffer_size(RBuffer *buf) FUNC_ATTR_NONNULL_ALL
{
return buf->size;
}
size_t rbuffer_capacity(RBuffer *buf) FUNC_ATTR_NONNULL_ALL
{
return (size_t)(buf->end_ptr - buf->start_ptr);
}
size_t rbuffer_space(RBuffer *buf) FUNC_ATTR_NONNULL_ALL
{
return rbuffer_capacity(buf) - buf->size;
}
/// Return a pointer to a raw buffer containing the first empty slot available
/// for writing. The second argument is a pointer to the maximum number of
/// bytes that could be written.
///
/// It is necessary to call this function twice to ensure all empty space was
/// used. See RBUFFER_UNTIL_FULL for a macro that simplifies this task.
char *rbuffer_write_ptr(RBuffer *buf, size_t *write_count) FUNC_ATTR_NONNULL_ALL
{
if (buf->size == rbuffer_capacity(buf)) {
*write_count = 0;
return NULL;
}
if (buf->write_ptr >= buf->read_ptr) {
*write_count = (size_t)(buf->end_ptr - buf->write_ptr);
} else {
*write_count = (size_t)(buf->read_ptr - buf->write_ptr);
}
return buf->write_ptr;
}
// Reset an RBuffer so read_ptr is at the beginning of the memory. If
// necessary, this moves existing data by allocating temporary memory.
void rbuffer_reset(RBuffer *buf) FUNC_ATTR_NONNULL_ALL
{
size_t temp_size;
if ((temp_size = rbuffer_size(buf))) {
if (buf->temp == NULL) {
buf->temp = xcalloc(1, rbuffer_capacity(buf));
}
rbuffer_read(buf, buf->temp, buf->size);
}
buf->read_ptr = buf->write_ptr = buf->start_ptr;
if (temp_size) {
rbuffer_write(buf, buf->temp, temp_size);
}
}
/// Adjust `rbuffer` write pointer to reflect produced data. This is called
/// automatically by `rbuffer_write`, but when using `rbuffer_write_ptr`
/// directly, this needs to called after the data was copied to the internal
/// buffer. The write pointer will be wrapped if required.
void rbuffer_produced(RBuffer *buf, size_t count) FUNC_ATTR_NONNULL_ALL
{
assert(count && count <= rbuffer_space(buf));
buf->write_ptr += count;
if (buf->write_ptr >= buf->end_ptr) {
// wrap around
buf->write_ptr -= rbuffer_capacity(buf);
}
buf->size += count;
if (buf->full_cb && !rbuffer_space(buf)) {
buf->full_cb(buf, buf->data);
}
}
/// Return a pointer to a raw buffer containing the first byte available
/// for reading. The second argument is a pointer to the maximum number of
/// bytes that could be read.
///
/// It is necessary to call this function twice to ensure all available bytes
/// were read. See RBUFFER_UNTIL_EMPTY for a macro that simplifies this task.
char *rbuffer_read_ptr(RBuffer *buf, size_t *read_count) FUNC_ATTR_NONNULL_ALL
{
if (!buf->size) {
*read_count = 0;
return buf->read_ptr;
}
if (buf->read_ptr < buf->write_ptr) {
*read_count = (size_t)(buf->write_ptr - buf->read_ptr);
} else {
*read_count = (size_t)(buf->end_ptr - buf->read_ptr);
}
return buf->read_ptr;
}
/// Adjust `rbuffer` read pointer to reflect consumed data. This is called
/// automatically by `rbuffer_read`, but when using `rbuffer_read_ptr`
/// directly, this needs to called after the data was copied from the internal
/// buffer. The read pointer will be wrapped if required.
void rbuffer_consumed(RBuffer *buf, size_t count)
FUNC_ATTR_NONNULL_ALL
{
assert(count && count <= buf->size);
buf->read_ptr += count;
if (buf->read_ptr >= buf->end_ptr) {
buf->read_ptr -= rbuffer_capacity(buf);
}
bool was_full = buf->size == rbuffer_capacity(buf);
buf->size -= count;
if (buf->nonfull_cb && was_full) {
buf->nonfull_cb(buf, buf->data);
}
}
// Higher level functions for copying from/to RBuffer instances and data
// pointers
size_t rbuffer_write(RBuffer *buf, const char *src, size_t src_size)
FUNC_ATTR_NONNULL_ALL
{
size_t size = src_size;
RBUFFER_UNTIL_FULL(buf, wptr, wcnt) {
size_t copy_count = MIN(src_size, wcnt);
memcpy(wptr, src, copy_count);
rbuffer_produced(buf, copy_count);
if (!(src_size -= copy_count)) {
return size;
}
src += copy_count;
}
return size - src_size;
}
size_t rbuffer_read(RBuffer *buf, char *dst, size_t dst_size)
FUNC_ATTR_NONNULL_ALL
{
size_t size = dst_size;
RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) {
size_t copy_count = MIN(dst_size, rcnt);
memcpy(dst, rptr, copy_count);
rbuffer_consumed(buf, copy_count);
if (!(dst_size -= copy_count)) {
return size;
}
dst += copy_count;
}
return size - dst_size;
}
char *rbuffer_get(RBuffer *buf, size_t index)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
assert(index < buf->size);
char *rptr = buf->read_ptr + index;
if (rptr >= buf->end_ptr) {
rptr -= rbuffer_capacity(buf);
}
return rptr;
}
int rbuffer_cmp(RBuffer *buf, const char *str, size_t count)
FUNC_ATTR_NONNULL_ALL
{
assert(count <= buf->size);
size_t rcnt;
(void)rbuffer_read_ptr(buf, &rcnt);
size_t n = MIN(count, rcnt);
int rv = memcmp(str, buf->read_ptr, n);
count -= n;
size_t remaining = buf->size - rcnt;
if (rv || !count || !remaining) {
return rv;
}
return memcmp(str + n, buf->start_ptr, count);
}