ffi-bitfield - read/write bit fields with Ruby-FFI

What is Ruby-FFI?

Ruby-FFI did not support bit fields

However, as of July 2021, Ruby-FFI does not support struct bit field. The fact that bit field is not supported is stated in the Wiki. When I contacted the developers, they replied that bit field is still not supported. I was taught how to add a custom method instead.

If only a few bit fields are used, I think it is enough to create some custom methods to read or wite bit fields. However, if a large number of bit fields are provided, it is necessary to create many custom methods. This is a lot of work and increases the probability of mistakes.

Motivation

I'm working on a bioinformatics-related binding called ruby-htslib. htslib makes heavy use of bit fields throughout the library, so supporting bit fields is inevitable.

I would like to create a gem to solve this problem at once.

ffi-bitfield gem

So I created a gem to read or write bit fields in Ruby/FFI.

Actually, I didn't know anything about bitwise operations, so I managed to understand the elementary part after thinking about it for several hours.

Even so, I couldn't write efficient code by myself, so I asked other people to write better code for me on Ruby-JP slack and Stack Overflow. At times like this, the Internet community is really helpful.

Install

gem install ffi-bitfield

How to use

loading

require 'ffi/bit_struct'
# FFI::BitStruct
require 'ffi/managed_bit_struct'
# FFI::ManagedBitStruct
require 'ffi/bit_structs'
# Both FFI::BitStruct and FFI::ManagedBitStruct

Structures and bitfields are defined as follows.

require 'ffi/bit_struct'

class Struct1 < FFI::BitStruct
  layout \
    :a, :uint8,
    :b, :uint8

    bit_fields :a,
               :a0, 1,
               :a1, 1,
               :a2, 1,
               :a3, 1,
               :a4, 1,
               :a5, 1,
               :a6, 1,
               :a7, 1

    bit_fields :b,
               :b0, 1,
               :b1, 1,
               :b2, 2,
               :b3, 4
end

bit_field is an alias for bit_fileds. You can use either of them.

Creating a struct

Same as Ruby-FFI.

a = Struct1.new

Reading bit filed

Same as Ruby-FFI.

p a[:a0] # 0

Writing bit field

Same as Ruby-FFI.

a[:a7] = 1
p a[:0] # 128

It ’s very easy, isn’t it?

If you find a bug, please report it

I wrote a test to check that it works. In many cases, it will work fine, I think.

However, there may still be bugs in the corner case. If you find any strange behavior, please report it to the github issue. I would be very grateful.

Tips

Ruby allows you to call bits with Integer#[]. Did you know that?

128[0] # 0
128[7] # 1
128[6..7] # 2
128[6, 2] # 2

In order to take advantage of this, ffi-bitfield only supports newer versions of Ruby.

Thank you for reading. Have a nice day!

28