ringbuf: add a generic ring-buffer data type
This commit is contained in:
parent
34963e21a3
commit
e3a4e9065e
3 changed files with 189 additions and 0 deletions
|
@ -86,6 +86,7 @@ taisei_src = files(
|
|||
'projectile.c',
|
||||
'projectile_prototypes.c',
|
||||
'random.c',
|
||||
'ringbuf.c',
|
||||
'stage.c',
|
||||
'stagedraw.c',
|
||||
'stageinfo.c',
|
||||
|
|
59
src/ringbuf.c
Normal file
59
src/ringbuf.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT License.
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
||||
*/
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "ringbuf.h"
|
||||
|
||||
static void push_index(RingBuffer *rbuf, ringbuf_size_t *idx) {
|
||||
++(*idx);
|
||||
|
||||
if(*idx == rbuf->capacity) {
|
||||
*idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ringbuf_size_t _ringbuf_prepare_push(RingBuffer *rbuf) {
|
||||
ringbuf_size_t pidx = rbuf->head;
|
||||
|
||||
if(_ringbuf_is_full(rbuf)) {
|
||||
push_index(rbuf, &rbuf->tail);
|
||||
} else {
|
||||
++rbuf->num_elements;
|
||||
}
|
||||
|
||||
push_index(rbuf, &rbuf->head);
|
||||
return pidx;
|
||||
}
|
||||
|
||||
ringbuf_size_t _ringbuf_prepare_pop(RingBuffer *rbuf) {
|
||||
ringbuf_size_t pidx = rbuf->tail;
|
||||
|
||||
if(_ringbuf_is_empty(rbuf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
push_index(rbuf, &rbuf->tail);
|
||||
--rbuf->num_elements;
|
||||
|
||||
return pidx;
|
||||
}
|
||||
|
||||
ringbuf_size_t _ringbuf_prepare_peek(RingBuffer *rbuf, ringbuf_size_t ofs) {
|
||||
if(ofs >= rbuf->num_elements || ofs < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ringbuf_size_t pidx = rbuf->tail + ofs;
|
||||
|
||||
if(pidx >= rbuf->num_elements) {
|
||||
pidx -= rbuf->num_elements;
|
||||
}
|
||||
|
||||
return pidx;
|
||||
}
|
129
src/ringbuf.h
Normal file
129
src/ringbuf.h
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT License.
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "taisei.h"
|
||||
|
||||
#include "util/compat.h"
|
||||
#include "util/macrohax.h"
|
||||
|
||||
typedef int32_t ringbuf_size_t;
|
||||
|
||||
#define RING_BUFFER_BASE(elem_type) struct { \
|
||||
elem_type *data; \
|
||||
ringbuf_size_t head; \
|
||||
ringbuf_size_t tail; \
|
||||
ringbuf_size_t capacity; \
|
||||
ringbuf_size_t num_elements; \
|
||||
}
|
||||
|
||||
typedef RING_BUFFER_BASE(void) RingBuffer;
|
||||
|
||||
#define RING_BUFFER(elem_type) union { \
|
||||
RING_BUFFER_BASE(elem_type); \
|
||||
RingBuffer ring_buffer; \
|
||||
}
|
||||
|
||||
#define RINGBUF_ASSERT_VALID(rbuf) do { \
|
||||
static_assert( \
|
||||
__builtin_types_compatible_p(RingBuffer, __typeof__((rbuf)->ring_buffer)), \
|
||||
"x->ring_buffer must be of RingBuffer type"); \
|
||||
static_assert( \
|
||||
__builtin_offsetof(__typeof__(*(rbuf)), ring_buffer) == 0, \
|
||||
"x->ring_buffer must be the first member in struct"); \
|
||||
} while(0)
|
||||
|
||||
#define RBUF_ASSERT_CONSISTENT(rbuf) ({ \
|
||||
RINGBUF_ASSERT_VALID(rbuf); \
|
||||
auto _rbuf = rbuf; \
|
||||
assert(_rbuf->data != NULL); \
|
||||
assert(_rbuf->num_elements >= 0); \
|
||||
assert(_rbuf->num_elements <= _rbuf->capacity); \
|
||||
_rbuf; \
|
||||
})
|
||||
|
||||
#define RINGBUF_CHECK_ELEMENT_TYPE(rbuf, elem) do { \
|
||||
static_assert( \
|
||||
__builtin_types_compatible_p(__typeof__(elem), __typeof__(*(rbuf)->data)), \
|
||||
"Ring buffer element type mismatch" \
|
||||
); \
|
||||
} while(0)
|
||||
|
||||
#define RINGBUF_CAST_TO_BASE(rbuf) \
|
||||
&RBUF_ASSERT_CONSISTENT(rbuf)->ring_buffer
|
||||
|
||||
#define RING_BUFFER_INIT(backbuffer, num_elements) \
|
||||
{ .data = (backbuffer), .capacity = (num_elements), }
|
||||
|
||||
ringbuf_size_t _ringbuf_prepare_push(RingBuffer *rbuf) attr_nonnull_all attr_nodiscard;
|
||||
ringbuf_size_t _ringbuf_prepare_pop(RingBuffer *rbuf) attr_nonnull_all attr_nodiscard;
|
||||
ringbuf_size_t _ringbuf_prepare_peek(RingBuffer *rbuf, ringbuf_size_t ofs) attr_nonnull_all attr_nodiscard;
|
||||
|
||||
attr_nonnull_all
|
||||
INLINE bool _ringbuf_is_full(RingBuffer *rbuf) {
|
||||
return rbuf->num_elements == rbuf->capacity;
|
||||
}
|
||||
|
||||
attr_nonnull_all
|
||||
INLINE bool _ringbuf_is_empty(RingBuffer *rbuf) {
|
||||
return rbuf->num_elements == 0;
|
||||
}
|
||||
|
||||
#define ringbuf_is_full(rbuf) _ringbuf_is_full(RINGBUF_CAST_TO_BASE(rbuf))
|
||||
#define ringbuf_is_empty(rbuf) _ringbuf_is_empty(RINGBUF_CAST_TO_BASE(rbuf))
|
||||
|
||||
#define ringbuf_push(rbuf, value) ({ \
|
||||
auto _rbuf = RBUF_ASSERT_CONSISTENT(rbuf); \
|
||||
RINGBUF_CHECK_ELEMENT_TYPE(_rbuf, value); \
|
||||
auto _idx = _ringbuf_prepare_push(&_rbuf->ring_buffer); \
|
||||
_rbuf->data[_idx] = value; \
|
||||
})
|
||||
|
||||
#define ringbuf_pop(rbuf, default_value) ({ \
|
||||
auto _rbuf = RBUF_ASSERT_CONSISTENT(rbuf); \
|
||||
RINGBUF_CHECK_ELEMENT_TYPE(_rbuf, default_value); \
|
||||
auto _return_value = default_value; \
|
||||
auto _idx = _ringbuf_prepare_pop(&_rbuf->ring_buffer); \
|
||||
if(_idx >= 0) _return_value = _rbuf->data[_idx]; \
|
||||
_return_value; \
|
||||
})
|
||||
|
||||
#define _ringbuf_peek_ptr(_rbuf, ofs) ({ \
|
||||
typeof(_rbuf->data) _ptr = NULL; \
|
||||
auto _idx = _ringbuf_prepare_peek(&_rbuf->ring_buffer, ofs); \
|
||||
if(_idx >= 0) _ptr = _rbuf->data + _idx; \
|
||||
_ptr; \
|
||||
})
|
||||
|
||||
#define ringbuf_peek_ptr(rbuf, ofs) ({ \
|
||||
auto _rbuf = RBUF_ASSERT_CONSISTENT(rbuf); \
|
||||
_ringbuf_peek_ptr(_rbuf, ofs); \
|
||||
})
|
||||
|
||||
#define _ringbuf_peek(_rbuf, ofs, default_value) ({ \
|
||||
RINGBUF_CHECK_ELEMENT_TYPE(_rbuf, default_value); \
|
||||
auto _ptr = _ringbuf_peek_ptr(_rbuf, ofs); \
|
||||
auto _return_value = default_value; \
|
||||
if(_ptr != NULL) _return_value = *_ptr; \
|
||||
_return_value; \
|
||||
})
|
||||
|
||||
#define ringbuf_peek(rbuf, ofs, default_value) ({ \
|
||||
auto _rbuf = RBUF_ASSERT_CONSISTENT(rbuf); \
|
||||
_ringbuf_peek(_rbuf, ofs, default_value); \
|
||||
})
|
||||
|
||||
#define ringbuf_rpeek_ptr(rbuf, ofs) ({ \
|
||||
auto _rbuf = RBUF_ASSERT_CONSISTENT(rbuf); \
|
||||
_ringbuf_peek_ptr(_rbuf, _rbuf->num_elements - ((ofs) + 1)); \
|
||||
})
|
||||
|
||||
#define ringbuf_rpeek(rbuf, ofs, default_value) ({ \
|
||||
auto _rbuf = RBUF_ASSERT_CONSISTENT(rbuf); \
|
||||
_ringbuf_peek(_rbuf, _rbuf->num_elements - ((ofs) + 1), default_value); \
|
||||
})
|
Loading…
Reference in a new issue