module HexaPDF::Utils::BitField

def bit_field(name, mapping, lister: "#{name}_values", getter: "#{name}_include?",

+unsetter+.
The method names can be overridden using the arguments +lister+, +getter+, +setter+ and

* unset_NAME(*bits) for clearing the given bits.
* set_NAME(*bits, clear_existing: false) for setting the given bits.
* NAME_include?(bit) which returns true if the given bit is set.
* NAME_values which returns an array of bit names representing the set bits.

After invoking the method the calling class has four new instance methods:

and +value_setter+ arguments.
get and set the raw integer value; or provide custom method names using the +value_getter+
The calling class needs to respond to \#name and \#name= because these methods are used to

bit name or bit index, an error is raised.
one to use either the bit name or its index when getting or setting. When using an unknown
The +mapping+ argument specifies the mapping of names to zero-based bit indices which allows

Creates a bit field for managing the integer attribute +name+.
def bit_field(name, mapping, lister: "#{name}_values", getter: "#{name}_include?",
              setter: "set_#{name}", unsetter: "unset_#{name}", value_getter: name,
              value_setter: "self.#{name}")
  mapping.default_proc = proc do |h, k|
    if h.value?(k)
      k
    else
      raise ArgumentError, "Invalid bit field name or index '#{k}' for #{self.name}##{name}"
    end
  end
  module_eval(<<-EOF, __FILE__, __LINE__ + 1)
    #{name.upcase}_BIT_MAPPING = mapping.freeze
    def #{lister}
      self.class::#{name.upcase}_BIT_MAPPING.keys.map {|n| #{getter}(n) ? n : nil }.compact
    end
    def #{getter}(bit)
      (#{value_getter} || 0)[self.class::#{name.upcase}_BIT_MAPPING[bit]] == 1
    end
    def #{setter}(*bits, clear_existing: false)
      #{value_setter} = 0 if clear_existing || #{value_getter}.nil?
      result = #{value_getter}
      bits.each {|bit| result |= 1 << self.class::#{name.upcase}_BIT_MAPPING[bit] }
      #{value_setter} =  result
    end
    def #{unsetter}(*bits)
      result = #{value_getter} || 0
      return if result == 0
      bits.each {|bit| result &= ~(1 << self.class::#{name.upcase}_BIT_MAPPING[bit]) }
      #{value_setter} = result
    end
  EOF
end