https://github.com/ffi/ffi/issues/874
Use a struct with a variable length array in Ruby-FFI. Or how to use bitfields.
When I consulted on the official Github issue, the developer taught me how to do it, so I will keep it as a record.
Hello Ruby-FFI developers!
Thank you for your wonderful work.
I have a question. How do I convert the structure of a variable length array into Ruby code?
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;
I have another question. I checked the ruby-ffi wiki and it says that bitfields are not supported. Is this still true today?
Answer by Lars Kanis
Bit fields are not supported, but can be emulated with simple integer arithmetic. Variable length arrays can be used in
struct.pointers
. Use it as follows:
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"
You can also add custom methods to MmExtra to access fields and perform bitfield operations.
Thank you.
Recommended Posts