Show HN: Embedded Ring Buffer C++98
github.com
Show HN: Embedded Ring Buffer C++98
1–10 of 41 posts
Re: Show HN: Embedded Ring Buffer C++98
#2Re: Show HN: Embedded Ring Buffer C++98
#3[1] https://cpp4arduino.com/2018/10/26/why-cpp-programmers-dont-...
Re: Show HN: Embedded Ring Buffer C++98
#4>// ...
> T data[LENGTH];
I'm not sure how important it is in practice, but I'm pretty sure you don't zero out the whole array for sizeof(T) > 1.
Anyhow, memsetting to 0 a complex type is... not something I'd recommend in most cases.
Re: Show HN: Embedded Ring Buffer C++98
#5Looking for CC, advice and bugs!
Sample implementation in C++17 here:
https://github.com/stanford-stagecast/audio/blob/main/src/ut...
https://github.com/stanford-stagecast/audio/blob/main/src/ut...
Re: Show HN: Embedded Ring Buffer C++98
#6Don't use "NULL" in C++[1]. [1] https://cpp4arduino.com/2018/10/26/why-cpp-programmers-dont-...
Re: Show HN: Embedded Ring Buffer C++98
#7> memset(data, 0, LENGTH); >// ... > T data[LENGTH]; I'm not sure how important it is in practice, but I'm pretty sure you don't zero out the whole array for sizeof(T) > 1. Anyhow, memsetting to 0 a complex type is... not something I'd recommend in most cases.
Re: Show HN: Embedded Ring Buffer C++98
#8But confining yourself to C++98 is just masochism. Just because we're embedded doesn't mean we can't have nice things.
Re: Show HN: Embedded Ring Buffer C++98
#9Looking for CC, advice and bugs!
Looks reasonable! On a system with virtual memory, one popular trick is the "virtual ring buffer," which lets the reader and writer always access a contiguous region of the full requested length. The idea is to map the same backing store twice sequentially in virtual memory. It leads to a much simpler implementation, because you don't have to handle the edge cases that relate to wrapping around. Sample implementation…
You don't even need to double-map memory. Just make the ring buffer smaller than your actual buffer, and treat any overrun as if it had wrapped around, but really just write past the (official) end. You might waste a bit of space at the front when you do, just to keep the code simple, but if you didn't have some room to waste, you wouldn't be using a ring buffer.
Another way to simplify management is to give the ring buffer a power-of-two size, and make the head and tail counters 64 bits, masking off the high bits when actually looking at the buffer. They only ever increase.
Re: Show HN: Embedded Ring Buffer C++98
#10> memset(data, 0, LENGTH); >// ... > T data[LENGTH]; I'm not sure how important it is in practice, but I'm pretty sure you don't zero out the whole array for sizeof(T) > 1. Anyhow, memsetting to 0 a complex type is... not something I'd recommend in most cases.
/* * @brief Retrieve a continuous block of * valid buffered data. * @param num_reads_requested How many reads are required. * @param skip Whether to increment the read position * automatically, (false for manual skip * control) * @return A block of items containing the maximum * number the buffer can provide at this time. / Block Read(unsigned int num_reads_requested)
Where is skip?
> if (buffer_full) > { > / > * Tried to append a value while the buffer is full. > / > overrun_flag = true; > } > else > { > / > * Buffer isn't full yet, write to the curr write position > * and increment it by 1. > / > overrun_flag = false; > data[write_position] = value; > write_position = (write_position + 1U) % LENGTH; > }
You don't write in the case of an "overrun". Isn't that one of the most interesting property of a ringbuffer? It seems that your implementation is specific to your use case (buffering to sd cards?). I don't think your _current_ implementation is apropriate for a _general_ purpose ring buffer mostly because of api concerns. It may be interesting to emphasis this part in your doc.
> reads_to_end = LENGTH - read_position; > req_surpasses_buffer_end = num_reads_requested > reads_to_end; > > if (req_surpasses_buffer_end) > { > / > * If the block requested exceeds the buffer end. Then > * return a block that reaches the end and no more. > / > block.SetStart(&(data[read_position])); > block.SetLength(reads_to_end); > } > else > { > / > * If the block requested does not exceed 0 > * then return a block that reaches the number of reads required. > */ > block.SetStart(&(data[read_position])); > block.SetLength(num_reads_requested); > }
Maybe : > reads_to_end = LENGTH - read_position; > eff_reads = (num_reads_requested > reads_to_end) ? reads_to_end : num_reads_requested; > //or : eff_reads = std::min(num_reads_requested, reads_to_end) > block.SetStart(&(data[read_position])); > block.SetLength(eff_reads);
Still, I understand the need to be very explicit in an embedded context.
Same principle for ``if (!bridges_zero)`` (the ``else`` case)