A Rubyist's Walk Along the C-side (Part 9): Circular Buffer Project
This is an article in a multi-part series called “A Rubyist’s Walk Along the C-side”
In the previous article, we saw ways to raise errors and rescue from errors. In this article, you’ll get to practice what you’ve learned with a small project!
Circular buffer API
In this project, you’ll be implementing a circular buffer. The circular buffer will have three instance methods:
initialize(capacity)
: Initializes a buffer with capacity. The read and write cursors are initially at the beginning.write(obj)
: Writesobj
at the write cursor and advances the write cursor. Raises an error and does not write if the buffer is full.read()
: Returns the element at the read cursor and advances the read cursor. Raises an error if the buffer is empty.
It’s called a circular buffer because when the buffer reaches the end, it will wrap around and start from the beginning again. An element is “consumed” and can be overwritten when it is read. Take a look at the Wikipedia article for some helpful animations.
Setup
- Clone the
peterzhu2118/ruby-c-ext-code
repository. - Navigate to the
part9
directory. - Install dependencies using
bundle install
.
Ruby implementation
There is a reference Ruby implementation in the circular_buffer_ruby.rb
file. Read through it and make sure you understand the code.
Test suite
There is a test suite in the test_circular_buffer.rb
file. It can be ran using bundle exec rake test:ruby
.
C implementations
For this project, you’re asked to implement two C implementations: one using only instance variables and one using only TypedData objects.
Instance variable implementation
For this implementation, you should edit the ext_ivar/circular_buffer_ivar.c
file. You should implement a class called CircularBufferIvar
which implements the three methods described above. This implementation should be roughly a direct translation of the Ruby implementation into C code (i.e. it should use instance variables and the buffer should be backed by a Ruby array).
Once complete, you can compile your code using bundle exec rake compile:circular_buffer_ivar
and test it using bundle exec rake test:ivar
.
TypedData implementation
Once you’re done the instance variable implementation, try implementing it again using TypedData objects. You should edit the ext_typeddata/circular_buffer_typeddata.c
file and implement a class called CircularBufferTypedData
. There are multiple ways to implement the circular buffer, and which one you choose is up to you. For example, you can use either a C array or a linked list to back the buffer. How you decide to implement it is up to you!
As an extra challenge, try supporting compaction!
Once complete, you can compile your code using bundle exec rake compile:circular_buffer_typeddata
and test it using bundle exec rake test:typeddata
.
Reference solutions
If you’ve finished or are stuck, you can take a look at the reference solutions that I wrote. But don’t cheat! I’m watching you to make sure you don’t…
Conclusion
In this article, you wrote a small project to practice your C extension skills! Hold on to your solutions as you’ll be using it in the next article. Hopefully you found it fun!