Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 1 | /* 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 | |
| 56 | struct 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 | |
| 80 | alloc_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 | */ |
| 89 | bool 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 | */ |
| 103 | const 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 | |
| 116 | bool _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 Freyther | 476cf33 | 2013-07-03 09:30:02 +0200 | [diff] [blame] | 121 | if (bufi >= rb->size) |
Katerina Barone-Adesi | 3309a43 | 2013-02-21 05:16:29 +0000 | [diff] [blame] | 122 | 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 | */ |
| 133 | size_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 | */ |
| 149 | int 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 | } |