Class: Philiprehberger::BitField::Base
- Inherits:
-
Object
- Object
- Philiprehberger::BitField::Base
- Includes:
- Comparable
- Defined in:
- lib/philiprehberger/bit_field.rb
Overview
Named bit flags with symbolic access, set operations, and serialization
Class Method Summary collapse
-
.flag(name, position) ⇒ void
Define a named flag at the given bit position.
-
.flags ⇒ Array<Symbol>
Return all defined flag names.
- .flags_map ⇒ Object private
-
.from_h(hash) ⇒ Base
Create an instance from a hash.
-
.from_i(value) ⇒ Base
Create an instance from an integer value.
-
.from_json(json_string) ⇒ Base
Create an instance from a JSON string.
-
.group(name, flag_names) ⇒ void
Define a named group of flags.
-
.groups ⇒ Hash{Symbol => Array<Symbol>}
Return all defined group definitions.
- .groups_map ⇒ Object private
- .inherited(subclass) ⇒ Object private
-
.strict(*initial_flags) ⇒ Base
Create an instance that raises on unknown flag names.
Instance Method Summary collapse
-
#&(other) ⇒ Base
Bitwise AND.
-
#<=>(other) ⇒ Integer?
Compare by integer value.
-
#==(other) ⇒ Boolean
(also: #eql?)
Equality check.
-
#^(other) ⇒ Base
Bitwise XOR.
-
#added_flags(other) ⇒ Array<Symbol>
Return flags set in self but not in other.
-
#clear(flag) ⇒ self
Clear a flag.
-
#clear_all ⇒ self
Clear all defined flags.
-
#clear_flags(*flag_names) ⇒ self
Clear multiple specific flags at once.
-
#clear_group(group_name) ⇒ self
Clear all flags in a group.
-
#count_clear ⇒ Integer
Return the number of flags currently clear.
-
#count_set ⇒ Integer
Return the number of flags currently set.
-
#empty? ⇒ Boolean
Return true when no flags are set.
-
#flag_clear?(flag) ⇒ Boolean
Check if a flag is clear (not set).
-
#flag_set?(flag) ⇒ Boolean
Check if a flag is set.
-
#flags ⇒ Array<Symbol>
Return all defined flag names.
-
#group_any_set?(group_name) ⇒ Boolean
Check if any flag in a group is set.
-
#group_none_set?(group_name) ⇒ Boolean
Check if no flags in a group are set.
-
#group_set?(group_name) ⇒ Boolean
Check if all flags in a group are set.
-
#hash ⇒ Integer
Hash code for use in Hash keys.
-
#initialize(*initial_flags) ⇒ Base
constructor
Create a new bit field with the given flags set.
-
#removed_flags(other) ⇒ Array<Symbol>
Return flags set in other but not in self.
-
#set(flag) ⇒ self
Set a flag.
-
#set_all ⇒ self
Set all defined flags.
-
#set_flags(*flag_names) ⇒ self
Set multiple specific flags at once.
-
#set_group(group_name) ⇒ self
Set all flags in a group.
-
#subset_of?(other) ⇒ Boolean
Check whether every flag set in self is also set in ‘other`.
-
#to_a ⇒ Array<Symbol>
Return an array of set flag names.
-
#to_binary_string(width: nil) ⇒ String
Return the field as a binary string (MSB-first).
-
#to_h ⇒ Hash{Symbol => Object}
Return a hash representation.
-
#to_i ⇒ Integer
Return the integer representation.
-
#to_json(*_args) ⇒ String
Return a JSON string representation.
-
#toggle(flag) ⇒ self
Toggle a flag.
-
#|(other) ⇒ Base
Bitwise OR.
Constructor Details
#initialize(*initial_flags) ⇒ Base
Create a new bit field with the given flags set
139 140 141 142 |
# File 'lib/philiprehberger/bit_field.rb', line 139 def initialize(*initial_flags) @value = 0 initial_flags.each { |f| set(f) } end |
Class Method Details
.flag(name, position) ⇒ void
This method returns an undefined value.
Define a named flag at the given bit position
31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/philiprehberger/bit_field.rb', line 31 def flag(name, position) raise Error, 'position must be a non-negative integer' unless position.is_a?(Integer) && position >= 0 raise Error, "flag #{name} already defined" if flags_map.key?(name) raise Error, "position #{position} already used" if flags_map.any? { |_, p| p == position } flags_map[name] = position define_method(:"#{name}?") do flag_set?(name) end end |
.flags ⇒ Array<Symbol>
Return all defined flag names
61 62 63 |
# File 'lib/philiprehberger/bit_field.rb', line 61 def flags flags_map.keys end |
.flags_map ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
118 119 120 |
# File 'lib/philiprehberger/bit_field.rb', line 118 def flags_map @flags_map ||= {} end |
.from_h(hash) ⇒ Base
Create an instance from a hash
110 111 112 113 114 115 |
# File 'lib/philiprehberger/bit_field.rb', line 110 def from_h(hash) value = hash[:value] || hash['value'] raise Error, 'hash must include a value key' if value.nil? from_i(value) end |
.from_i(value) ⇒ Base
Create an instance from an integer value
89 90 91 92 93 94 95 |
# File 'lib/philiprehberger/bit_field.rb', line 89 def from_i(value) raise Error, 'value must be a non-negative integer' unless value.is_a?(Integer) && value >= 0 instance = new instance.instance_variable_set(:@value, value) instance end |
.from_json(json_string) ⇒ Base
Create an instance from a JSON string
101 102 103 104 |
# File 'lib/philiprehberger/bit_field.rb', line 101 def from_json(json_string) data = JSON.parse(json_string) from_h(data) end |
.group(name, flag_names) ⇒ void
This method returns an undefined value.
Define a named group of flags
48 49 50 51 52 53 54 55 56 |
# File 'lib/philiprehberger/bit_field.rb', line 48 def group(name, flag_names) raise Error, "group #{name} already defined" if groups_map.key?(name) flag_names.each do |f| raise Error, "unknown flag #{f} in group #{name}" unless flags_map.key?(f) end groups_map[name] = flag_names.dup.freeze end |
.groups ⇒ Hash{Symbol => Array<Symbol>}
Return all defined group definitions
68 69 70 |
# File 'lib/philiprehberger/bit_field.rb', line 68 def groups groups_map.dup end |
.groups_map ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
123 124 125 |
# File 'lib/philiprehberger/bit_field.rb', line 123 def groups_map @groups_map ||= {} end |
.inherited(subclass) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
128 129 130 131 132 |
# File 'lib/philiprehberger/bit_field.rb', line 128 def inherited(subclass) super subclass.instance_variable_set(:@flags_map, flags_map.dup) subclass.instance_variable_set(:@groups_map, groups_map.dup) end |
Instance Method Details
#&(other) ⇒ Base
Bitwise AND
353 354 355 356 357 |
# File 'lib/philiprehberger/bit_field.rb', line 353 def &(other) raise Error, 'cannot combine different bit field types' unless other.is_a?(self.class) self.class.from_i(@value & other.to_i) end |
#<=>(other) ⇒ Integer?
Compare by integer value
373 374 375 376 377 |
# File 'lib/philiprehberger/bit_field.rb', line 373 def <=>(other) return nil unless other.is_a?(self.class) @value <=> other.to_i end |
#==(other) ⇒ Boolean Also known as: eql?
Equality check
383 384 385 |
# File 'lib/philiprehberger/bit_field.rb', line 383 def ==(other) other.is_a?(self.class) && @value == other.to_i end |
#^(other) ⇒ Base
Bitwise XOR
363 364 365 366 367 |
# File 'lib/philiprehberger/bit_field.rb', line 363 def ^(other) raise Error, 'cannot combine different bit field types' unless other.is_a?(self.class) self.class.from_i(@value ^ other.to_i) end |
#added_flags(other) ⇒ Array<Symbol>
Return flags set in self but not in other
398 399 400 401 402 |
# File 'lib/philiprehberger/bit_field.rb', line 398 def added_flags(other) raise Error, 'cannot compare different bit field types' unless other.is_a?(self.class) self.class.flags.select { |f| flag_set?(f) && !other.flag_set?(f) } end |
#clear(flag) ⇒ self
Clear a flag
196 197 198 199 200 |
# File 'lib/philiprehberger/bit_field.rb', line 196 def clear(flag) pos = position_for(flag) @value &= ~(1 << pos) self end |
#clear_all ⇒ self
Clear all defined flags
223 224 225 226 |
# File 'lib/philiprehberger/bit_field.rb', line 223 def clear_all @value = 0 self end |
#clear_flags(*flag_names) ⇒ self
Clear multiple specific flags at once
241 242 243 244 |
# File 'lib/philiprehberger/bit_field.rb', line 241 def clear_flags(*flag_names) flag_names.each { |f| clear(f) } self end |
#clear_group(group_name) ⇒ self
Clear all flags in a group
259 260 261 262 |
# File 'lib/philiprehberger/bit_field.rb', line 259 def clear_group(group_name) group_flags(group_name).each { |f| clear(f) } self end |
#count_clear ⇒ Integer
Return the number of flags currently clear
171 172 173 |
# File 'lib/philiprehberger/bit_field.rb', line 171 def count_clear self.class.flags.size - count_set end |
#count_set ⇒ Integer
Return the number of flags currently set
164 165 166 |
# File 'lib/philiprehberger/bit_field.rb', line 164 def count_set self.class.flags.count { |f| flag_set?(f) } end |
#empty? ⇒ Boolean
Return true when no flags are set
178 179 180 |
# File 'lib/philiprehberger/bit_field.rb', line 178 def empty? @value.zero? end |
#flag_clear?(flag) ⇒ Boolean
Check if a flag is clear (not set)
157 158 159 |
# File 'lib/philiprehberger/bit_field.rb', line 157 def flag_clear?(flag) !flag_set?(flag) end |
#flag_set?(flag) ⇒ Boolean
Check if a flag is set
148 149 150 151 |
# File 'lib/philiprehberger/bit_field.rb', line 148 def flag_set?(flag) pos = position_for(flag) @value.anybits?(1 << pos) end |
#flags ⇒ Array<Symbol>
Return all defined flag names
321 322 323 |
# File 'lib/philiprehberger/bit_field.rb', line 321 def flags self.class.flags end |
#group_any_set?(group_name) ⇒ Boolean
Check if any flag in a group is set
276 277 278 |
# File 'lib/philiprehberger/bit_field.rb', line 276 def group_any_set?(group_name) group_flags(group_name).any? { |f| flag_set?(f) } end |
#group_none_set?(group_name) ⇒ Boolean
Check if no flags in a group are set
284 285 286 |
# File 'lib/philiprehberger/bit_field.rb', line 284 def group_none_set?(group_name) group_flags(group_name).none? { |f| flag_set?(f) } end |
#group_set?(group_name) ⇒ Boolean
Check if all flags in a group are set
268 269 270 |
# File 'lib/philiprehberger/bit_field.rb', line 268 def group_set?(group_name) group_flags(group_name).all? { |f| flag_set?(f) } end |
#hash ⇒ Integer
Hash code for use in Hash keys
390 391 392 |
# File 'lib/philiprehberger/bit_field.rb', line 390 def hash [self.class, @value].hash end |
#removed_flags(other) ⇒ Array<Symbol>
Return flags set in other but not in self
408 409 410 411 412 |
# File 'lib/philiprehberger/bit_field.rb', line 408 def removed_flags(other) raise Error, 'cannot compare different bit field types' unless other.is_a?(self.class) self.class.flags.select { |f| !flag_set?(f) && other.flag_set?(f) } end |
#set(flag) ⇒ self
Set a flag
186 187 188 189 190 |
# File 'lib/philiprehberger/bit_field.rb', line 186 def set(flag) pos = position_for(flag) @value |= (1 << pos) self end |
#set_all ⇒ self
Set all defined flags
215 216 217 218 |
# File 'lib/philiprehberger/bit_field.rb', line 215 def set_all self.class.flags.each { |f| set(f) } self end |
#set_flags(*flag_names) ⇒ self
Set multiple specific flags at once
232 233 234 235 |
# File 'lib/philiprehberger/bit_field.rb', line 232 def set_flags(*flag_names) flag_names.each { |f| set(f) } self end |
#set_group(group_name) ⇒ self
Set all flags in a group
250 251 252 253 |
# File 'lib/philiprehberger/bit_field.rb', line 250 def set_group(group_name) group_flags(group_name).each { |f| set(f) } self end |
#subset_of?(other) ⇒ Boolean
Check whether every flag set in self is also set in ‘other`.
419 420 421 422 423 |
# File 'lib/philiprehberger/bit_field.rb', line 419 def subset_of?(other) raise ArgumentError, "expected #{self.class}, got #{other.class}" unless other.is_a?(self.class) other.to_i.allbits?(to_i) end |
#to_a ⇒ Array<Symbol>
Return an array of set flag names
314 315 316 |
# File 'lib/philiprehberger/bit_field.rb', line 314 def to_a self.class.flags.select { |f| flag_set?(f) } end |
#to_binary_string(width: nil) ⇒ String
Return the field as a binary string (MSB-first)
By default the string is padded with leading zeros to the declared flag count. If width is given and larger than the natural length, the string is padded to that width. If width is smaller than the natural length, the full representation is returned without truncation to preserve correctness.
305 306 307 308 309 |
# File 'lib/philiprehberger/bit_field.rb', line 305 def to_binary_string(width: nil) bits = self.class.flags.size effective_width = width || bits to_i.to_s(2).rjust(effective_width, '0') end |
#to_h ⇒ Hash{Symbol => Object}
Return a hash representation
328 329 330 |
# File 'lib/philiprehberger/bit_field.rb', line 328 def to_h { flags: to_a, value: @value } end |
#to_i ⇒ Integer
Return the integer representation
291 292 293 |
# File 'lib/philiprehberger/bit_field.rb', line 291 def to_i @value end |
#to_json(*_args) ⇒ String
Return a JSON string representation
335 336 337 |
# File 'lib/philiprehberger/bit_field.rb', line 335 def to_json(*_args) { flags: to_a.map(&:to_s), value: @value }.to_json end |
#toggle(flag) ⇒ self
Toggle a flag
206 207 208 209 210 |
# File 'lib/philiprehberger/bit_field.rb', line 206 def toggle(flag) pos = position_for(flag) @value ^= (1 << pos) self end |