2
0

More than 3 years have passed since last update.

Ruby-FFIで可変長配列を持つ構造体を使う、またはビットフィールドを使う方法

Last updated at Posted at 2021-01-07

Ruby-FFIで可変長配列(variable length array)を持つ構造体(struct)を使う。またはビットフィールド(bitfields)を使う方法。

公式のGithubのissueで相談したところ、開発者の方から方法を教えてもらったので記録として残します。

こんにちはRuby-FFI開発者!

素晴らしい仕事をありがとう。

質問があります。 可変長配列の構造をRubyコードに変換するにはどうすればよいですか?

typedef struct {
    uint32_t capacity;
    int32_t dp_score, dp_max, dp_max2;
    uint32_t n_ambi:30, trans_strand:2;
    uint32_t n_cigar;
    uint32_t cigar[]; # Here
} mm_extra_t;

もう1つ質問があります。
ruby-ffi wikiを調べたところ、ビットフィールドはサポートされていないと書かれています。 これは今日でも当てはまりますか?

Lars Kanisさんによる回答

ビットフィールド(Bit fields)はサポートされていませんが、単純な整数演算でエミュレートできます。 可変長配列は struct.pointers で使用できます。 次のように使用します。

class MmExtra < FFI::Struct
  layout capacity: :uint32,
    dp_score: :int32,
    dp_max: :int32,
    dp_max2: :int32,
    n_ambi_trans_strand: :uint32,
    n_cigar: :uint32
end

n_ambi = 123456
trans_strand = 0x2
cigar = [4,5,6]
s = MmExtra.new(FFI::MemoryPointer.new(MmExtra.size + FFI.type_size(:uint32) * cigar.size))
s[:n_ambi_trans_strand] = n_ambi | (trans_strand << 30)


s[:n_cigar] = cigar.size
s.pointer.put_array_of_uint32(s.size, cigar)

p n_ambi: s[:n_ambi_trans_strand] & ((1 << 30) - 1), trans_strand: (s[:n_ambi_trans_strand] >> 30) & ((1 << 2) - 1) # => {:n_ambi=>123456, :trans_strand=>2}
p s[:n_cigar]  # => 3
p s.pointer.get_array_of_uint32(s.size, 3) # => [4, 5, 6]
p s.pointer.read_bytes(s.pointer.size) # => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\xE2\x01\x80\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00"

MmExtraにカスタムメソッドを追加して、フィールドにアクセスし、ビットフィールド演算を実行することもできますよ。

ありがとうございます。

2
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
2
0