blob: 461fdecca8bc18e12172151b892decde3c1733d9 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file strrb.c
2 * Ringbuffer implementation, tailored for logging.
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +00003 * This is a lossy ringbuffer. It keeps up to N of the newest messages,
4 * overwriting the oldest as newer ones come in.
5 *
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02006 * Ringbuffer assumptions, invarients, and notes:
7 * - start is the index of the first used index slot in the ring buffer.
8 * - end is the index of the next index slot in the ring buffer.
9 * - start == end => buffer is empty
10 * - Consequence: the buffer can hold at most size - 1 messages
11 * (if this were not the case, full and empty buffers would be indistinguishable
12 * given the conventions in this implementation).
13 * - Whenever the ringbuffer is full, start is advanced. The second oldest
14 * message becomes unreachable by valid indexes (end is not a valid index)
15 * and the oldest message is overwritten (if there was a message there, which
16 * is the case unless this is the first time the ringbuffer becomes full).
17 */
18/*
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +000019 * (C) 2012-2013, Katerina Barone-Adesi <kat.obsc@gmail.com>
20 * All Rights Reserved
21 *
Harald Weltee08da972017-11-13 01:00:26 +090022 * SPDX-License-Identifier: GPL-2.0+
23 *
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +000024 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
Harald Welte33e940b2014-10-26 20:52:25 +010026 * the Free Software Foundation; either version 2 of the License, or
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +000027 * (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 *
34 * You should have received a copy of the GNU General Public License along
35 * with this program; if not, write to the Free Software Foundation, Inc.,
36 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37 *
38 */
39
Harald Welte96e2a002017-06-12 21:44:18 +020040/*! \addtogroup utils
41 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020042 * \file strrb.c */
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +000043
44#include <stdio.h>
45#include <string.h>
Harald Welteb592ab72014-03-10 18:12:17 +010046#include <string.h>
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +000047
48#include <osmocom/core/strrb.h>
Harald Weltea5bb2b92017-05-15 12:50:58 +020049#include <osmocom/core/talloc.h>
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +000050
Neels Hofmeyr87e45502017-06-20 00:17:59 +020051/*! Create an empty, initialized osmo_strrb.
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +000052 * \param[in] ctx The talloc memory context which should own this.
53 * \param[in] rb_size The number of message slots the osmo_strrb can hold.
54 * \returns A struct osmo_strrb* on success, NULL in case of error.
55 *
56 * This function creates and initializes a ringbuffer.
57 * Note that the ringbuffer stores at most rb_size - 1 messages.
58 */
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +000059struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * ctx, size_t rb_size)
60{
61 struct osmo_strrb *rb = NULL;
62 unsigned int i;
63
64 rb = talloc_zero(ctx, struct osmo_strrb);
65 if (!rb)
66 goto alloc_error;
67
68 /* start and end are zero already, which is correct */
69 rb->size = rb_size;
70
71 rb->buffer = talloc_array(rb, char *, rb->size);
72 if (!rb->buffer)
73 goto alloc_error;
74 for (i = 0; i < rb->size; i++) {
75 rb->buffer[i] =
76 talloc_zero_size(rb->buffer, RB_MAX_MESSAGE_SIZE);
77 if (!rb->buffer[i])
78 goto alloc_error;
79 }
80
81 return rb;
82
83alloc_error: /* talloc_free(NULL) is safe */
84 talloc_free(rb);
85 return NULL;
86}
87
Neels Hofmeyr87e45502017-06-20 00:17:59 +020088/*! Check if an osmo_strrb is empty.
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +000089 * \param[in] rb The osmo_strrb to check.
90 * \returns True if the osmo_strrb is empty, false otherwise.
91 */
92bool osmo_strrb_is_empty(const struct osmo_strrb *rb)
93{
94 return rb->end == rb->start;
95}
96
Neels Hofmeyr87e45502017-06-20 00:17:59 +020097/*! Return a pointer to the Nth string in the osmo_strrb.
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +000098 * \param[in] rb The osmo_strrb to search.
99 * \param[in] string_index The index sought (N), zero-indexed.
100 *
101 * Return a pointer to the Nth string in the osmo_strrb.
102 * Return NULL if there is no Nth string.
103 * Note that N is zero-indexed.
104 * \returns A pointer to the target string on success, NULL in case of error.
105 */
106const char *osmo_strrb_get_nth(const struct osmo_strrb *rb,
107 unsigned int string_index)
108{
109 unsigned int offset = string_index + rb->start;
110
111 if ((offset >= rb->size) && (rb->start > rb->end))
112 offset -= rb->size;
113 if (_osmo_strrb_is_bufindex_valid(rb, offset))
114 return rb->buffer[offset];
115
116 return NULL;
117}
118
119bool _osmo_strrb_is_bufindex_valid(const struct osmo_strrb *rb,
120 unsigned int bufi)
121{
122 if (osmo_strrb_is_empty(rb))
123 return 0;
Holger Hans Peter Freyther476cf332013-07-03 09:30:02 +0200124 if (bufi >= rb->size)
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +0000125 return 0;
126 if (rb->start < rb->end)
127 return (bufi >= rb->start) && (bufi < rb->end);
128 return (bufi < rb->end) || (bufi >= rb->start);
129}
130
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200131/*! Count the number of log messages in an osmo_strrb.
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +0000132 * \param[in] rb The osmo_strrb to count the elements of.
133 *
134 * \returns The number of log messages in the osmo_strrb.
135 */
136size_t osmo_strrb_elements(const struct osmo_strrb *rb)
137{
138 if (rb->end < rb->start)
139 return rb->end + (rb->size - rb->start);
140
141 return rb->end - rb->start;
142}
143
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200144/*! Add a string to the osmo_strrb.
Katerina Barone-Adesi3309a432013-02-21 05:16:29 +0000145 * \param[in] rb The osmo_strrb to add to.
146 * \param[in] data The string to add.
147 *
148 * Add a message to the osmo_strrb.
149 * Older messages will be overwritten as necessary.
150 * \returns 0 normally, 1 as a warning (ie, if data was truncated).
151 */
152int osmo_strrb_add(struct osmo_strrb *rb, const char *data)
153{
154 size_t len = strlen(data);
155 int ret = 0;
156
157 if (len >= RB_MAX_MESSAGE_SIZE) {
158 len = RB_MAX_MESSAGE_SIZE - 1;
159 ret = 1;
160 }
161
162 memcpy(rb->buffer[rb->end], data, len);
163 rb->buffer[rb->end][len] = '\0';
164
165 rb->end += 1;
166 rb->end %= rb->size;
167
168 /* The buffer is full; oldest message is forgotten - see notes above */
169 if (rb->end == rb->start) {
170 rb->start += 1;
171 rb->start %= rb->size;
172 }
173 return ret;
174}
Harald Welte96e2a002017-06-12 21:44:18 +0200175
176/*! @} */