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