Skip to content

Commit 9ea4e24

Browse files
committed
Merge branch 'dalibor-bits_order'
2 parents f1b4d40 + 12ed0ba commit 9ea4e24

File tree

5 files changed

+75
-10
lines changed

5 files changed

+75
-10
lines changed

Gemfile.lock

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

66
GEM
77
remote: https://rubygems.org/
@@ -18,4 +18,4 @@ DEPENDENCIES
1818
rake
1919

2020
BUNDLED WITH
21-
1.14.5
21+
1.16.3

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +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
66+
- 1.2 in 2018 (Added option to skip reverse the bits for each byte)
5167
- 1.1 in 2018 (fixed a significant bug)
5268
- 1.0 in 2017 (updated for modern Ruby, more efficient storage, and 10th birthday)
5369
- 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 = "1.1.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 = "1.1.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 << (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 << (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 << (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).reverse}.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: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,26 @@ def test_size
5252

5353
def test_to_s
5454
ba = BitArray.new(35)
55-
[1, 5, 6, 7, 10, 16, 33].each{|i|ba[i] = 1}
55+
[1, 5, 6, 7, 10, 16, 33].each { |i| ba[i] = 1}
5656
assert_equal "01000111001000001000000000000000010", ba.to_s
5757
end
5858

59+
def test_field
60+
ba = BitArray.new(35)
61+
[1, 5, 6, 7, 10, 16, 33].each { |i| ba[i] = 1}
62+
assert_equal "1110001000000100000000010000000000000010", ba.field.unpack('B*')[0]
63+
end
64+
65+
def test_initialize_with_field
66+
ba = BitArray.new(15, ["0100011100100001"].pack('B*'))
67+
68+
assert_equal [0, 1, 2, 6, 8, 13], 0.upto(15).select { |i| ba[i] == 1 }
69+
70+
ba[2] = 1
71+
ba[12] = 1
72+
assert_equal [0, 1, 2, 6, 8, 12, 13], 0.upto(15).select { |i| ba[i] == 1 }
73+
end
74+
5975
def test_total_set
6076
ba = BitArray.new(10)
6177
ba[1] = 1
@@ -64,3 +80,27 @@ def test_total_set
6480
assert_equal 3, ba.total_set
6581
end
6682
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)