Skip to content

Commit 4674990

Browse files
author
Peter Cooper
committed
Merge pull request #1 from mceachen/master
TLC
2 parents 9fe36be + 265f33b commit 4674990

File tree

6 files changed

+118
-51
lines changed

6 files changed

+118
-51
lines changed

Gemfile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
11
source "http://rubygems.org"
2-
3-
# Specify your gem's dependencies in bitarray.gemspec
42
gemspec

Gemfile.lock

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
PATH
2+
remote: .
3+
specs:
4+
bitarray (0.0.5)
5+
6+
GEM
7+
remote: http://rubygems.org/
8+
specs:
9+
rake (0.9.2.2)
10+
test-unit (2.4.5)
11+
yard (0.7.4)
12+
13+
PLATFORMS
14+
ruby
15+
16+
DEPENDENCIES
17+
bitarray!
18+
rake
19+
test-unit
20+
yard

README.md

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,57 @@
22

33
Basic, pure Ruby bit field. Pretty fast (for what it is) and memory efficient. Works well for Bloom filters (the reason I wrote it).
44

5-
Written in 2007 and not updated since then, just bringing it on to GitHub by user request. It used to be called Bitfield and was hosted at http://snippets.dzone.com/posts/show/4234 .. I will review the code and bring the docs up to date in due course.
5+
Written in 2007 and not updated since then, just bringing it on to GitHub by user request. It used to be called BitField and was hosted at http://snippets.dzone.com/posts/show/4234 .. I will review the code and bring the docs up to date in due course.
66

77
## Installation
88

9-
gem install bitarray
9+
```ruby
10+
gem install bitarray
11+
```
1012

1113
## Examples
1214

1315
To use:
1416

15-
require 'bitarray'
17+
```ruby
18+
require 'bitarray'
19+
```
1620

17-
Create a bit field 1000 bits wide:
21+
Create a bit array 1000 bits wide:
1822

19-
ba = BitArray.new(1000)
23+
```ruby
24+
ba = BitArray.new(1000)
25+
```
2026

2127
Setting and reading bits:
2228

23-
ba[100] = 1
24-
ba[100] .. => 1
25-
ba[100] = 0
29+
```ruby
30+
ba[100] = 1
31+
ba[100]
32+
#=> 1
33+
34+
ba[100] = 0
35+
ba[100]
36+
#=> 0
37+
```
2638

2739
More:
2840

29-
ba.to_s = "10101000101010101" (example)
30-
ba.total_set .. => 10 (example - 10 bits are set to "1")
41+
```ruby
42+
ba = BitArray.new(20)
43+
[1,3,5,9,11,13,15].each { |i| ba[i] = 1 }
44+
ba.to_s
45+
#=> "01010100010101010000"
46+
ba.total_set
47+
#=> 7
48+
```
49+
50+
## History
51+
- v5 (added support for flags being on by default, instead of off)
52+
- v4 (fixed bug where setting 0 bits to 0 caused a set to 1)
53+
- v3 (supports dynamic bitwidths for array elements.. now doing 32 bit widths default)
54+
- v2 (now uses 1 << y, rather than 2 ** y .. it's 21.8 times faster!)
55+
- v1 (first release)
3156

3257
## License
3358

bitarray.gemspec

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# -*- encoding: utf-8 -*-
22
$:.push File.expand_path("../lib", __FILE__)
33

4+
require 'bitarray'
5+
46
Gem::Specification.new do |s|
57
s.name = "bitarray"
6-
s.version = "0.0.1"
8+
s.version = BitArray::VERSION
79
s.authors = ["Peter Cooper"]
810
s.email = ["git@peterc.org"]
911
s.homepage = "https://github.com/peterc/bitarray"
@@ -16,4 +18,8 @@ Gem::Specification.new do |s|
1618
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
1719
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
1820
s.require_paths = ["lib"]
21+
22+
s.add_development_dependency "rake"
23+
s.add_development_dependency "yard"
24+
s.add_development_dependency "test-unit"
1925
end

lib/bitarray.rb

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
class BitArray
22
attr_reader :size
33
include Enumerable
4-
4+
VERSION = "0.0.5"
55
ELEMENT_WIDTH = 32
6-
7-
def initialize(size)
6+
7+
def initialize(size, default_value = 0)
88
@size = size
99
@field = Array.new(((size - 1) / ELEMENT_WIDTH) + 1, 0)
10+
@field.map!{|i| ~i} if (default_value == 1)
1011
end
11-
12+
1213
# Set a bit (1/0)
1314
def []=(position, value)
1415
if value == 1
@@ -17,22 +18,22 @@ def []=(position, value)
1718
@field[position / ELEMENT_WIDTH] ^= 1 << (position % ELEMENT_WIDTH)
1819
end
1920
end
20-
21+
2122
# Read a bit (1/0)
2223
def [](position)
2324
@field[position / ELEMENT_WIDTH] & 1 << (position % ELEMENT_WIDTH) > 0 ? 1 : 0
2425
end
25-
26+
2627
# Iterate over each bit
2728
def each(&block)
2829
@size.times { |position| yield self[position] }
2930
end
30-
31+
3132
# Returns the field as a string like "0101010100111100," etc.
3233
def to_s
33-
inject("") { |a, b| a + b.to_s }
34+
@field.collect{|ea| ("%032b" % ea).reverse}.join[0..@size-1]
3435
end
35-
36+
3637
# Returns the total number of bits that are set
3738
# (The technique used here is about 6 times faster than using each or inject direct on the bitfield)
3839
def total_set

test/test_bitarray.rb

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,78 @@
22
require "bitarray"
33

44
class TestBitArray < Test::Unit::TestCase
5+
56
def setup
6-
@public_bf = BitArray.new(1000)
7+
@public_ba = BitArray.new(1000)
78
end
8-
9+
910
def test_basic
1011
assert_equal 0, BitArray.new(100)[0]
1112
assert_equal 0, BitArray.new(100)[1]
1213
end
13-
14+
1415
def test_setting_and_unsetting
15-
@public_bf[100] = 1
16-
assert_equal 1, @public_bf[100]
17-
@public_bf[100] = 0
18-
assert_equal 0, @public_bf[100]
16+
@public_ba[100] = 1
17+
assert_equal 1, @public_ba[100]
18+
@public_ba[100] = 0
19+
assert_equal 0, @public_ba[100]
1920
end
2021

2122
def test_random_setting_and_unsetting
2223
100.times do
2324
index = rand(1000)
24-
@public_bf[index] = 1
25-
assert_equal 1, @public_bf[index]
26-
@public_bf[index] = 0
27-
assert_equal 0, @public_bf[index]
25+
@public_ba[index] = 1
26+
assert_equal 1, @public_ba[index]
27+
@public_ba[index] = 0
28+
assert_equal 0, @public_ba[index]
2829
end
2930
end
30-
31+
32+
def test_random_side_effects
33+
ba2 = BitArray.new(@public_ba.size, 1)
34+
35+
on = (@public_ba.size / 2).times.collect do
36+
index = rand(@public_ba.size)
37+
@public_ba[index] = 1
38+
ba2[index] = 0
39+
index
40+
end
41+
assert_equal(@public_ba.to_s, @public_ba.to_s_fast)
42+
43+
@public_ba.size.times do |i|
44+
assert_equal(@public_ba[i], on.include?(i) ? 1 : 0)
45+
assert_equal(ba2[i], on.include?(i) ? 0 : 1)
46+
end
47+
end
48+
3149
def test_multiple_setting
3250
1.upto(999) do |pos|
33-
2.times { @public_bf[pos] = 1 }
34-
assert_equal 1, @public_bf[pos]
51+
2.times { @public_ba[pos] = 1 }
52+
assert_equal 1, @public_ba[pos]
3553
end
3654
end
3755

3856
def test_multiple_unsetting
3957
1.upto(999) do |pos|
40-
2.times { @public_bf[pos] = 0 }
41-
assert_equal 0, @public_bf[pos]
58+
2.times { @public_ba[pos] = 0 }
59+
assert_equal 0, @public_ba[pos]
4260
end
4361
end
44-
62+
4563
def test_size
46-
assert_equal 1000, @public_bf.size
64+
assert_equal 1000, @public_ba.size
4765
end
48-
66+
4967
def test_to_s
50-
bf = BitArray.new(10)
51-
bf[1] = 1
52-
bf[5] = 1
53-
assert_equal "0100010000", bf.to_s
68+
ba = BitArray.new(35)
69+
[1, 5, 6, 7, 10, 16, 33].each{|i|ba[i] = 1}
70+
assert_equal "01000111001000001000000000000000010", ba.to_s
5471
end
55-
72+
5673
def test_total_set
57-
bf = BitArray.new(10)
58-
bf[1] = 1
59-
bf[5] = 1
60-
assert_equal 2, bf.total_set
74+
ba = BitArray.new(10)
75+
ba[1] = 1
76+
ba[5] = 1
77+
assert_equal 2, ba.total_set
6178
end
62-
end
79+
end

0 commit comments

Comments
 (0)