Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions lib/ffi/enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ class Bitmask < Enum
# @param [nil, Symbol] tag name of new Bitmask
def initialize(*args)
@native_type = args.first.kind_of?(FFI::Type) ? args.shift : Type::INT
@signed = [Type::INT8, Type::INT16, Type::INT32, Type::INT64].include?(@native_type)
info, @tag = *args
@kv_map = Hash.new
unless info.nil?
Expand Down Expand Up @@ -220,7 +221,7 @@ def initialize(*args)
# @param [Symbol] query
# @return [Integer]
# @overload [](query)
# Get bitmaks value from symbol array
# Get bitmask value from symbol array
# @param [Array<Symbol>] query
# @return [Integer]
# @overload [](*query)
Expand All @@ -240,7 +241,7 @@ def [](*query)
when Symbol
flat_query.inject(0) do |val, o|
v = @kv_map[o]
if v then val |= v else val end
if v then val | v else val end
end
when Integer, ->(o) { o.respond_to?(:to_int) }
val = flat_query.inject(0) { |mask, o| mask |= o.to_int }
Expand All @@ -260,35 +261,41 @@ def [](*query)
def to_native(query, ctx)
return 0 if query.nil?
flat_query = [query].flatten
flat_query.inject(0) do |val, o|
res = flat_query.inject(0) do |val, o|
case o
when Symbol
v = @kv_map[o]
raise ArgumentError, "invalid bitmask value, #{o.inspect}" unless v
val |= v
val | v
when Integer
val |= o
val | o
when ->(obj) { obj.respond_to?(:to_int) }
val |= o.to_int
val | o.to_int
else
raise ArgumentError, "invalid bitmask value, #{o.inspect}"
end
end
# Take two's complement of positive values bigger than the max value
# for the type when native type is signed.
if @signed && res >= (1 << (@native_type.size * 8 - 1))
res = -(-res & ((1 << (@native_type.size * 8)) - 1))
end
res
end

# @param [Integer] val
# @param ctx unused
# @return [Array<Symbol, Integer>] list of symbol names corresponding to val, plus an optional remainder if some bits don't match any constant
def from_native(val, ctx)
list = @kv_map.select { |_, v| v & val != 0 }.keys
flags = @kv_map.select { |_, v| v & val != 0 }
list = flags.keys
# force an unsigned value of the correct size
val &= (1 << (@native_type.size * 8)) - 1 if @signed
# If there are unmatch flags,
# return them in an integer,
# else information can be lost.
# Similar to Enum behavior.
remainder = val ^ list.inject(0) do |tmp, o|
v = @kv_map[o]
if v then tmp |= v else tmp end
end
remainder = val ^ flags.values.reduce(0, :|)
list.push remainder unless remainder == 0
return list
end
Expand Down
71 changes: 71 additions & 0 deletions spec/ffi/bitmask_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,40 @@ module TestBitmask4
attach_function :test_tagged_nonint_bitmask6, :test_tagged_nonint_bitmask3, [:bitmask_type6], :bitmask_type6
end

module TestBitmask5
extend FFI::Library
ffi_lib TestLibrary::PATH

bitmask FFI::Type::INT8, :bitmask_type1, [:c1, :c2, :c3, 7]
bitmask FFI::Type::INT16, :bitmask_type2, [:c4, :c5, :c6, 15]
bitmask FFI::Type::INT32, :bitmask_type3, [:c7, :c8, :c9, 31]
bitmask FFI::Type::INT64, :bitmask_type4, [:c10, :c11, :c12, 63]
bitmask FFI::Type::INT, :bitmask_type5, [:c13, :c14, :c15, FFI::Type::INT.size * 8 - 1]

attach_function :test_tagged_nonint_signed_bitmask1, [:bitmask_type1], :bitmask_type1
attach_function :test_tagged_nonint_signed_bitmask2, [:bitmask_type2], :bitmask_type2
attach_function :test_tagged_nonint_signed_bitmask3, [:bitmask_type3], :bitmask_type3
attach_function :test_tagged_nonint_signed_bitmask4, [:bitmask_type4], :bitmask_type4
attach_function :test_tagged_nonint_signed_bitmask5, :test_untagged_bitmask, [:bitmask_type5], :bitmask_type5
end

module TestBitmask6
extend FFI::Library
ffi_lib TestLibrary::PATH

bitmask FFI::Type::UINT8, :bitmask_type1, [:c1, :c2, :c3, 7]
bitmask FFI::Type::UINT16, :bitmask_type2, [:c4, :c5, :c6, 15]
bitmask FFI::Type::UINT32, :bitmask_type3, [:c7, :c8, :c9, 31]
bitmask FFI::Type::UINT64, :bitmask_type4, [:c10, :c11, :c12, 63]
bitmask FFI::Type::UINT, :bitmask_type5, [:c13, :c14, :c15, FFI::Type::UINT.size * 8 - 1]

attach_function :test_tagged_nonint_unsigned_bitmask1, :test_untagged_nonint_bitmask, [:bitmask_type1], :bitmask_type1
attach_function :test_tagged_nonint_unsigned_bitmask2, :test_tagged_nonint_bitmask1, [:bitmask_type2], :bitmask_type2
attach_function :test_tagged_nonint_unsigned_bitmask3, :test_tagged_nonint_bitmask2, [:bitmask_type3], :bitmask_type3
attach_function :test_tagged_nonint_unsigned_bitmask4, :test_tagged_nonint_bitmask3, [:bitmask_type4], :bitmask_type4
attach_function :test_tagged_nonint_unsigned_bitmask5, :test_tagged_uint_bitmask, [:bitmask_type5], :bitmask_type5
end

describe "A library with no bitmask or enum defined" do
it "returns nil when asked for an enum" do
expect(TestBitmask0.enum_type(:foo)).to be_nil
Expand Down Expand Up @@ -248,6 +282,23 @@ module TestBitmask4
expect(TestBitmask4.test_tagged_nonint_bitmask6([1<<14,1<<42,1<<43])).to eq([:c26,:c28,1<<43])
end

it "only remainder is given if only undefined mask are returned" do
expect(TestBitmask3.test_tagged_typedef_bitmask1(1<<4)).to eq([1<<4])
expect(TestBitmask3.test_tagged_typedef_bitmask1([1<<4])).to eq([1<<4])
expect(TestBitmask3.test_tagged_typedef_bitmask2(1<<6)).to eq([1<<6])
expect(TestBitmask3.test_tagged_typedef_bitmask2([1<<6])).to eq([1<<6])
expect(TestBitmask3.test_tagged_typedef_bitmask3(1<<7)).to eq([1<<7])
expect(TestBitmask3.test_tagged_typedef_bitmask3([1<<7])).to eq([1<<7])
expect(TestBitmask3.test_tagged_typedef_bitmask4(1<<9)).to eq([1<<9])
expect(TestBitmask3.test_tagged_typedef_bitmask4([1<<9])).to eq([1<<9])
expect(TestBitmask4.test_tagged_nonint_bitmask4(1<<10)).to eq([1<<10])
expect(TestBitmask4.test_tagged_nonint_bitmask4([1<<10])).to eq([1<<10])
expect(TestBitmask4.test_tagged_nonint_bitmask5(1<<16)).to eq([1<<16])
expect(TestBitmask4.test_tagged_nonint_bitmask5([1<<16])).to eq([1<<16])
expect(TestBitmask4.test_tagged_nonint_bitmask6(1<<43)).to eq([1<<43])
expect(TestBitmask4.test_tagged_nonint_bitmask6([1<<43])).to eq([1<<43])
end

it "wrong constants rejected" do
expect { TestBitmask3.test_tagged_typedef_bitmask1([:c2,:c4,:c5]) }.to raise_error(ArgumentError)
expect { TestBitmask3.test_tagged_typedef_bitmask2([:c6,:c8,:c9]) }.to raise_error(ArgumentError)
Expand Down Expand Up @@ -583,3 +634,23 @@ module TestBitmask4
end.to raise_error(ArgumentError, /duplicate/)
end
end

describe "Signed bitmasks" do
it "do not return a remainder when used with their most significant bit set" do
expect(TestBitmask5.test_tagged_nonint_signed_bitmask1([:c1, :c2, :c3])).to eq([:c1, :c2, :c3])
expect(TestBitmask5.test_tagged_nonint_signed_bitmask2([:c4, :c5, :c6])).to eq([:c4, :c5, :c6])
expect(TestBitmask5.test_tagged_nonint_signed_bitmask3([:c7, :c8, :c9])).to eq([:c7, :c8, :c9])
expect(TestBitmask5.test_tagged_nonint_signed_bitmask4([:c10, :c11, :c12])).to eq([:c10, :c11, :c12])
expect(TestBitmask5.test_tagged_nonint_signed_bitmask5([:c13, :c14, :c15])).to eq([:c13, :c14, :c15])
end
end

describe "Unsigned bitmasks" do
it "do not return a remainder when used with their most significant bit set" do
expect(TestBitmask6.test_tagged_nonint_unsigned_bitmask1([:c1, :c2, :c3])).to eq([:c1, :c2, :c3])
expect(TestBitmask6.test_tagged_nonint_unsigned_bitmask2([:c4, :c5, :c6])).to eq([:c4, :c5, :c6])
expect(TestBitmask6.test_tagged_nonint_unsigned_bitmask3([:c7, :c8, :c9])).to eq([:c7, :c8, :c9])
expect(TestBitmask6.test_tagged_nonint_unsigned_bitmask4([:c10, :c11, :c12])).to eq([:c10, :c11, :c12]) unless RUBY_ENGINE == 'truffleruby'
expect(TestBitmask6.test_tagged_nonint_unsigned_bitmask5([:c13, :c14, :c15])).to eq([:c13, :c14, :c15])
end
end
20 changes: 20 additions & 0 deletions spec/ffi/fixtures/BitmaskTest.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ uint64_t test_tagged_nonint_bitmask3(uint64_t val) {
return val;
}

unsigned int test_tagged_uint_bitmask(unsigned int val) {
return val;
}

int8_t test_tagged_nonint_signed_bitmask1(int8_t val) {
return val;
}

int16_t test_tagged_nonint_signed_bitmask2(int16_t val) {
return val;
}

int32_t test_tagged_nonint_signed_bitmask3(int32_t val) {
return val;
}

int64_t test_tagged_nonint_signed_bitmask4(int64_t val) {
return val;
}

typedef enum {c1 = (1<<0), c2 = (1<<1), c3 = (1<<2), c4 = (1<<3)} bitmask_type1;
int test_tagged_typedef_bitmask1(int val) {
return val;
Expand Down