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): Writes obj 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

  1. Clone the peterzhu2118/ruby-c-ext-code repository.
  2. Navigate to the part9 directory.
  3. 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!