

Show HN: Revring, a circular buffer with zero memory waste - codehero
https://analog10.com/posts/circular_buffer.html

======
jhallenworld
A conceptually simpler way to do this is to assign one (or more) extra bits in
the head an tail pointers. For example, for a 512 entry ring, use 16-bit
indices. Whenever you index the ring, and the index with 511 before performing
the index.

    
    
      Write to ring: ring[511&(head++)] = data
    
      Read from ring: data = ring[511&(tail++)]
    
      Ring is empty: head == tail
    
      Ring is full: tail + 512 == head

------
dpc_pw
This post is literally about saving one byte, at the cost of being slower and
not producer-consumer friendly. Not very interesting.

The flag could have been hidden in any other field as a bit or something. Then
it could be at least masked with simple AND operation which is usually faster
than branching, especially on pipelined CPUs.

Update: Quick implementation:
[https://gist.github.com/dpc/a194b7784adfa150a450](https://gist.github.com/dpc/a194b7784adfa150a450)

This fix for concurrency issue is an ugly hack. I'm not sure if it's even
correct in this particular scenario, and definitely not proper for anything
that would aspire to be good reusable code. I'd advise this code to push
atomicity requirement onto caller. Irqs should have been disabled by calling
code.

"register" keyword is obsolete. There's no point in using it.

------
kstenerud
I did something similar back in the DOS era for a serial port library:
[https://github.com/kstenerud/DOS-Serial-
Library/blob/master/...](https://github.com/kstenerud/DOS-Serial-
Library/blob/master/serial.c)

------
rhaps0dy
You still have a buffer or size X that may store X-Y items. I fail to see the
"zero memory waste" (not that it's a good tradeoff).

~~~
mungoman2
No, the author can use all entries in the buffer. The space cost is hidden in
that one of the pointers need to hold X+1 values, ie for 256 entries it is not
enough with 8-bit indices.

An unfortunate effect of this implementation is that both indices are modified
by the consumer. It's not safe to write/read dats from different contexts,
something that would be very useful in a driver.

~~~
codehero
Yeah your intuition is correct here. On an MSP430 (which is were I use it) the
producer is in interrupt context but as a savvy redditor pointed out, I will
need to enable/disable interrupts in the consumer function as well.

------
cpr
Gosh, all that to save one byte? ;-)

~~~
jonsen
That byte will be needed elsewhere to make it multitasking safe.

~~~
codehero
I'm running it on a system with 512 bytes of RAM and multiple queues...saving
1 byte helps. I do not need extra space for synchronization; I can
enable/disable interrupts without extra RAM required.

