Neels Hofmeyr | 17518fe | 2017-06-20 04:35:06 +0200 | [diff] [blame] | 1 | /*! \file strrb.c |
| 2 | * Ringbuffer implementation, tailored for logging. |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 3 | * 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 Hofmeyr | 17518fe | 2017-06-20 04:35:06 +0200 | [diff] [blame] | 6 | * 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-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 19 | * (C) 2012-2013, Katerina Barone-Adesi <kat.obsc@gmail.com> |
| 20 | * All Rights Reserved |
| 21 | * |
Harald Welte | e08da97 | 2017-11-13 01:00:26 +0900 | [diff] [blame] | 22 | * SPDX-License-Identifier: GPL-2.0+ |
| 23 | * |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 24 | * 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 Welte | 33e940b | 2014-10-26 20:52:25 +0100 | [diff] [blame] | 26 | * the Free Software Foundation; either version 2 of the License, or |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 27 | * (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 | * |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 34 | */ |
| 35 | |
Harald Welte | 96e2a00 | 2017-06-12 21:44:18 +0200 | [diff] [blame] | 36 | /*! \addtogroup utils |
| 37 | * @{ |
Neels Hofmeyr | 17518fe | 2017-06-20 04:35:06 +0200 | [diff] [blame] | 38 | * \file strrb.c */ |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 39 | |
| 40 | #include <stdio.h> |
| 41 | #include <string.h> |
Harald Welte | b592ab7 | 2014-03-10 18:12:17 +0100 | [diff] [blame] | 42 | #include <string.h> |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 43 | |
| 44 | #include <osmocom/core/strrb.h> |
Harald Welte | a5bb2b9 | 2017-05-15 12:50:58 +0200 | [diff] [blame] | 45 | #include <osmocom/core/talloc.h> |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 46 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 47 | /*! Create an empty, initialized osmo_strrb. |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 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 | */ |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 55 | struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * ctx, size_t rb_size) |
| 56 | { |
| 57 | struct osmo_strrb *rb = NULL; |
| 58 | unsigned int i; |
| 59 | |
| 60 | rb = talloc_zero(ctx, struct osmo_strrb); |
| 61 | if (!rb) |
| 62 | goto alloc_error; |
| 63 | |
| 64 | /* start and end are zero already, which is correct */ |
| 65 | rb->size = rb_size; |
| 66 | |
| 67 | rb->buffer = talloc_array(rb, char *, rb->size); |
| 68 | if (!rb->buffer) |
| 69 | goto alloc_error; |
| 70 | for (i = 0; i < rb->size; i++) { |
| 71 | rb->buffer[i] = |
| 72 | talloc_zero_size(rb->buffer, RB_MAX_MESSAGE_SIZE); |
| 73 | if (!rb->buffer[i]) |
| 74 | goto alloc_error; |
| 75 | } |
| 76 | |
| 77 | return rb; |
| 78 | |
| 79 | alloc_error: /* talloc_free(NULL) is safe */ |
| 80 | talloc_free(rb); |
| 81 | return NULL; |
| 82 | } |
| 83 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 84 | /*! Check if an osmo_strrb is empty. |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 85 | * \param[in] rb The osmo_strrb to check. |
| 86 | * \returns True if the osmo_strrb is empty, false otherwise. |
| 87 | */ |
| 88 | bool osmo_strrb_is_empty(const struct osmo_strrb *rb) |
| 89 | { |
| 90 | return rb->end == rb->start; |
| 91 | } |
| 92 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 93 | /*! Return a pointer to the Nth string in the osmo_strrb. |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 94 | * \param[in] rb The osmo_strrb to search. |
| 95 | * \param[in] string_index The index sought (N), zero-indexed. |
| 96 | * |
| 97 | * Return a pointer to the Nth string in the osmo_strrb. |
| 98 | * Return NULL if there is no Nth string. |
| 99 | * Note that N is zero-indexed. |
| 100 | * \returns A pointer to the target string on success, NULL in case of error. |
| 101 | */ |
| 102 | const char *osmo_strrb_get_nth(const struct osmo_strrb *rb, |
| 103 | unsigned int string_index) |
| 104 | { |
| 105 | unsigned int offset = string_index + rb->start; |
| 106 | |
| 107 | if ((offset >= rb->size) && (rb->start > rb->end)) |
| 108 | offset -= rb->size; |
| 109 | if (_osmo_strrb_is_bufindex_valid(rb, offset)) |
| 110 | return rb->buffer[offset]; |
| 111 | |
| 112 | return NULL; |
| 113 | } |
| 114 | |
| 115 | bool _osmo_strrb_is_bufindex_valid(const struct osmo_strrb *rb, |
| 116 | unsigned int bufi) |
| 117 | { |
| 118 | if (osmo_strrb_is_empty(rb)) |
| 119 | return 0; |
Holger Hans Peter Freyther | 476cf33 | 2013-07-03 09:30:02 +0200 | [diff] [blame] | 120 | if (bufi >= rb->size) |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 121 | return 0; |
| 122 | if (rb->start < rb->end) |
| 123 | return (bufi >= rb->start) && (bufi < rb->end); |
| 124 | return (bufi < rb->end) || (bufi >= rb->start); |
| 125 | } |
| 126 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 127 | /*! Count the number of log messages in an osmo_strrb. |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 128 | * \param[in] rb The osmo_strrb to count the elements of. |
| 129 | * |
| 130 | * \returns The number of log messages in the osmo_strrb. |
| 131 | */ |
| 132 | size_t osmo_strrb_elements(const struct osmo_strrb *rb) |
| 133 | { |
| 134 | if (rb->end < rb->start) |
| 135 | return rb->end + (rb->size - rb->start); |
| 136 | |
| 137 | return rb->end - rb->start; |
| 138 | } |
| 139 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 140 | /*! Add a string to the osmo_strrb. |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 141 | * \param[in] rb The osmo_strrb to add to. |
| 142 | * \param[in] data The string to add. |
| 143 | * |
| 144 | * Add a message to the osmo_strrb. |
| 145 | * Older messages will be overwritten as necessary. |
| 146 | * \returns 0 normally, 1 as a warning (ie, if data was truncated). |
| 147 | */ |
| 148 | int osmo_strrb_add(struct osmo_strrb *rb, const char *data) |
| 149 | { |
| 150 | size_t len = strlen(data); |
| 151 | int ret = 0; |
| 152 | |
| 153 | if (len >= RB_MAX_MESSAGE_SIZE) { |
| 154 | len = RB_MAX_MESSAGE_SIZE - 1; |
| 155 | ret = 1; |
| 156 | } |
| 157 | |
| 158 | memcpy(rb->buffer[rb->end], data, len); |
| 159 | rb->buffer[rb->end][len] = '\0'; |
| 160 | |
| 161 | rb->end += 1; |
| 162 | rb->end %= rb->size; |
| 163 | |
| 164 | /* The buffer is full; oldest message is forgotten - see notes above */ |
| 165 | if (rb->end == rb->start) { |
| 166 | rb->start += 1; |
| 167 | rb->start %= rb->size; |
| 168 | } |
| 169 | return ret; |
| 170 | } |
Harald Welte | 96e2a00 | 2017-06-12 21:44:18 +0200 | [diff] [blame] | 171 | |
| 172 | /*! @} */ |