libosmocore/src/strrb.c

172 lines
5.1 KiB
C

/* Ringbuffer implementation, tailored for logging.
* This is a lossy ringbuffer. It keeps up to N of the newest messages,
* overwriting the oldest as newer ones come in.
*
* (C) 2012-2013, Katerina Barone-Adesi <kat.obsc@gmail.com>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*! \file strrb.c
* \brief Lossy string ringbuffer for logging; keeps newest messages.
*/
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <osmocom/core/strrb.h>
/* Ringbuffer assumptions, invarients, and notes:
* - start is the index of the first used index slot in the ring buffer.
* - end is the index of the next index slot in the ring buffer.
* - start == end => buffer is empty
* - Consequence: the buffer can hold at most size - 1 messages
* (if this were not the case, full and empty buffers would be indistinguishable
* given the conventions in this implementation).
* - Whenever the ringbuffer is full, start is advanced. The second oldest
* message becomes unreachable by valid indexes (end is not a valid index)
* and the oldest message is overwritten (if there was a message there, which
* is the case unless this is the first time the ringbuffer becomes full).
*/
/*! \brief Create an empty, initialized osmo_strrb.
* \param[in] ctx The talloc memory context which should own this.
* \param[in] rb_size The number of message slots the osmo_strrb can hold.
* \returns A struct osmo_strrb* on success, NULL in case of error.
*
* This function creates and initializes a ringbuffer.
* Note that the ringbuffer stores at most rb_size - 1 messages.
*/
struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * ctx, size_t rb_size)
{
struct osmo_strrb *rb = NULL;
unsigned int i;
rb = talloc_zero(ctx, struct osmo_strrb);
if (!rb)
goto alloc_error;
/* start and end are zero already, which is correct */
rb->size = rb_size;
rb->buffer = talloc_array(rb, char *, rb->size);
if (!rb->buffer)
goto alloc_error;
for (i = 0; i < rb->size; i++) {
rb->buffer[i] =
talloc_zero_size(rb->buffer, RB_MAX_MESSAGE_SIZE);
if (!rb->buffer[i])
goto alloc_error;
}
return rb;
alloc_error: /* talloc_free(NULL) is safe */
talloc_free(rb);
return NULL;
}
/*! \brief Check if an osmo_strrb is empty.
* \param[in] rb The osmo_strrb to check.
* \returns True if the osmo_strrb is empty, false otherwise.
*/
bool osmo_strrb_is_empty(const struct osmo_strrb *rb)
{
return rb->end == rb->start;
}
/*! \brief Return a pointer to the Nth string in the osmo_strrb.
* \param[in] rb The osmo_strrb to search.
* \param[in] string_index The index sought (N), zero-indexed.
*
* Return a pointer to the Nth string in the osmo_strrb.
* Return NULL if there is no Nth string.
* Note that N is zero-indexed.
* \returns A pointer to the target string on success, NULL in case of error.
*/
const char *osmo_strrb_get_nth(const struct osmo_strrb *rb,
unsigned int string_index)
{
unsigned int offset = string_index + rb->start;
if ((offset >= rb->size) && (rb->start > rb->end))
offset -= rb->size;
if (_osmo_strrb_is_bufindex_valid(rb, offset))
return rb->buffer[offset];
return NULL;
}
bool _osmo_strrb_is_bufindex_valid(const struct osmo_strrb *rb,
unsigned int bufi)
{
if (osmo_strrb_is_empty(rb))
return 0;
if (bufi >= rb->size)
return 0;
if (rb->start < rb->end)
return (bufi >= rb->start) && (bufi < rb->end);
return (bufi < rb->end) || (bufi >= rb->start);
}
/*! \brief Count the number of log messages in an osmo_strrb.
* \param[in] rb The osmo_strrb to count the elements of.
*
* \returns The number of log messages in the osmo_strrb.
*/
size_t osmo_strrb_elements(const struct osmo_strrb *rb)
{
if (rb->end < rb->start)
return rb->end + (rb->size - rb->start);
return rb->end - rb->start;
}
/*! \brief Add a string to the osmo_strrb.
* \param[in] rb The osmo_strrb to add to.
* \param[in] data The string to add.
*
* Add a message to the osmo_strrb.
* Older messages will be overwritten as necessary.
* \returns 0 normally, 1 as a warning (ie, if data was truncated).
*/
int osmo_strrb_add(struct osmo_strrb *rb, const char *data)
{
size_t len = strlen(data);
int ret = 0;
if (len >= RB_MAX_MESSAGE_SIZE) {
len = RB_MAX_MESSAGE_SIZE - 1;
ret = 1;
}
memcpy(rb->buffer[rb->end], data, len);
rb->buffer[rb->end][len] = '\0';
rb->end += 1;
rb->end %= rb->size;
/* The buffer is full; oldest message is forgotten - see notes above */
if (rb->end == rb->start) {
rb->start += 1;
rb->start %= rb->size;
}
return ret;
}