Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit f112c543a20eded56fd55ce391cb3d86b423b9e2 @peterc committed Jan 18, 2012
Showing with 158 additions and 0 deletions.
  1. +4 −0 .gitignore
  2. +4 −0 Gemfile
  3. +23 −0 README.md
  4. +5 −0 Rakefile
  5. +19 −0 bitarray.gemspec
  6. +41 −0 lib/bitarray.rb
  7. +62 −0 test/test_bitarray.rb
4 .gitignore
@@ -0,0 +1,4 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
4 Gemfile
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in bitarray.gemspec
+gemspec
23 README.md
@@ -0,0 +1,23 @@
+# BitArray: A simple bit array/bit field library in pure Ruby
+
+Basic, pure Ruby bit field. Pretty fast (for what it is) and memory efficient. Works well for Bloom filters (the reason I wrote it).
+
+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
+
+## Examples
+
+Create a bit field 1000 bits wide
+ bf = BitField.new(1000)
+
+Setting and reading bits
+ bf[100] = 1
+ bf[100] .. => 1
+ bf[100] = 0
+
+More
+ bf.to_s = "10101000101010101" (example)
+ bf.total_set .. => 10 (example - 10 bits are set to "1")
+
+## License
+
+MIT licensed. Copyright 2007-2012 Peter Cooper, yada yada.
5 Rakefile
@@ -0,0 +1,5 @@
+require "bundler/gem_tasks"
+require "rake/testtask"
+
+Rake::TestTask.new # defaults are fine for now
+task :default => :test
19 bitarray.gemspec
@@ -0,0 +1,19 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+
+Gem::Specification.new do |s|
+ s.name = "bitarray"
+ s.version = "0.0.1"
+ s.authors = ["Peter Cooper"]
+ s.email = ["git@peterc.org"]
+ s.homepage = "https://github.com/peterc/bitarray"
+ s.summary = %q{A simple, pure Ruby bit array implementation.}
+ s.description = %q{A simple, pure Ruby bit array implementation.}
+
+ s.rubyforge_project = "bitarray"
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+end
41 lib/bitarray.rb
@@ -0,0 +1,41 @@
+class BitArray
+ attr_reader :size
+ include Enumerable
+
+ ELEMENT_WIDTH = 32
+
+ def initialize(size)
+ @size = size
+ @field = Array.new(((size - 1) / ELEMENT_WIDTH) + 1, 0)
+ end
+
+ # Set a bit (1/0)
+ def []=(position, value)
+ if value == 1
+ @field[position / ELEMENT_WIDTH] |= 1 << (position % ELEMENT_WIDTH)
+ elsif (@field[position / ELEMENT_WIDTH]) & (1 << (position % ELEMENT_WIDTH)) != 0
+ @field[position / ELEMENT_WIDTH] ^= 1 << (position % ELEMENT_WIDTH)
+ end
+ end
+
+ # Read a bit (1/0)
+ def [](position)
+ @field[position / ELEMENT_WIDTH] & 1 << (position % ELEMENT_WIDTH) > 0 ? 1 : 0
+ end
+
+ # Iterate over each bit
+ def each(&block)
+ @size.times { |position| yield self[position] }
+ end
+
+ # Returns the field as a string like "0101010100111100," etc.
+ def to_s
+ inject("") { |a, b| a + b.to_s }
+ end
+
+ # Returns the total number of bits that are set
+ # (The technique used here is about 6 times faster than using each or inject direct on the bitfield)
+ def total_set
+ @field.inject(0) { |a, byte| a += byte & 1 and byte >>= 1 until byte == 0; a }
+ end
+end
62 test/test_bitarray.rb
@@ -0,0 +1,62 @@
+require "test/unit"
+require "bitarray"
+
+class TestBitArray < Test::Unit::TestCase
+ def setup
+ @public_bf = BitArray.new(1000)
+ end
+
+ def test_basic
+ assert_equal 0, BitArray.new(100)[0]
+ assert_equal 0, BitArray.new(100)[1]
+ end
+
+ def test_setting_and_unsetting
+ @public_bf[100] = 1
+ assert_equal 1, @public_bf[100]
+ @public_bf[100] = 0
+ assert_equal 0, @public_bf[100]
+ end
+
+ def test_random_setting_and_unsetting
+ 100.times do
+ index = rand(1000)
+ @public_bf[index] = 1
+ assert_equal 1, @public_bf[index]
+ @public_bf[index] = 0
+ assert_equal 0, @public_bf[index]
+ end
+ end
+
+ def test_multiple_setting
+ 1.upto(999) do |pos|
+ 2.times { @public_bf[pos] = 1 }
+ assert_equal 1, @public_bf[pos]
+ end
+ end
+
+ def test_multiple_unsetting
+ 1.upto(999) do |pos|
+ 2.times { @public_bf[pos] = 0 }
+ assert_equal 0, @public_bf[pos]
+ end
+ end
+
+ def test_size
+ assert_equal 1000, @public_bf.size
+ end
+
+ def test_to_s
+ bf = BitArray.new(10)
+ bf[1] = 1
+ bf[5] = 1
+ assert_equal "0100010000", bf.to_s
+ end
+
+ def test_total_set
+ bf = BitArray.new(10)
+ bf[1] = 1
+ bf[5] = 1
+ assert_equal 2, bf.total_set
+ end
+end

0 comments on commit f112c54

Please sign in to comment.