3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Ruby-FFIで構造体のビットフィールド(bit field)を読み書きできるgemをつくりました

Last updated at Posted at 2021-07-25

はじめに

Ruby-FFI とは

Ruby-FFIはlibffiを使ってRubyでC言語のライブラリのバインディングを作成する有力なツールです。

Ruby-FFI はビットフィールド未対応だった

しかし、2021年7月現在、Ruby-FFIは構造体のビットフィールドをサポートしていません。ビットフィールド未対応であることはWikiにも記載されていますし、issueで開発者の方にも直接確認して、たしかに対応していないという返事をいただきました。

筆者の見聞によるとC言語の ビットフィールドへのサポートが進んでいないのはRuby言語に限った話ではないので、ある意味仕方がないことではあります。もしもビットフィールドが一部に使われているだけであれば、その都度カスタムメソッドを用意すれば十分だと思います。しかしターゲットとしているC言語のライブラリのあちこちでビットフィールドが利用されているとなるとその都度カスタムのメソッドを用意するのはとても手間がかかります。gemを作成して一気に解決したくなります。

私は ruby-htslib というバイオインフォマティックス関連のバインディングを作成しています。htslibはライブラリ全体でビットフィールドが多用されているためビットフィールドへの対応が避けられません。しかし、大量のカスタムメソッドを書くのはなんとかして避けたいと思いました。

ffi-bitfield GEMを作った

そこで、Ruby-FFIでビットフィールドを扱うためのgemを作成しました。

ビット演算については全然わからなかったので、ウンウン数時間かけて考えてなんとか初歩的な部分を理解しました。それでも自力で効率的なコードを書くことはできなかったので、Ruby-JPのslackに質問したり、スタックオーバーフローに質問したりしてほかの人にコードを書いてもらいました。こういうときにインターネットのコミュニティーは本当に頼りになると思います。

実装はピュアRubyで書かれており、[]= メソッドや [] メソッドを挙動を変更するようになっています。C言語やFFIのレベルでどうこうするものではありません。

インストール

FFI以外の依存は特にありません。

gem install ffi-bitfield

使い方

require 'bit_structs とすると FFI::BitStructFFI::ManagedBitStruct が使えるようになります。

require 'ffi/bit_structs'
require 'ffi/bit_struct'         # FFI::BitStruct        だけ使いたいとき
require 'ffi/managed_bit_struct' # FFI::ManagedBitStruct だけ使いたいとき

構造体とビットフィールドは次のような感じで定義します。
bit_fieldbit_fields は同じ挙動で、メソッドの別名です。

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

あとは普通のRuby-FFIと全く同じです。

a = Struct1.new

読み込むときは

p a[:a0]

書き込むときは

a[:a0] = 1

とします。

一応テストもちょこっと書いたので、通常の用途ではほとんど問題なく使えると思います。

バグを見つけたらぜひ報告ください!

しかしなにぶん苦手な分野ですので、コーナーケースでは、ひょっとするとまだまだバグが残っているかも知れません。
もしもおかしな挙動を発見した方はgithubのissue欄にぜひ報告してください。
作者としては大変助かります

気持ちとしてはお礼の品を差し上げたいぐらいですが、そういうわけにもいかないので、そのかわりに幸せな気持ちになるハトのgif画像を貼っておきます。
pigeon.gif

この記事は以上です。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?