class FFI::Bitmask

Represents a C enum whose values are power of 2

@example

enum {
  red = (1<<0),
  green = (1<<1),
  blue = (1<<2)
}

Contrary to classical enums, bitmask values are usually combined when used.

Public Class Methods

@overload initialize(info, tag=nil)

@param [nil, Enumerable] info symbols and bit rank for new Bitmask
@param [nil, Symbol] tag name of new Bitmask

@overload initialize(native_type, info, tag=nil)

@param [FFI::Type] native_type Native type for new Bitmask
@param [nil, Enumerable] info symbols and bit rank for new Bitmask
@param [nil, Symbol] tag name of new Bitmask
# File lib/ffi/enum.rb, line 192
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?
    last_cst = nil
    value = 0
    info.each do |i|
      case i
      when Symbol
        raise ArgumentError, "duplicate bitmask key" if @kv_map.has_key?(i)
        @kv_map[i] = 1 << value
        last_cst = i
        value += 1
      when Integer
        raise ArgumentError, "bitmask index should be positive" if i<0
        @kv_map[last_cst] = 1 << i
        value = i+1
      end
    end
  end
  @vk_map = @kv_map.invert
end

Public Instance Methods

Get a symbol list or a value from the bitmask @overload [](*query)

Get bitmask value from symbol list
@param [Symbol] query
@return [Integer]

@overload [](query)

Get bitmask value from symbol array
@param [Array<Symbol>] query
@return [Integer]

@overload [](*query)

Get a list of bitmask symbols corresponding to
the or reduction of a list of integer
@param [Integer] query
@return [Array<Symbol>]

@overload [](query)

Get a list of bitmask symbols corresponding to
the or reduction of a list of integer
@param [Array<Integer>] query
@return [Array<Symbol>]
# File lib/ffi/enum.rb, line 236
def [](*query)
  flat_query = query.flatten
  raise ArgumentError, "query should be homogeneous, #{query.inspect}" unless flat_query.all? { |o| o.is_a?(Symbol) } || flat_query.all? { |o| o.is_a?(Integer) || o.respond_to?(:to_int) }
  case flat_query[0]
  when Symbol
    flat_query.inject(0) do |val, o|
      v = @kv_map[o]
      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 }
    @kv_map.select { |_, v| v & val != 0 }.keys
  end
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

# File lib/ffi/enum.rb, line 288
def from_native(val, ctx)
  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 ^ flags.values.reduce(0, :|)
  list.push remainder unless remainder == 0
  return list
end

Get the native value of a bitmask @overload to_native(query, ctx)

@param [Symbol, Integer, #to_int] query
@param ctx unused
@return [Integer] value of a bitmask

@overload to_native(query, ctx)

@param [Array<Symbol, Integer, #to_int>] query
@param ctx unused
@return [Integer] value of a bitmask
# File lib/ffi/enum.rb, line 260
def to_native(query, ctx)
  return 0 if query.nil?
  flat_query = [query].flatten
  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
    when Integer
      val | o
    when ->(obj) { obj.respond_to?(: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