Class: FFI::Bitmask

Inherits:
Enum
  • Object
show all
Defined in:
lib/ffi/enum.rb

Overview

Represents a C enum whose values are power of 2

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

Examples:

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

Instance Attribute Summary

Attributes inherited from Enum

#native_type, #tag

Instance Method Summary collapse

Methods inherited from Enum

#symbol_map, #symbols

Methods included from DataConverter

#native_type

Constructor Details

#initialize(info, tag = nil) ⇒ Bitmask #initialize(native_type, info, tag = nil) ⇒ Bitmask

Returns a new instance of Bitmask.

Overloads:

  • #initialize(info, tag = nil) ⇒ Bitmask

    Parameters:

    • info (nil, Enumerable)

      symbols and bit rank for new Bitmask

    • tag (nil, Symbol) (defaults to: nil)

      name of new Bitmask

  • #initialize(native_type, info, tag = nil) ⇒ Bitmask

    Parameters:

    • native_type (FFI::Type)

      Native type for new Bitmask

    • info (nil, Enumerable)

      symbols and bit rank for new Bitmask

    • tag (nil, Symbol) (defaults to: nil)

      name of new Bitmask



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/ffi/enum.rb', line 193

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

Instance Method Details

#[](*query) ⇒ Integer #[](query) ⇒ Integer #[](*query) ⇒ Array<Symbol> #[](query) ⇒ Array<Symbol>

Get a symbol list or a value from the bitmask

Overloads:

  • #[](*query) ⇒ Integer

    Get bitmask value from symbol list

    Parameters:

    • query (Symbol)

    Returns:

    • (Integer)
  • #[](query) ⇒ Integer

    Get bitmask value from symbol array

    Parameters:

    • query (Array<Symbol>)

    Returns:

    • (Integer)
  • #[](*query) ⇒ Array<Symbol>

    Get a list of bitmask symbols corresponding to the or reduction of a list of integer

    Parameters:

    • query (Integer)

    Returns:

    • (Array<Symbol>)
  • #[](query) ⇒ Array<Symbol>

    Get a list of bitmask symbols corresponding to the or reduction of a list of integer

    Parameters:

    • query (Array<Integer>)

    Returns:

    • (Array<Symbol>)

Raises:

  • (ArgumentError)


237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/ffi/enum.rb', line 237

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

#from_native(val, ctx) ⇒ Array<Symbol, Integer>

Returns list of symbol names corresponding to val, plus an optional remainder if some bits don’t match any constant.

Parameters:

  • val (Integer)
  • ctx

    unused

Returns:

  • (Array<Symbol, Integer>)

    list of symbol names corresponding to val, plus an optional remainder if some bits don’t match any constant



289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/ffi/enum.rb', line 289

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

#to_native(query, ctx) ⇒ Integer #to_native(query, ctx) ⇒ Integer

Get the native value of a bitmask

Overloads:

  • #to_native(query, ctx) ⇒ Integer

    Returns value of a bitmask.

    Parameters:

    • query (Symbol, Integer, #to_int)
    • ctx

      unused

    Returns:

    • (Integer)

      value of a bitmask

  • #to_native(query, ctx) ⇒ Integer

    Returns value of a bitmask.

    Parameters:

    • query (Array<Symbol, Integer, #to_int>)
    • ctx

      unused

    Returns:

    • (Integer)

      value of a bitmask



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/ffi/enum.rb', line 261

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