Skip to content

Commit 12ed0ba

Browse files
committed
Add option to skip reverse the bits for each byte
1 parent 978833b commit 12ed0ba

File tree

5 files changed

+60
-12
lines changed

5 files changed

+60
-12
lines changed

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
bitarray (2.0.0)
4+
bitarray (1.2.0)
55

66
GEM
77
remote: https://rubygems.org/

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,23 @@ ba.total_set
4747
#=> 7
4848
```
4949

50+
Initializing `BitArray` with a custom field value:
51+
52+
```ruby
53+
ba = BitArray.new(16, ["0000111111110000"].pack('B*'))
54+
ba.to_s # "1111000000001111"
55+
```
56+
57+
`BitArray` by default stores the bits in reverse order for each byte. If for example, you are initializing `BitArray` with Redis raw value manipulated with `setbit` / `getbit` operations, you will need to tell `BitArray` to not reverse the bits in each byte using the `reverse_byte: false` option:
58+
59+
```ruby
60+
ba = BitArray.new(16, ["0000111111110000"].pack('B*'), reverse_byte: false)
61+
ba.to_s # "0000111111110000"
62+
```
63+
64+
5065
## History
51-
- 2.0.0 in 2018 (Changed bits order to be compliant with Redis' setbit/getbit)
66+
- 1.2 in 2018 (Added option to skip reverse the bits for each byte)
5267
- 1.1 in 2018 (fixed a significant bug)
5368
- 1.0 in 2017 (updated for modern Ruby, more efficient storage, and 10th birthday)
5469
- 0.0.1 in 2012 (original v5 released on GitHub)

lib/bitarray-array.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ class BitArray
33
attr_reader :field
44
include Enumerable
55

6-
VERSION = "2.0.0"
6+
VERSION = "1.2.0"
77
ELEMENT_WIDTH = 32
88

99
def initialize(size, field = nil)

lib/bitarray.rb

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,26 @@ class BitArray
33
attr_reader :field
44
include Enumerable
55

6-
VERSION = "2.0.0"
6+
VERSION = "1.2.0"
77

8-
def initialize(size, field = nil)
8+
def initialize(size, field = nil, reverse_byte: true)
99
@size = size
1010
@field = field || "\0" * (size / 8 + 1)
11+
@reverse_byte = reverse_byte
1112
end
1213

1314
# Set a bit (1/0)
1415
def []=(position, value)
1516
if value == 1
16-
@field.setbyte(position >> 3, @field.getbyte(position >> 3) | (1 << (7 - position % 8)))
17+
@field.setbyte(position >> 3, @field.getbyte(position >> 3) | (1 << (byte_position(position) % 8)))
1718
else
18-
@field.setbyte(position >> 3, @field.getbyte(position >> 3) & ~(1 << (7 - position % 8)))
19+
@field.setbyte(position >> 3, @field.getbyte(position >> 3) & ~(1 << (byte_position(position) % 8)))
1920
end
2021
end
2122

2223
# Read a bit (1/0)
2324
def [](position)
24-
(@field.getbyte(position >> 3) & (1 << (7 - position % 8))) > 0 ? 1 : 0
25+
(@field.getbyte(position >> 3) & (1 << (byte_position(position) % 8))) > 0 ? 1 : 0
2526
end
2627

2728
# Iterate over each bit
@@ -31,12 +32,20 @@ def each(&block)
3132

3233
# Returns the field as a string like "0101010100111100," etc.
3334
def to_s
34-
@field.bytes.collect { |ea| ("%08b" % ea) }.join[0, @size]
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
3540
end
3641

3742
# Returns the total number of bits that are set
3843
# (The technique used here is about 6 times faster than using each or inject direct on the bitfield)
3944
def total_set
4045
@field.bytes.inject(0) { |a, byte| a += byte & 1 and byte >>= 1 until byte == 0; a }
4146
end
47+
48+
def byte_position(position)
49+
@reverse_byte ? position : 7 - position
50+
end
4251
end

test/test_bitarray.rb

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,17 @@ def test_to_s
5959
def test_field
6060
ba = BitArray.new(35)
6161
[1, 5, 6, 7, 10, 16, 33].each { |i| ba[i] = 1}
62-
assert_equal "0100011100100000100000000000000001000000", ba.field.unpack('B*')[0]
62+
assert_equal "1110001000000100000000010000000000000010", ba.field.unpack('B*')[0]
6363
end
6464

6565
def test_initialize_with_field
6666
ba = BitArray.new(15, ["0100011100100001"].pack('B*'))
6767

68-
assert_equal [1, 5, 6, 7, 10, 15], 0.upto(15).select { |i| ba[i] == 1 }
68+
assert_equal [0, 1, 2, 6, 8, 13], 0.upto(15).select { |i| ba[i] == 1 }
6969

7070
ba[2] = 1
7171
ba[12] = 1
72-
assert_equal [1, 2, 5, 6, 7, 10, 12, 15], 0.upto(15).select { |i| ba[i] == 1 }
72+
assert_equal [0, 1, 2, 6, 8, 12, 13], 0.upto(15).select { |i| ba[i] == 1 }
7373
end
7474

7575
def test_total_set
@@ -80,3 +80,27 @@ def test_total_set
8080
assert_equal 3, ba.total_set
8181
end
8282
end
83+
84+
class TestBitArrayWhenNonReversedByte < Minitest::Test
85+
def test_to_s
86+
ba = BitArray.new(35, nil, reverse_byte: true)
87+
[1, 5, 6, 7, 10, 16, 33].each { |i| ba[i] = 1}
88+
assert_equal "01000111001000001000000000000000010", ba.to_s
89+
end
90+
91+
def test_field
92+
ba = BitArray.new(35, nil, reverse_byte: false)
93+
[1, 5, 6, 7, 10, 16, 33].each { |i| ba[i] = 1}
94+
assert_equal "0100011100100000100000000000000001000000", ba.field.unpack('B*')[0]
95+
end
96+
97+
def test_initialize_with_field
98+
ba = BitArray.new(15, ["0100011100100001"].pack('B*'), reverse_byte: false)
99+
100+
assert_equal [1, 5, 6, 7, 10, 15], 0.upto(15).select { |i| ba[i] == 1 }
101+
102+
ba[2] = 1
103+
ba[12] = 1
104+
assert_equal [1, 2, 5, 6, 7, 10, 12, 15], 0.upto(15).select { |i| ba[i] == 1 }
105+
end
106+
end

0 commit comments

Comments
 (0)