CppCommon 1.0.5.0
C++ Common Library
Loading...
Searching...
No Matches
spsc_ring_buffer.inl
Go to the documentation of this file.
1
9namespace CppCommon {
10
11inline SPSCRingBuffer::SPSCRingBuffer(size_t capacity) : _capacity(capacity), _mask(capacity - 1), _buffer(new uint8_t[capacity]), _head(0), _tail(0)
12{
13 assert((capacity > 1) && "Ring buffer capacity must be greater than one!");
14 assert(((capacity & (capacity - 1)) == 0) && "Ring buffer capacity must be a power of two!");
15
16 memset(_pad0, 0, sizeof(cache_line_pad));
17 memset(_pad1, 0, sizeof(cache_line_pad));
18 memset(_pad2, 0, sizeof(cache_line_pad));
19 memset(_pad3, 0, sizeof(cache_line_pad));
20}
21
22inline size_t SPSCRingBuffer::size() const noexcept
23{
24 const size_t head = _head.load(std::memory_order_acquire);
25 const size_t tail = _tail.load(std::memory_order_acquire);
26
27 return head - tail;
28}
29
30inline bool SPSCRingBuffer::Enqueue(const void* data, size_t size)
31{
32 assert((size <= _capacity) && "Data size should not be greater than ring buffer capacity!");
33 if (size > _capacity)
34 return false;
35
36 if (size == 0)
37 return true;
38
39 assert((data != nullptr) && "Pointer to the data should not be null!");
40 if (data == nullptr)
41 return false;
42
43 const size_t head = _head.load(std::memory_order_relaxed);
44 const size_t tail = _tail.load(std::memory_order_acquire);
45
46 // Check if there is required free space in the ring buffer
47 if ((size + head - tail) > _capacity)
48 return false;
49
50 // Copy data into the ring buffer
51 size_t head_index = head & _mask;
52 size_t tail_index = tail & _mask;
53 size_t remain = (tail_index > head_index) ? (tail_index - head_index) : (_capacity - head_index);
54 size_t first = (size > remain) ? remain : size;
55 size_t last = (size > remain) ? size - remain : 0;
56 memcpy(&_buffer[head_index], (uint8_t*)data, first);
57 memcpy(_buffer, (uint8_t*)data + first, last);
58
59 // Increase the head cursor
60 _head.store(head + size, std::memory_order_release);
61
62 return true;
63}
64
65inline bool SPSCRingBuffer::Dequeue(void* data, size_t& size)
66{
67 if (size == 0)
68 return true;
69
70 assert((data != nullptr) && "Pointer to the data should not be null!");
71 if (data == nullptr)
72 return false;
73
74 const size_t tail = _tail.load(std::memory_order_relaxed);
75 const size_t head = _head.load(std::memory_order_acquire);
76
77 // Get the ring buffer size
78 size_t available = head - tail;
79 if (size > available)
80 size = available;
81
82 // Check if the ring buffer is empty
83 if (size == 0)
84 return false;
85
86 // Copy data from the ring buffer
87 size_t head_index = head & _mask;
88 size_t tail_index = tail & _mask;
89 size_t remain = (head_index > tail_index) ? (head_index - tail_index) : (_capacity - tail_index);
90 size_t first = (size > remain) ? remain : size;
91 size_t last = (size > remain) ? size - remain : 0;
92 memcpy((uint8_t*)data, &_buffer[tail_index], first);
93 memcpy((uint8_t*)data + first, _buffer, last);
94
95 // Increase the tail cursor
96 _tail.store(tail + size, std::memory_order_release);
97
98 return true;
99}
100
101} // namespace CppCommon
bool Enqueue(const void *data, size_t size)
Enqueue a data into the ring buffer (single producer thread method)
size_t size() const noexcept
Get ring buffer size in bytes.
bool Dequeue(void *data, size_t &size)
Dequeue a data from the ring buffer (single consumer thread method)
size_t capacity() const noexcept
Get ring buffer capacity in bytes.
SPSCRingBuffer(size_t capacity)
Default class constructor.
C++ Common project definitions.