diff options
author | David Robillard <d@drobilla.net> | 2020-12-31 15:34:15 +0100 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2020-12-31 15:34:15 +0100 |
commit | ed9a6e98b8e4e010117e1228333569aa31c51d9e (patch) | |
tree | 3c333f1c7ce2109222d06aa026b939e379e55b5d /src/ring.c | |
parent | de27dcfe0bb72ef1ec937c4aaee26eef6ff7918e (diff) | |
download | zix-ed9a6e98b8e4e010117e1228333569aa31c51d9e.tar.gz zix-ed9a6e98b8e4e010117e1228333569aa31c51d9e.tar.bz2 zix-ed9a6e98b8e4e010117e1228333569aa31c51d9e.zip |
Separate source from headers
Diffstat (limited to 'src/ring.c')
-rw-r--r-- | src/ring.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/src/ring.c b/src/ring.c new file mode 100644 index 0000000..0607a18 --- /dev/null +++ b/src/ring.c @@ -0,0 +1,222 @@ +/* + Copyright 2011 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "zix/ring.h" + +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_MLOCK +# include <sys/mman.h> +# define ZIX_MLOCK(ptr, size) mlock((ptr), (size)) +#elif defined(_WIN32) +# include <windows.h> +# define ZIX_MLOCK(ptr, size) VirtualLock((ptr), (size)) +#else +# pragma message("warning: No memory locking, possible RT violations") +# define ZIX_MLOCK(ptr, size) +#endif + +#if defined(_MSC_VER) +# include <windows.h> +# define ZIX_READ_BARRIER() MemoryBarrier() +# define ZIX_WRITE_BARRIER() MemoryBarrier() +#elif defined(__GNUC__) +# define ZIX_READ_BARRIER() __atomic_thread_fence(__ATOMIC_ACQUIRE) +# define ZIX_WRITE_BARRIER() __atomic_thread_fence(__ATOMIC_RELEASE) +#else +# pragma message("warning: No memory barriers, possible SMP bugs") +# define ZIX_READ_BARRIER() +# define ZIX_WRITE_BARRIER() +#endif + +struct ZixRingImpl { + uint32_t write_head; ///< Read index into buf + uint32_t read_head; ///< Write index into buf + uint32_t size; ///< Size (capacity) in bytes + uint32_t size_mask; ///< Mask for fast modulo + char* buf; ///< Contents +}; + +static inline uint32_t +next_power_of_two(uint32_t size) +{ + // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + size--; + size |= size >> 1u; + size |= size >> 2u; + size |= size >> 4u; + size |= size >> 8u; + size |= size >> 16u; + size++; + return size; +} + +ZixRing* +zix_ring_new(uint32_t size) +{ + ZixRing* ring = (ZixRing*)malloc(sizeof(ZixRing)); + ring->write_head = 0; + ring->read_head = 0; + ring->size = next_power_of_two(size); + ring->size_mask = ring->size - 1; + ring->buf = (char*)malloc(ring->size); + return ring; +} + +void +zix_ring_free(ZixRing* ring) +{ + free(ring->buf); + free(ring); +} + +void +zix_ring_mlock(ZixRing* ring) +{ + ZIX_MLOCK(ring, sizeof(ZixRing)); + ZIX_MLOCK(ring->buf, ring->size); +} + +void +zix_ring_reset(ZixRing* ring) +{ + ring->write_head = 0; + ring->read_head = 0; +} + +static inline uint32_t +read_space_internal(const ZixRing* ring, uint32_t r, uint32_t w) +{ + if (r < w) { + return w - r; + } + + return (w - r + ring->size) & ring->size_mask; +} + +uint32_t +zix_ring_read_space(const ZixRing* ring) +{ + return read_space_internal(ring, ring->read_head, ring->write_head); +} + +static inline uint32_t +write_space_internal(const ZixRing* ring, uint32_t r, uint32_t w) +{ + if (r == w) { + return ring->size - 1; + } + + if (r < w) { + return ((r - w + ring->size) & ring->size_mask) - 1; + } + + return (r - w) - 1; +} + +uint32_t +zix_ring_write_space(const ZixRing* ring) +{ + return write_space_internal(ring, ring->read_head, ring->write_head); +} + +uint32_t +zix_ring_capacity(const ZixRing* ring) +{ + return ring->size - 1; +} + +static inline uint32_t +peek_internal(const ZixRing* ring, + uint32_t r, + uint32_t w, + uint32_t size, + void* dst) +{ + if (read_space_internal(ring, r, w) < size) { + return 0; + } + + if (r + size < ring->size) { + memcpy(dst, &ring->buf[r], size); + } else { + const uint32_t first_size = ring->size - r; + memcpy(dst, &ring->buf[r], first_size); + memcpy((char*)dst + first_size, &ring->buf[0], size - first_size); + } + + return size; +} + +uint32_t +zix_ring_peek(ZixRing* ring, void* dst, uint32_t size) +{ + return peek_internal(ring, ring->read_head, ring->write_head, size, dst); +} + +uint32_t +zix_ring_read(ZixRing* ring, void* dst, uint32_t size) +{ + const uint32_t r = ring->read_head; + const uint32_t w = ring->write_head; + + if (peek_internal(ring, r, w, size, dst)) { + ZIX_READ_BARRIER(); + ring->read_head = (r + size) & ring->size_mask; + return size; + } + + return 0; +} + +uint32_t +zix_ring_skip(ZixRing* ring, uint32_t size) +{ + const uint32_t r = ring->read_head; + const uint32_t w = ring->write_head; + if (read_space_internal(ring, r, w) < size) { + return 0; + } + + ZIX_READ_BARRIER(); + ring->read_head = (r + size) & ring->size_mask; + return size; +} + +uint32_t +zix_ring_write(ZixRing* ring, const void* src, uint32_t size) +{ + const uint32_t r = ring->read_head; + const uint32_t w = ring->write_head; + if (write_space_internal(ring, r, w) < size) { + return 0; + } + + if (w + size <= ring->size) { + memcpy(&ring->buf[w], src, size); + ZIX_WRITE_BARRIER(); + ring->write_head = (w + size) & ring->size_mask; + } else { + const uint32_t this_size = ring->size - w; + memcpy(&ring->buf[w], src, this_size); + memcpy(&ring->buf[0], (const char*)src + this_size, size - this_size); + ZIX_WRITE_BARRIER(); + ring->write_head = size - this_size; + } + + return size; +} |