├── README.md ├── ijcringbuffer.h └── ijcringbuffer_test.c /README.md: -------------------------------------------------------------------------------- 1 | ijcringbuffer.h - v0.01 - public domain - [@incrediblejr][4], Dec 2015 2 | 3 | no warranty implied; use at your own risk 4 | 5 | A continuous ringbuffer ala [@rygorous][2] ['magic ring buffer'][1] (without the magic) 6 | 7 | ABOUT 8 | 9 | ijcringbuffer is a ringbuffer that handles variable sized commands/data, inspired 10 | by [@rygorous][2] blog post about the ['magic ring buffer'][1] (but implemented sans the magic). 11 | 12 | The ring buffer guarantees that data will written as-is or not at all (no splitting). 13 | This can be used when the consumer expects the data be linear in memory. 14 | 15 | Usage examples (and tests) is at the bottom of the file in the IJCRINGBUFFER_TEST section. 16 | 17 | USAGE 18 | 19 | The ringbuffer is implemented as a [stb-style header-file library][3] which means that 20 | in *ONE* source file, put: 21 | 22 | #define IJCRINGBUFFER_IMPLEMENTATION 23 | // if custom assert and memcpy is wanted/needed 24 | // (and no dependencies on assert.h + string.h) 25 | // you can define/override the default by : 26 | // #define IJCRB_assert custom_assert 27 | // #define IJCRB_memcpy custom_memcpy 28 | #include "ijcringbuffer.h" 29 | 30 | Other source files should just include ijcringbuffer.h 31 | 32 | 33 | NOTES 34 | 35 | There are a myriad of ways to implement said ring buffer, especially if you 36 | would allow the producer to modify the read-cursor (but you have to 37 | have _some_ standards). 38 | 39 | I also wanted to solve the (not-in-real-life-?) problem when the read-cursor catches 40 | up with the write-cursor, giving the producer only part of the buffer (worst case 41 | half of the buffer) to write into, when we should be able to start writing to the 42 | start of the buffer (essentially doing a buffer reset). 43 | 44 | [1]: https://fgiesen.wordpress.com/2012/07/21/the-magic-ring-buffer/ 45 | [2]: https://twitter.com/rygorous 46 | [3]: https://github.com/nothings/stb 47 | [4]: https://twitter.com/incrediblejr 48 | 49 | LICENSE 50 | 51 | This software is in the public domain. Where that dedication is not 52 | recognized, you are granted a perpetual, irrevocable license to copy, 53 | distribute, and modify this file as you see fit. 54 | -------------------------------------------------------------------------------- /ijcringbuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | ijcringbuffer.h - v0.01 - public domain - @incrediblejr, Dec 2015 3 | 4 | no warranty implied; use at your own risk 5 | 6 | A continuous ringbuffer ala [@rygorous][2] ['magic ring buffer'][1] (without the magic) 7 | 8 | ABOUT 9 | 10 | ijcringbuffer is a ringbuffer that handles variable sized commands/data, inspired 11 | by [@rygorous][2] blog post about the ['magic ring buffer'][1] (but implemented sans the magic). 12 | 13 | The ring buffer guarantees that data will written as-is or not at all (no splitting). 14 | This can be used when the consumer expects the data be linear in memory. 15 | 16 | Usage examples (and tests) is at the bottom of the file in the IJCRINGBUFFER_TEST section. 17 | 18 | USAGE 19 | 20 | The ringbuffer is implemented as a [stb-style header-file library][3] which means that 21 | in *ONE* source file, put: 22 | 23 | #define IJCRINGBUFFER_IMPLEMENTATION 24 | // if custom assert and memcpy is wanted/needed 25 | // (and no dependencies on assert.h + string.h) 26 | // you can define/override the default by : 27 | // #define IJCRB_assert custom_assert 28 | // #define IJCRB_memcpy custom_memcpy 29 | #include "ijcringbuffer.h" 30 | 31 | Other source files should just include ijcringbuffer.h 32 | 33 | 34 | NOTES 35 | 36 | There are a myriad of ways to implement said ring buffer, especially if you 37 | would allow the producer to modify the read-cursor (but you have to 38 | have _some_ standards). 39 | 40 | I also wanted to solve the (not-in-real-life-?) problem when the read-cursor catches 41 | up with the write-cursor, giving the producer only part of the buffer (worst case 42 | half of the buffer) to write into, when we should be able to start writing to the 43 | start of the buffer (essentially doing a buffer reset). 44 | 45 | [1]: https://fgiesen.wordpress.com/2012/07/21/the-magic-ring-buffer/ 46 | [2]: https://twitter.com/rygorous 47 | [3]: https://github.com/nothings/stb 48 | 49 | LICENSE 50 | 51 | This software is in the public domain. Where that dedication is not 52 | recognized, you are granted a perpetual, irrevocable license to copy, 53 | distribute, and modify this file as you see fit. 54 | 55 | */ 56 | 57 | #ifndef INCLUDE_IJCRINGBUFFER_H 58 | #define INCLUDE_IJCRINGBUFFER_H 59 | 60 | #ifdef IJCRB_STATIC 61 | #define IJCRB_DEF static 62 | #else 63 | #define IJCRB_DEF extern 64 | #endif 65 | 66 | #ifdef __cplusplus 67 | extern "C" { 68 | #endif 69 | 70 | typedef struct ijcringbuffer { 71 | unsigned char *data; 72 | unsigned size, mask; /* mask could be calculated each time (size-1) */ 73 | unsigned read_cursor, write_cursor, wrap_cursor; 74 | } ijcringbuffer; 75 | 76 | /* data_size must be a power of 2 */ 77 | IJCRB_DEF void ijcringbuffer_init(ijcringbuffer *self, void *data, unsigned data_size); 78 | IJCRB_DEF void ijcringbuffer_reset(ijcringbuffer *self); 79 | 80 | /* returns the number of continuous bytes that can be read/consumed */ 81 | IJCRB_DEF unsigned ijcringbuffer_consumeable_size_continuous(ijcringbuffer *self); 82 | 83 | /* returns the number of bytes that can be read/consumed. 84 | (as the buffer can split/wrap, this can be more than ijcringbuffer_consumeable_size_continuous) 85 | */ 86 | IJCRB_DEF unsigned ijcringbuffer_consumeable_size(ijcringbuffer *self); 87 | 88 | /* advance the consume/read cursor (essentially freeing memory to be used for produce/write) */ 89 | IJCRB_DEF void ijcringbuffer_consume(ijcringbuffer *self, unsigned size); 90 | 91 | /* returns the current read/consume pointer */ 92 | IJCRB_DEF void *ijcringbuffer_peek(ijcringbuffer *self); 93 | 94 | IJCRB_DEF int ijcringbuffer_is_empty(ijcringbuffer *self); 95 | IJCRB_DEF int ijcringbuffer_is_full(ijcringbuffer *self); 96 | 97 | /* returns 1 on success (insize bytes is written), returns 0 on fail (0 bytes written) */ 98 | IJCRB_DEF int ijcringbuffer_produce(ijcringbuffer *self, void *indata, unsigned insize); 99 | 100 | #ifdef __cplusplus 101 | } 102 | #endif 103 | #endif 104 | 105 | #ifdef IJCRINGBUFFER_IMPLEMENTATION 106 | 107 | #ifndef IJCRB_assert 108 | #include 109 | #define IJCRB_assert assert 110 | #endif 111 | 112 | #ifndef IJCRB_memcpy 113 | #include 114 | #define IJCRB_memcpy memcpy 115 | #endif 116 | 117 | static unsigned ijcringbuffer__difference(unsigned a, unsigned b) 118 | { 119 | unsigned ab = a - b; 120 | unsigned ba = b - a; 121 | return ab > ba ? ba : ab; 122 | } 123 | 124 | IJCRB_DEF void ijcringbuffer_init(ijcringbuffer *self, void *data, unsigned data_size) 125 | { 126 | IJCRB_assert(data_size > 0 && !(data_size & (data_size - 1))); 127 | self->data = (unsigned char*)data; 128 | 129 | self->size = data_size; 130 | self->mask = data_size - 1; 131 | self->read_cursor = self->write_cursor = self->wrap_cursor = 0; 132 | } 133 | 134 | IJCRB_DEF void ijcringbuffer_reset(ijcringbuffer *self) 135 | { 136 | self->read_cursor = self->write_cursor = self->wrap_cursor = 0; 137 | } 138 | 139 | static int ijcringbuffer__is_split(ijcringbuffer *self) 140 | { 141 | return ijcringbuffer__difference(self->read_cursor, self->write_cursor) > self->size; 142 | } 143 | 144 | IJCRB_DEF void *ijcringbuffer_peek(ijcringbuffer *self) 145 | { 146 | unsigned peek_rc; 147 | if (self->read_cursor == self->wrap_cursor && ijcringbuffer__is_split(self)) 148 | peek_rc = 0; 149 | else 150 | peek_rc = self->read_cursor&self->mask; 151 | 152 | return self->data+peek_rc; 153 | } 154 | 155 | static unsigned ijcringbuffer__consumeable_size(ijcringbuffer *self, int continuous) 156 | { 157 | unsigned cs; 158 | if (ijcringbuffer__is_split(self)) { 159 | if (self->read_cursor == self->wrap_cursor) { 160 | /* check if the buffer is a wrap + full fill */ 161 | unsigned masked_write = self->write_cursor&self->mask; 162 | cs = masked_write ? masked_write : self->size; 163 | } else { 164 | IJCRB_assert(self->size > ijcringbuffer__difference(self->wrap_cursor, self->read_cursor)); 165 | cs = ((self->wrap_cursor-self->read_cursor)&self->mask) + (continuous ? 0 : (self->write_cursor&self->mask)); 166 | } 167 | } else { 168 | cs = self->write_cursor - self->read_cursor; 169 | } 170 | IJCRB_assert(self->size >= cs); 171 | return cs; 172 | } 173 | 174 | IJCRB_DEF unsigned ijcringbuffer_consumeable_size_continuous(ijcringbuffer *self) 175 | { 176 | return ijcringbuffer__consumeable_size(self, 1); 177 | } 178 | 179 | IJCRB_DEF unsigned ijcringbuffer_consumeable_size(ijcringbuffer *self) 180 | { 181 | return ijcringbuffer__consumeable_size(self, 0); 182 | } 183 | 184 | IJCRB_DEF void ijcringbuffer_consume(ijcringbuffer *self, unsigned size) 185 | { 186 | IJCRB_assert(ijcringbuffer_consumeable_size_continuous(self) >= size); 187 | if (self->read_cursor == self->wrap_cursor && ijcringbuffer__is_split(self)) 188 | self->read_cursor += self->size + (self->size - (self->read_cursor&self->mask)) + size; 189 | else 190 | self->read_cursor += size; 191 | } 192 | 193 | IJCRB_DEF int ijcringbuffer_is_empty(ijcringbuffer *self) { return self->write_cursor == self->read_cursor; } 194 | IJCRB_DEF int ijcringbuffer_is_full(ijcringbuffer *self) { return ijcringbuffer_consumeable_size(self) == self->size; } 195 | 196 | IJCRB_DEF int ijcringbuffer_produce(ijcringbuffer *self, void *indata, unsigned insize) 197 | { 198 | unsigned masked_write = self->write_cursor&self->mask; 199 | int is_empty; 200 | 201 | if (ijcringbuffer__is_split(self)) { 202 | unsigned avail_write_size; 203 | if (self->wrap_cursor == self->read_cursor) { 204 | avail_write_size = masked_write ? (self->size - masked_write) : 0; 205 | } 206 | else { 207 | IJCRB_assert((self->read_cursor&self->mask) >= (self->write_cursor&self->mask)); 208 | avail_write_size = (self->read_cursor - self->write_cursor) & self->mask; 209 | } 210 | 211 | if (avail_write_size >= insize) { 212 | IJCRB_memcpy(self->data + masked_write, indata, insize); 213 | self->write_cursor += insize; 214 | return 1; 215 | } 216 | return 0; 217 | } 218 | 219 | is_empty = ijcringbuffer_is_empty(self); 220 | /* 221 | check if we are empty and write cursor is not at the start, then we 222 | start writing to the front and 'split' the buffer (giving us an 'auto-reset') 223 | */ 224 | if (masked_write && is_empty && self->size >= insize) 225 | goto write_to_front; 226 | 227 | /* 228 | if !masked_write then 229 | has_filled_the_whole_buffer 230 | _or_ 231 | has_not_filled_anything -> read == write -> empty 232 | */ 233 | if (!masked_write && !is_empty) { 234 | /* we can not write to _back_ but we still have to check if we can write to front */ 235 | goto check_front; 236 | } 237 | 238 | /* check write to back */ 239 | if ((self->size - masked_write) >= insize) { 240 | IJCRB_memcpy(self->data + masked_write, indata, insize); 241 | self->write_cursor += insize; 242 | return 1; 243 | } 244 | 245 | check_front: 246 | /* check write to front */ 247 | if ((self->read_cursor&self->mask) >= insize) { 248 | write_to_front: 249 | self->wrap_cursor = self->write_cursor; 250 | IJCRB_memcpy(self->data, indata, insize); 251 | /* 'split' the buffer */ 252 | self->write_cursor += self->size + (self->size - masked_write) + insize; 253 | IJCRB_assert((self->write_cursor & self->mask) == insize); 254 | return 1; 255 | } 256 | 257 | return 0; 258 | } 259 | 260 | #if defined(IJCRINGBUFFER_TEST) || defined(IJCRINGBUFFER_TEST_MAIN) 261 | 262 | static void ijcringbuffer_test(void) 263 | { 264 | unsigned t; 265 | ijcringbuffer ring, *r = ˚ 266 | char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 267 | 268 | char temp[9]; memset(temp, 0, sizeof temp); 269 | ijcringbuffer_init(r, temp, sizeof temp - 1); 270 | 271 | /* */ 272 | IJCRB_assert(ijcringbuffer_produce(r, hex, 8)); 273 | IJCRB_assert(!ijcringbuffer_produce(r, hex, 1)); 274 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 8); 275 | IJCRB_assert(memcmp(ijcringbuffer_peek(r), hex, 8) == 0); 276 | ijcringbuffer_consume(r, 8); 277 | IJCRB_assert(ijcringbuffer_produce(r, hex + 4, 7)); 278 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 7); 279 | IJCRB_assert(memcmp(ijcringbuffer_peek(r), hex + 4, 7) == 0); 280 | ijcringbuffer_consume(r, 6); 281 | IJCRB_assert(ijcringbuffer_produce(r, hex, 6)); 282 | IJCRB_assert(memcmp(ijcringbuffer_peek(r), hex + 4 + 6, 1) == 0); 283 | ijcringbuffer_consume(r, 1); 284 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 6); 285 | IJCRB_assert(memcmp(ijcringbuffer_peek(r), hex, 6) == 0); 286 | ijcringbuffer_consume(r, 6); 287 | IJCRB_assert(ijcringbuffer_is_empty(r)); 288 | 289 | /* */ 290 | ijcringbuffer_reset(r); memset(temp, 0, sizeof temp); 291 | 292 | IJCRB_assert(ijcringbuffer_produce(r, hex, 6)); 293 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 6); 294 | ijcringbuffer_consume(r, 5); 295 | IJCRB_assert(ijcringbuffer_produce(r, hex, 4)); 296 | IJCRB_assert(ijcringbuffer_produce(r, hex, 1)); 297 | IJCRB_assert(!ijcringbuffer_produce(r, hex, 1)); 298 | 299 | /* */ 300 | ijcringbuffer_reset(r); memset(temp, 0, sizeof temp); 301 | 302 | t = 0xffffffffu - 3u; 303 | r->read_cursor = r->write_cursor = t; 304 | IJCRB_assert(ijcringbuffer_produce(r, hex, 6)); 305 | IJCRB_assert(ijcringbuffer_produce(r, hex, 2)); 306 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 8); 307 | IJCRB_assert(memcmp(ijcringbuffer_peek(r), hex, 6) == 0); 308 | ijcringbuffer_consume(r, 6); 309 | 310 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 2); 311 | IJCRB_assert(memcmp(ijcringbuffer_peek(r), hex, 2) == 0); 312 | ijcringbuffer_consume(r, 2); 313 | 314 | /* */ 315 | ijcringbuffer_reset(r); memset(temp, 0, sizeof temp); 316 | 317 | t = 0xffffffffu - 3u; 318 | r->write_cursor = t; 319 | r->read_cursor = t-1; 320 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 1); 321 | 322 | IJCRB_assert(ijcringbuffer_produce(r, hex, 4)); 323 | IJCRB_assert(ijcringbuffer_consumeable_size_continuous(r) == 5); 324 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 5); 325 | IJCRB_assert(!ijcringbuffer_produce(r, hex, 4)); 326 | IJCRB_assert(ijcringbuffer_produce(r, hex, 3)); 327 | IJCRB_assert(!ijcringbuffer_produce(r, hex, 1)); 328 | IJCRB_assert(ijcringbuffer_consumeable_size_continuous(r) == 5); 329 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 8); 330 | 331 | /* */ 332 | ijcringbuffer_reset(r); memset(temp, 0, sizeof temp); 333 | IJCRB_assert(ijcringbuffer_produce(r, hex, 6)); 334 | ijcringbuffer_consume(r, 5); 335 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 1); 336 | IJCRB_assert(ijcringbuffer_produce(r, hex, 2)); 337 | IJCRB_assert(ijcringbuffer_consumeable_size_continuous(r) == 3); 338 | IJCRB_assert(ijcringbuffer_produce(r, hex, 5)); 339 | IJCRB_assert(ijcringbuffer_consumeable_size_continuous(r) == 3); 340 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 8); 341 | 342 | /* */ 343 | ijcringbuffer_reset(r); memset(temp, 0, sizeof temp); 344 | IJCRB_assert(ijcringbuffer_produce(r, hex, 8)); 345 | ijcringbuffer_consume(r, 1); 346 | IJCRB_assert(ijcringbuffer_consumeable_size_continuous(r) == 7); 347 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 7); 348 | IJCRB_assert(ijcringbuffer_produce(r, hex + 1, 1)); 349 | IJCRB_assert(ijcringbuffer_consumeable_size_continuous(r) == 7); 350 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 8); 351 | ijcringbuffer_consume(r, 7); 352 | IJCRB_assert(ijcringbuffer_consumeable_size_continuous(r) == 1); 353 | IJCRB_assert(ijcringbuffer_consumeable_size(r) == 1); 354 | 355 | IJCRB_assert(memcmp(ijcringbuffer_peek(r), hex + 1, 1) == 0); 356 | } 357 | 358 | #ifdef IJCRINGBUFFER_TEST_MAIN 359 | int main(int argc, char **argv) 360 | { 361 | (void)(argc, argv); 362 | ijcringbuffer_test(); 363 | return 0; 364 | } 365 | #endif /* IJCRINGBUFFER_TEST_MAIN */ 366 | 367 | #endif /* IJCRINGBUFFER_TEST */ 368 | 369 | #endif /* IJCRINGBUFFER_IMPLEMENTATION */ 370 | -------------------------------------------------------------------------------- /ijcringbuffer_test.c: -------------------------------------------------------------------------------- 1 | #define IJCRINGBUFFER_IMPLEMENTATION 2 | #define IJCRINGBUFFER_TEST_MAIN 3 | #include "ijcringbuffer.h" 4 | --------------------------------------------------------------------------------