|
| 1 | +class BitArray |
| 2 | + attr_reader :field, :reverse_byte, :size |
| 3 | + include Enumerable |
| 4 | + |
| 5 | + VERSION = "1.3.0" |
| 6 | + |
| 7 | + def initialize(size, field = nil, reverse_byte: true) |
| 8 | + @size = size |
| 9 | + @field = field || "\0" * (size / 8 + 1) |
| 10 | + @reverse_byte = reverse_byte |
| 11 | + end |
| 12 | + |
| 13 | + # Set a bit (1/0) |
| 14 | + def []=(position, value) |
| 15 | + if value == 1 |
| 16 | + @field.setbyte(position >> 3, @field.getbyte(position >> 3) | (1 << (byte_position(position) % 8))) |
| 17 | + else |
| 18 | + @field.setbyte(position >> 3, @field.getbyte(position >> 3) & ~(1 << (byte_position(position) % 8))) |
| 19 | + end |
| 20 | + end |
| 21 | + |
| 22 | + # Read a bit (1/0) |
| 23 | + def [](position) |
| 24 | + (@field.getbyte(position >> 3) & (1 << (byte_position(position) % 8))) > 0 ? 1 : 0 |
| 25 | + end |
| 26 | + |
| 27 | + # Iterate over each bit |
| 28 | + def each |
| 29 | + return to_enum(:each) unless block_given? |
| 30 | + @size.times { |position| yield self[position] } |
| 31 | + end |
| 32 | + |
| 33 | + # Returns the field as a string like "0101010100111100," etc. |
| 34 | + def to_s |
| 35 | + if @reverse_byte |
| 36 | + @field.bytes.collect { |ea| ("%08b" % ea).reverse }.join[0, @size] |
| 37 | + else |
| 38 | + @field.bytes.collect { |ea| ("%08b" % ea) }.join[0, @size] |
| 39 | + end |
| 40 | + end |
| 41 | + |
| 42 | + # Iterates over each byte |
| 43 | + def each_byte |
| 44 | + return to_enum(:each_byte) unless block_given? |
| 45 | + @field.bytes.each{ |byte| yield byte } |
| 46 | + end |
| 47 | + |
| 48 | + # Returns the total number of bits that are set |
| 49 | + # Use Brian Kernighan's way, see |
| 50 | + # https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan |
| 51 | + def total_set |
| 52 | + @field.each_byte.inject(0) { |a, byte| (a += 1; byte &= byte - 1) while byte > 0 ; a } |
| 53 | + end |
| 54 | + |
| 55 | + private def byte_position(position) |
| 56 | + @reverse_byte ? position : 7 - position |
| 57 | + end |
| 58 | +end |
0 commit comments