bitarray 1.0.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c79a60a3580af853940651ca520abd24f359fc97
4
- data.tar.gz: 96cb2ffe5551ac793e33f3eb06563aadcc6cfd1d
2
+ SHA256:
3
+ metadata.gz: 5a056ce9816063f7e51c6fce69fb88c4644336236545613d4224977a01c50df8
4
+ data.tar.gz: f9f05b2a142f7cea4f529f9ebfbf9e053f635bb75d4ba44a10bdb6a2864c3f0d
5
5
  SHA512:
6
- metadata.gz: bcd93a192a22cf477b45cd582028a3cd60c090dd9cdfbfd5b507a86f4c996a1dede387926d34f0495441cb90b0d899c96d5bb9a34dc8b16b5e933794f0e89bf1
7
- data.tar.gz: cb2b120ce2abe9242bfc07ac9f1dff0bff7aab271daabaaafca53f39a06be8ce690946575bc6c423e8258fbf99c284ab783301327c0495bb37477798a0def376
6
+ metadata.gz: 7373fc1fc97187412eee8e854839961b7647f880afbffa8d4f0e261830d214edc4626763a100dcd71a8ce4dd05c2892522594d63c6d21f95be7bc413afc26c87
7
+ data.tar.gz: f884ad42cba8ce15fa2c3810427f510028db459f14080ca9bda02f823b35d27e00769e941cb5232fb6e0220ddc494d55e00843e5186d4d96a41e5b250754cfc5
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
- # BitArray: A simple bit-array/bitfield library in pure Ruby
1
+ # BitArray: Pure Ruby bit-array/bitfield library
2
2
 
3
- Basic, pure Ruby bit field. Works well for Bloom filters (the use case for which I originally wrote it).
3
+ A simple, pure-Ruby 'bit field' object. Works well for Bloom filters (the use case for which I originally wrote it, although there are numerous good libraries for that task now).
4
4
 
5
- Originally written in 2007 and left without significant update until 2017, it has now been updated to work within a typical, modern Ruby environment while maintaining the same API.
5
+ Despite its age, BitArray has been updated to work within a typical, modern Ruby environment, but is only 'mildly' maintained.
6
6
 
7
7
  ## Installation
8
8
 
9
9
  ```ruby
10
- gem install bitarray
10
+ bundle add bitarray
11
11
  ```
12
12
 
13
13
  ## Examples
@@ -47,7 +47,25 @@ ba.total_set
47
47
  #=> 7
48
48
  ```
49
49
 
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
+
50
65
  ## History
66
+ - 1.3 in 2022 (cleanups and a minor perf tweak)
67
+ - 1.2 in 2018 (Added option to skip reverse the bits for each byte by @dalibor)
68
+ - 1.1 in 2018 (fixed a significant bug)
51
69
  - 1.0 in 2017 (updated for modern Ruby, more efficient storage, and 10th birthday)
52
70
  - 0.0.1 in 2012 (original v5 released on GitHub)
53
71
  - v5 (added support for flags being on by default, instead of off)
@@ -60,6 +78,8 @@ ba.total_set
60
78
 
61
79
  Thanks to Michael Slade for encouraging me to update this library on its 10th birthday and for suggesting finally using String's getbyte and setbyte methods now that we're all on 1.9+ compatible implementations.
62
80
 
81
+ Further thanks to @tdeo, @JoshuaSP, @dalibor, @yegct and @m1lt0n for pull requests.
82
+
63
83
  ## License
64
84
 
65
- MIT licensed. Copyright 2007-2017 Peter Cooper.
85
+ MIT licensed. Copyright 2007-2022 Peter Cooper.
data/Rakefile CHANGED
@@ -2,4 +2,10 @@ require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
 
4
4
  Rake::TestTask.new
5
+
5
6
  task :default => :test
7
+
8
+ desc "Open an irb session to work with bitarray"
9
+ task :console do
10
+ sh "irb -rubygems -I lib -r bitarray"
11
+ end
@@ -3,7 +3,7 @@ class BitArray
3
3
  attr_reader :field
4
4
  include Enumerable
5
5
 
6
- VERSION = "1.0.0"
6
+ VERSION = "1.3.0"
7
7
  ELEMENT_WIDTH = 32
8
8
 
9
9
  def initialize(size, field = nil)
@@ -26,7 +26,8 @@ class BitArray
26
26
  end
27
27
 
28
28
  # Iterate over each bit
29
- def each(&block)
29
+ def each
30
+ return to_enum unless block_given?
30
31
  @size.times { |position| yield self[position] }
31
32
  end
32
33
 
@@ -34,10 +35,17 @@ class BitArray
34
35
  def to_s
35
36
  @field.collect{|ea| ("%0#{ELEMENT_WIDTH}b" % ea).reverse}.join[0..@size-1]
36
37
  end
38
+
39
+ # Iterate over each byte
40
+ def each_byte
41
+ return to_enum(:each_byte) unless block_given?
42
+ @field.each { |byte| yield byte }
43
+ end
37
44
 
38
45
  # Returns the total number of bits that are set
39
- # (The technique used here is about 6 times faster than using each or inject direct on the bitfield)
46
+ # Use Brian Kernighan's way, see
47
+ # https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
40
48
  def total_set
41
- @field.inject(0) { |a, byte| a += byte & 1 and byte >>= 1 until byte == 0; a }
49
+ @field.each_byte.inject(0) { |a, byte| (a += 1; byte &= byte - 1) while byte > 0 ; a }
42
50
  end
43
51
  end
data/lib/bitarray.rb CHANGED
@@ -3,40 +3,57 @@ class BitArray
3
3
  attr_reader :field
4
4
  include Enumerable
5
5
 
6
- VERSION = "1.0.0"
6
+ VERSION = "1.3.0"
7
7
 
8
- def initialize(size, field = nil)
8
+ def initialize(size, field = nil, reverse_byte: true)
9
9
  @size = size
10
- @field = "\0" * (size / 8 + 1)
10
+ @field = field || "\0" * (size / 8 + 1)
11
+ @reverse_byte = reverse_byte
11
12
  end
12
13
 
13
14
  # Set a bit (1/0)
14
15
  def []=(position, value)
15
16
  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)))
17
18
  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)))
19
20
  end
20
21
  end
21
22
 
22
23
  # Read a bit (1/0)
23
24
  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
25
26
  end
26
27
 
27
28
  # Iterate over each bit
28
- def each(&block)
29
+ def each
30
+ return to_enum(:each) unless block_given?
29
31
  @size.times { |position| yield self[position] }
30
32
  end
31
33
 
32
34
  # Returns the field as a string like "0101010100111100," etc.
33
35
  def to_s
34
- @field.bytes.collect{|ea| ("%08b" % ea).reverse}.join[0, @size]
36
+ if @reverse_byte
37
+ @field.bytes.collect { |ea| ("%08b" % ea).reverse }.join[0, @size]
38
+ else
39
+ @field.bytes.collect { |ea| ("%08b" % ea) }.join[0, @size]
40
+ end
41
+ end
42
+
43
+ # Iterates over each byte
44
+ def each_byte
45
+ return to_enum(:each_byte) unless block_given?
46
+ @field.bytes.each{ |byte| yield byte }
35
47
  end
36
48
 
37
49
  # Returns the total number of bits that are set
38
- # (The technique used here is about 6 times faster than using each or inject direct on the bitfield)
50
+ # Use Brian Kernighan's way, see
51
+ # https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
39
52
  def total_set
40
- @field.bytes.inject(0) { |a, byte| a += byte & 1 and byte >>= 1 until byte == 0; a }
53
+ @field.each_byte.inject(0) { |a, byte| (a += 1; byte &= byte - 1) while byte > 0 ; a }
54
+ end
55
+
56
+ def byte_position(position)
57
+ @reverse_byte ? position : 7 - position
41
58
  end
42
59
  end
@@ -1,5 +1,5 @@
1
1
  require "minitest/autorun"
2
- require "bitarray"
2
+ require_relative "../lib/bitarray"
3
3
 
4
4
  class TestBitArray < Minitest::Test
5
5
  def setup
@@ -30,15 +30,19 @@ class TestBitArray < Minitest::Test
30
30
 
31
31
  def test_multiple_setting
32
32
  1.upto(999) do |pos|
33
- 2.times { @public_ba[pos] = 1 }
34
- assert_equal 1, @public_ba[pos]
33
+ 2.times do
34
+ @public_ba[pos] = 1
35
+ assert_equal 1, @public_ba[pos]
36
+ end
35
37
  end
36
38
  end
37
39
 
38
40
  def test_multiple_unsetting
39
41
  1.upto(999) do |pos|
40
- 2.times { @public_ba[pos] = 0 }
41
- assert_equal 0, @public_ba[pos]
42
+ 2.times do
43
+ @public_ba[pos] = 0
44
+ assert_equal 0, @public_ba[pos]
45
+ end
42
46
  end
43
47
  end
44
48
 
@@ -48,10 +52,26 @@ class TestBitArray < Minitest::Test
48
52
 
49
53
  def test_to_s
50
54
  ba = BitArray.new(35)
51
- [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}
52
56
  assert_equal "01000111001000001000000000000000010", ba.to_s
53
57
  end
54
58
 
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
+
55
75
  def test_total_set
56
76
  ba = BitArray.new(10)
57
77
  ba[1] = 1
@@ -60,3 +80,27 @@ class TestBitArray < Minitest::Test
60
80
  assert_equal 3, ba.total_set
61
81
  end
62
82
  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
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitarray
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Cooper
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-07 00:00:00.000000000 Z
11
+ date: 2022-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -47,7 +47,6 @@ extra_rdoc_files: []
47
47
  files:
48
48
  - ".gitignore"
49
49
  - Gemfile
50
- - Gemfile.lock
51
50
  - README.md
52
51
  - Rakefile
53
52
  - bitarray.gemspec
@@ -58,7 +57,7 @@ files:
58
57
  homepage: https://github.com/peterc/bitarray
59
58
  licenses: []
60
59
  metadata: {}
61
- post_install_message:
60
+ post_install_message:
62
61
  rdoc_options: []
63
62
  require_paths:
64
63
  - lib
@@ -73,9 +72,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
72
  - !ruby/object:Gem::Version
74
73
  version: '0'
75
74
  requirements: []
76
- rubyforge_project:
77
- rubygems_version: 2.6.8
78
- signing_key:
75
+ rubygems_version: 3.2.22
76
+ signing_key:
79
77
  specification_version: 4
80
78
  summary: A simple, pure Ruby bit-array / bitfield implementation.
81
79
  test_files:
data/Gemfile.lock DELETED
@@ -1,21 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- bitarray (1.0.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- minitest (5.10.1)
10
- rake (12.0.0)
11
-
12
- PLATFORMS
13
- ruby
14
-
15
- DEPENDENCIES
16
- bitarray!
17
- minitest
18
- rake
19
-
20
- BUNDLED WITH
21
- 1.13.7