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.
-
#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.
-
#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
346 347 348 349 350 |
# File 'lib/philiprehberger/bit_field.rb', line 346 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
366 367 368 369 370 |
# File 'lib/philiprehberger/bit_field.rb', line 366 def <=>(other) return nil unless other.is_a?(self.class) @value <=> other.to_i end |
#==(other) ⇒ Boolean Also known as: eql?
Equality check
376 377 378 |
# File 'lib/philiprehberger/bit_field.rb', line 376 def ==(other) other.is_a?(self.class) && @value == other.to_i end |
#^(other) ⇒ Base
Bitwise XOR
356 357 358 359 360 |
# File 'lib/philiprehberger/bit_field.rb', line 356 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
391 392 393 394 395 |
# File 'lib/philiprehberger/bit_field.rb', line 391 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
189 190 191 192 193 |
# File 'lib/philiprehberger/bit_field.rb', line 189 def clear(flag) pos = position_for(flag) @value &= ~(1 << pos) self end |
#clear_all ⇒ self
Clear all defined flags
216 217 218 219 |
# File 'lib/philiprehberger/bit_field.rb', line 216 def clear_all @value = 0 self end |
#clear_flags(*flag_names) ⇒ self
Clear multiple specific flags at once
234 235 236 237 |
# File 'lib/philiprehberger/bit_field.rb', line 234 def clear_flags(*flag_names) flag_names.each { |f| clear(f) } self end |
#clear_group(group_name) ⇒ self
Clear all flags in a group
252 253 254 255 |
# File 'lib/philiprehberger/bit_field.rb', line 252 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 |
#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
314 315 316 |
# File 'lib/philiprehberger/bit_field.rb', line 314 def flags self.class.flags end |
#group_any_set?(group_name) ⇒ Boolean
Check if any flag in a group is set
269 270 271 |
# File 'lib/philiprehberger/bit_field.rb', line 269 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
277 278 279 |
# File 'lib/philiprehberger/bit_field.rb', line 277 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
261 262 263 |
# File 'lib/philiprehberger/bit_field.rb', line 261 def group_set?(group_name) group_flags(group_name).all? { |f| flag_set?(f) } end |
#hash ⇒ Integer
Hash code for use in Hash keys
383 384 385 |
# File 'lib/philiprehberger/bit_field.rb', line 383 def hash [self.class, @value].hash end |
#removed_flags(other) ⇒ Array<Symbol>
Return flags set in other but not in self
401 402 403 404 405 |
# File 'lib/philiprehberger/bit_field.rb', line 401 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
179 180 181 182 183 |
# File 'lib/philiprehberger/bit_field.rb', line 179 def set(flag) pos = position_for(flag) @value |= (1 << pos) self end |
#set_all ⇒ self
Set all defined flags
208 209 210 211 |
# File 'lib/philiprehberger/bit_field.rb', line 208 def set_all self.class.flags.each { |f| set(f) } self end |
#set_flags(*flag_names) ⇒ self
Set multiple specific flags at once
225 226 227 228 |
# File 'lib/philiprehberger/bit_field.rb', line 225 def set_flags(*flag_names) flag_names.each { |f| set(f) } self end |
#set_group(group_name) ⇒ self
Set all flags in a group
243 244 245 246 |
# File 'lib/philiprehberger/bit_field.rb', line 243 def set_group(group_name) group_flags(group_name).each { |f| set(f) } self end |
#to_a ⇒ Array<Symbol>
Return an array of set flag names
307 308 309 |
# File 'lib/philiprehberger/bit_field.rb', line 307 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.
298 299 300 301 302 |
# File 'lib/philiprehberger/bit_field.rb', line 298 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
321 322 323 |
# File 'lib/philiprehberger/bit_field.rb', line 321 def to_h { flags: to_a, value: @value } end |
#to_i ⇒ Integer
Return the integer representation
284 285 286 |
# File 'lib/philiprehberger/bit_field.rb', line 284 def to_i @value end |
#to_json(*_args) ⇒ String
Return a JSON string representation
328 329 330 |
# File 'lib/philiprehberger/bit_field.rb', line 328 def to_json(*_args) { flags: to_a.map(&:to_s), value: @value }.to_json end |
#toggle(flag) ⇒ self
Toggle a flag
199 200 201 202 203 |
# File 'lib/philiprehberger/bit_field.rb', line 199 def toggle(flag) pos = position_for(flag) @value ^= (1 << pos) self end |