ingramj-bitarray 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/LICENSE +23 -0
- data/README +28 -0
- data/Rakefile +22 -0
- data/TODO +9 -0
- data/VERSION +1 -0
- data/bitarray.gemspec +53 -0
- data/ext/bitarray.c +347 -0
- data/ext/extconf.rb +3 -0
- data/test/bitfield.rb +67 -0
- data/test/bm.rb +38 -0
- data/test/test.rb +91 -0
- metadata +71 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2009 James E. Ingram
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
data/README
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
A bit array class for Ruby, implemented as a C extension. Includes methods for
|
2
|
+
setting and clearing individual bits, and all bits at once. Also has the
|
3
|
+
standard array access methods, [] and []=, and it mixes in Enumerable.
|
4
|
+
|
5
|
+
The test/ directory has a unit test file. It also has a benchmark utility for
|
6
|
+
comparison with Peter Cooper's pure Ruby BitField class.
|
7
|
+
|
8
|
+
This extension has only been tested with Ruby 1.9.1 on Ubuntu, and probably
|
9
|
+
won't compile for Ruby 1.8. Compatibility with Ruby 1.8 is planned.
|
10
|
+
|
11
|
+
This library is available as a gem. Install it with
|
12
|
+
|
13
|
+
sudo gem install ingramj-bitarray -s http://gems.github.com
|
14
|
+
|
15
|
+
Alternately, you can clone the repository and build things manually.
|
16
|
+
|
17
|
+
See the TODO file for current limitations and plans.
|
18
|
+
|
19
|
+
See the LICENSE file for copyright and license information.
|
20
|
+
|
21
|
+
Some inspiration was taken from Peter Cooper's BitField class, and various Open
|
22
|
+
Source bitarray implementations found online, but this implementation contains
|
23
|
+
only original code, except for test/bitfield.rb, which is Peter Cooper's
|
24
|
+
BitField class, and test/test.rb, which was modified from the BitField test
|
25
|
+
code.
|
26
|
+
|
27
|
+
BitField: http://snippets.dzone.com/posts/show/4234
|
28
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = "bitarray"
|
5
|
+
gem.summary = "A bitarray class for Ruby, implemented as a C extension."
|
6
|
+
gem.description = <<EOF
|
7
|
+
A bit array class for Ruby, implemented as a C extension. Includes methods for
|
8
|
+
setting and clearing individual bits, and all bits at once. Also has the
|
9
|
+
standard array access methods, [] and []=, and it mixes in Enumerable.
|
10
|
+
EOF
|
11
|
+
gem.email = "ingramj@gmail.com"
|
12
|
+
gem.homepage = "http://github.com/ingramj/bitarray"
|
13
|
+
gem.authors = ["James E. Ingram"]
|
14
|
+
gem.require_paths = ["ext"]
|
15
|
+
gem.extensions = ["ext/extconf.rb"]
|
16
|
+
gem.required_ruby_version = ">= 1.9.1"
|
17
|
+
gem.rdoc_options << '--exclude' << 'ext/Makefile' << '--title' << ' BitArray Documentation'
|
18
|
+
end
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
21
|
+
end
|
22
|
+
|
data/TODO
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
* Comment code. I'd like for it to be useful as a tutorial for extension
|
2
|
+
writing, especially with regards to implementing new types.
|
3
|
+
* Implement some more methods, like Hamming weight, and slice, and in-place
|
4
|
+
enumerator methods (map!, reverse!, etc).
|
5
|
+
* Maybe allow resizing.
|
6
|
+
* Write more tests
|
7
|
+
* Ruby 1.8 compatibility. This will probably involve just changing some of the
|
8
|
+
NUM2x and x2NUM defines.
|
9
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bitarray.gemspec
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{bitarray}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["James E. Ingram"]
|
9
|
+
s.date = %q{2009-05-23}
|
10
|
+
s.description = %q{A bit array class for Ruby, implemented as a C extension. Includes methods for setting and clearing individual bits, and all bits at once. Also has the standard array access methods, [] and []=, and it mixes in Enumerable.}
|
11
|
+
s.email = %q{ingramj@gmail.com}
|
12
|
+
s.extensions = ["ext/extconf.rb"]
|
13
|
+
s.extra_rdoc_files = [
|
14
|
+
"LICENSE",
|
15
|
+
"README"
|
16
|
+
]
|
17
|
+
s.files = [
|
18
|
+
".gitignore",
|
19
|
+
"LICENSE",
|
20
|
+
"README",
|
21
|
+
"Rakefile",
|
22
|
+
"TODO",
|
23
|
+
"VERSION",
|
24
|
+
"bitarray.gemspec",
|
25
|
+
"ext/bitarray.c",
|
26
|
+
"ext/extconf.rb",
|
27
|
+
"test/bitfield.rb",
|
28
|
+
"test/bm.rb",
|
29
|
+
"test/test.rb"
|
30
|
+
]
|
31
|
+
s.has_rdoc = true
|
32
|
+
s.homepage = %q{http://github.com/ingramj/bitarray}
|
33
|
+
s.rdoc_options = ["--charset=UTF-8", "--exclude", "ext/Makefile", "--title", " BitArray Documentation"]
|
34
|
+
s.require_paths = ["ext"]
|
35
|
+
s.required_ruby_version = Gem::Requirement.new(">= 1.9.1")
|
36
|
+
s.rubygems_version = %q{1.3.1}
|
37
|
+
s.summary = %q{A bitarray class for Ruby, implemented as a C extension.}
|
38
|
+
s.test_files = [
|
39
|
+
"test/bitfield.rb",
|
40
|
+
"test/test.rb",
|
41
|
+
"test/bm.rb"
|
42
|
+
]
|
43
|
+
|
44
|
+
if s.respond_to? :specification_version then
|
45
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
46
|
+
s.specification_version = 2
|
47
|
+
|
48
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
49
|
+
else
|
50
|
+
end
|
51
|
+
else
|
52
|
+
end
|
53
|
+
end
|
data/ext/bitarray.c
ADDED
@@ -0,0 +1,347 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include <limits.h>
|
3
|
+
#include <string.h>
|
4
|
+
|
5
|
+
/* Bits are stored in an array of unsigned ints. We'll use a couple of defines
|
6
|
+
* for getting the size of unsigned ints in bytes and bits.
|
7
|
+
*/
|
8
|
+
#define UINT_BYTES (sizeof(unsigned int))
|
9
|
+
#define UINT_BITS (UINT_BYTES * CHAR_BIT)
|
10
|
+
|
11
|
+
/* bitmask macro for accessing a particular bit inside an array element. */
|
12
|
+
#define bitmask(bit) (1 << (bit % UINT_BITS))
|
13
|
+
|
14
|
+
/* Bit Array structure. */
|
15
|
+
struct bit_array {
|
16
|
+
size_t bits; /* Number of bits. */
|
17
|
+
size_t array_size; /* Size of the storage array. */
|
18
|
+
unsigned int *array; /* Array of unsigned ints, used for bit storage. */
|
19
|
+
};
|
20
|
+
|
21
|
+
|
22
|
+
/* Bit Array manipulation functions. */
|
23
|
+
|
24
|
+
/* Set the specified bit to 1. Return 1 on success, 0 on failure. */
|
25
|
+
static int
|
26
|
+
set_bit(struct bit_array *ba, ptrdiff_t bit)
|
27
|
+
{
|
28
|
+
if (bit < 0) bit += ba->bits;
|
29
|
+
if (bit >= ba->bits) {
|
30
|
+
return 0;
|
31
|
+
}
|
32
|
+
|
33
|
+
ba->array[bit / UINT_BITS] |= bitmask(bit);
|
34
|
+
return 1;
|
35
|
+
}
|
36
|
+
|
37
|
+
|
38
|
+
/* Set all bits to 1. */
|
39
|
+
static int
|
40
|
+
set_all_bits(struct bit_array *ba)
|
41
|
+
{
|
42
|
+
memset(ba->array, 0xff, (ba->array_size * UINT_BYTES));
|
43
|
+
return 1;
|
44
|
+
}
|
45
|
+
|
46
|
+
|
47
|
+
/* Clear the specified bit to 0. Return 1 on success, 0 on failure. */
|
48
|
+
static int
|
49
|
+
clear_bit(struct bit_array *ba, ptrdiff_t bit)
|
50
|
+
{
|
51
|
+
if (bit < 0) bit += ba->bits;
|
52
|
+
if (bit >= ba->bits) {
|
53
|
+
return 0;
|
54
|
+
}
|
55
|
+
|
56
|
+
ba->array[bit / UINT_BITS] &= ~bitmask(bit);
|
57
|
+
return 1;
|
58
|
+
}
|
59
|
+
|
60
|
+
|
61
|
+
/* Clear all bits to 0. */
|
62
|
+
static int
|
63
|
+
clear_all_bits(struct bit_array *ba)
|
64
|
+
{
|
65
|
+
memset(ba->array, 0x00, (ba->array_size * UINT_BYTES));
|
66
|
+
return 1;
|
67
|
+
}
|
68
|
+
|
69
|
+
|
70
|
+
/* Toggle the state of the specified bit. Return 1 on success, 0 on failure. */
|
71
|
+
static int
|
72
|
+
toggle_bit(struct bit_array *ba, ptrdiff_t bit)
|
73
|
+
{
|
74
|
+
if (bit < 0) bit += ba->bits;
|
75
|
+
if (bit >= ba->bits) {
|
76
|
+
return 0;
|
77
|
+
}
|
78
|
+
|
79
|
+
ba->array[bit / UINT_BITS] ^= bitmask(bit);
|
80
|
+
return 1;
|
81
|
+
}
|
82
|
+
|
83
|
+
|
84
|
+
/* Assign the specified value to a bit. Return 1 on success, 0 on invalid bit
|
85
|
+
* index, and -1 on invalid value. */
|
86
|
+
static int
|
87
|
+
assign_bit(struct bit_array *ba, ptrdiff_t bit, int value)
|
88
|
+
{
|
89
|
+
if (value == 0) {
|
90
|
+
return clear_bit(ba, bit);
|
91
|
+
} else if (value == 1) {
|
92
|
+
return set_bit(ba, bit);
|
93
|
+
} else {
|
94
|
+
return -1;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
|
99
|
+
/* Get the state of the specified bit. Return -1 on failure. */
|
100
|
+
static int
|
101
|
+
get_bit(struct bit_array *ba, ptrdiff_t bit)
|
102
|
+
{
|
103
|
+
if (bit < 0) bit += ba->bits;
|
104
|
+
if (bit >= ba->bits) {
|
105
|
+
return -1;
|
106
|
+
}
|
107
|
+
|
108
|
+
unsigned int b = (ba->array[bit / UINT_BITS] & bitmask(bit));
|
109
|
+
if (b > 0) {
|
110
|
+
return 1;
|
111
|
+
} else {
|
112
|
+
return 0;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
|
117
|
+
/* Ruby Interface Functions. These aren't documented yet. */
|
118
|
+
|
119
|
+
static VALUE rb_bitarray_class;
|
120
|
+
|
121
|
+
|
122
|
+
static void
|
123
|
+
rb_bitarray_free(struct bit_array *ba)
|
124
|
+
{
|
125
|
+
if (ba && ba->array) {
|
126
|
+
ruby_xfree(ba->array);
|
127
|
+
}
|
128
|
+
ruby_xfree(ba);
|
129
|
+
}
|
130
|
+
|
131
|
+
|
132
|
+
static VALUE
|
133
|
+
rb_bitarray_alloc(VALUE klass)
|
134
|
+
{
|
135
|
+
struct bit_array *ba;
|
136
|
+
return Data_Make_Struct(rb_bitarray_class, struct bit_array, NULL,
|
137
|
+
rb_bitarray_free, ba);
|
138
|
+
}
|
139
|
+
|
140
|
+
|
141
|
+
static VALUE
|
142
|
+
rb_bitarray_initialize(VALUE self, VALUE size)
|
143
|
+
{
|
144
|
+
struct bit_array *ba;
|
145
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
146
|
+
|
147
|
+
size_t bits = NUM2SIZET(size);
|
148
|
+
size_t array_size = ((bits - 1) / UINT_BITS) + 1;
|
149
|
+
|
150
|
+
ba->bits = bits;
|
151
|
+
ba->array_size = array_size;
|
152
|
+
ba->array = ruby_xcalloc(array_size, UINT_BYTES);
|
153
|
+
|
154
|
+
return self;
|
155
|
+
}
|
156
|
+
|
157
|
+
|
158
|
+
static VALUE
|
159
|
+
rb_bitarray_clone(VALUE self)
|
160
|
+
{
|
161
|
+
struct bit_array *ba, *new_ba;
|
162
|
+
VALUE rb_new_ba;
|
163
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
164
|
+
rb_new_ba = Data_Make_Struct(rb_bitarray_class, struct bit_array, NULL,
|
165
|
+
rb_bitarray_free, new_ba);
|
166
|
+
|
167
|
+
new_ba->bits = ba->bits;
|
168
|
+
new_ba->array_size = ba->array_size;
|
169
|
+
new_ba->array = ruby_xcalloc(new_ba->array_size, UINT_BYTES);
|
170
|
+
|
171
|
+
memcpy(new_ba->array, ba->array, (ba->array_size * UINT_BYTES));
|
172
|
+
|
173
|
+
return rb_new_ba;
|
174
|
+
}
|
175
|
+
|
176
|
+
|
177
|
+
static VALUE
|
178
|
+
rb_bitarray_size(VALUE self)
|
179
|
+
{
|
180
|
+
struct bit_array *ba;
|
181
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
182
|
+
|
183
|
+
return SIZET2NUM(ba->bits);
|
184
|
+
}
|
185
|
+
|
186
|
+
|
187
|
+
static VALUE
|
188
|
+
rb_bitarray_set_bit(VALUE self, VALUE bit)
|
189
|
+
{
|
190
|
+
struct bit_array *ba;
|
191
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
192
|
+
|
193
|
+
ptrdiff_t index = NUM2SSIZET(bit);
|
194
|
+
|
195
|
+
if (set_bit(ba, index)) {
|
196
|
+
return self;
|
197
|
+
} else {
|
198
|
+
rb_raise(rb_eIndexError, "index %ld out of bit array", (long)index);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
202
|
+
|
203
|
+
static VALUE
|
204
|
+
rb_bitarray_set_all_bits(VALUE self)
|
205
|
+
{
|
206
|
+
struct bit_array *ba;
|
207
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
208
|
+
|
209
|
+
if(set_all_bits(ba)) {
|
210
|
+
return self;
|
211
|
+
} else {
|
212
|
+
rb_bug("BitArray#set_all_bits failed. This should not occur.");
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
|
217
|
+
static VALUE
|
218
|
+
rb_bitarray_clear_bit(VALUE self, VALUE bit)
|
219
|
+
{
|
220
|
+
struct bit_array *ba;
|
221
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
222
|
+
|
223
|
+
ptrdiff_t index = NUM2SSIZET(bit);
|
224
|
+
|
225
|
+
if (clear_bit(ba, index)) {
|
226
|
+
return self;
|
227
|
+
} else {
|
228
|
+
rb_raise(rb_eIndexError, "index %ld out of bit array", (long)index);
|
229
|
+
}
|
230
|
+
}
|
231
|
+
|
232
|
+
|
233
|
+
static VALUE
|
234
|
+
rb_bitarray_clear_all_bits(VALUE self)
|
235
|
+
{
|
236
|
+
struct bit_array *ba;
|
237
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
238
|
+
|
239
|
+
if(clear_all_bits(ba)) {
|
240
|
+
return self;
|
241
|
+
} else {
|
242
|
+
rb_bug("BitArray#clear_all_bits failed. This should not occur.");
|
243
|
+
}
|
244
|
+
}
|
245
|
+
|
246
|
+
|
247
|
+
static VALUE
|
248
|
+
rb_bitarray_get_bit(VALUE self, VALUE bit)
|
249
|
+
{
|
250
|
+
struct bit_array *ba;
|
251
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
252
|
+
|
253
|
+
ptrdiff_t index = NUM2SSIZET(bit);
|
254
|
+
|
255
|
+
int bit_value = get_bit(ba, index);
|
256
|
+
|
257
|
+
if (bit_value >= 0) {
|
258
|
+
return INT2NUM(bit_value);
|
259
|
+
} else {
|
260
|
+
rb_raise(rb_eIndexError, "index %ld out of bit array", (long)index);
|
261
|
+
}
|
262
|
+
}
|
263
|
+
|
264
|
+
|
265
|
+
static VALUE
|
266
|
+
rb_bitarray_assign_bit(VALUE self, VALUE bit, VALUE value)
|
267
|
+
{
|
268
|
+
struct bit_array *ba;
|
269
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
270
|
+
|
271
|
+
ptrdiff_t index = NUM2SSIZET(bit);
|
272
|
+
int bit_value = NUM2INT(value);
|
273
|
+
|
274
|
+
int result = assign_bit(ba, index, bit_value);
|
275
|
+
if (result == 1) {
|
276
|
+
return self;
|
277
|
+
} else if (result == 0) {
|
278
|
+
rb_raise(rb_eIndexError, "index %ld out of bit array", (long)index);
|
279
|
+
} else {
|
280
|
+
rb_raise(rb_eRuntimeError, "bit value %d out of range", bit_value);
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
|
285
|
+
static VALUE
|
286
|
+
rb_bitarray_inspect(VALUE self)
|
287
|
+
{
|
288
|
+
struct bit_array *ba;
|
289
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
290
|
+
|
291
|
+
size_t cstr_size = ba->bits + 1;
|
292
|
+
char cstr[cstr_size];
|
293
|
+
|
294
|
+
size_t i;
|
295
|
+
for (i = 0; i < ba->bits; i++) {
|
296
|
+
cstr[i] = get_bit(ba, i) + '0';
|
297
|
+
}
|
298
|
+
cstr[ba->bits] = '\0';
|
299
|
+
|
300
|
+
VALUE str = rb_str_new2(cstr);
|
301
|
+
return str;
|
302
|
+
}
|
303
|
+
|
304
|
+
|
305
|
+
static VALUE
|
306
|
+
rb_bitarray_each(VALUE self)
|
307
|
+
{
|
308
|
+
struct bit_array *ba;
|
309
|
+
Data_Get_Struct(self, struct bit_array, ba);
|
310
|
+
|
311
|
+
size_t i;
|
312
|
+
|
313
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
314
|
+
for (i = 0; i < ba->bits; i++) {
|
315
|
+
int bit_value = get_bit(ba, i);
|
316
|
+
rb_yield(INT2NUM(bit_value));
|
317
|
+
}
|
318
|
+
return self;
|
319
|
+
}
|
320
|
+
|
321
|
+
|
322
|
+
void
|
323
|
+
Init_bitarray()
|
324
|
+
{
|
325
|
+
rb_bitarray_class = rb_define_class("BitArray", rb_cObject);
|
326
|
+
rb_define_alloc_func(rb_bitarray_class, rb_bitarray_alloc);
|
327
|
+
|
328
|
+
rb_define_method(rb_bitarray_class, "initialize",
|
329
|
+
rb_bitarray_initialize, 1);
|
330
|
+
rb_define_method(rb_bitarray_class, "clone", rb_bitarray_clone, 0);
|
331
|
+
rb_define_method(rb_bitarray_class, "size", rb_bitarray_size, 0);
|
332
|
+
rb_define_alias(rb_bitarray_class, "length", "size");
|
333
|
+
rb_define_method(rb_bitarray_class, "set_bit", rb_bitarray_set_bit, 1);
|
334
|
+
rb_define_method(rb_bitarray_class, "set_all_bits",
|
335
|
+
rb_bitarray_set_all_bits, 0);
|
336
|
+
rb_define_method(rb_bitarray_class, "clear_bit", rb_bitarray_clear_bit, 1);
|
337
|
+
rb_define_method(rb_bitarray_class, "clear_all_bits",
|
338
|
+
rb_bitarray_clear_all_bits, 0);
|
339
|
+
rb_define_method(rb_bitarray_class, "[]", rb_bitarray_get_bit, 1);
|
340
|
+
rb_define_method(rb_bitarray_class, "[]=", rb_bitarray_assign_bit, 2);
|
341
|
+
rb_define_method(rb_bitarray_class, "inspect", rb_bitarray_inspect, 0);
|
342
|
+
rb_define_alias(rb_bitarray_class, "to_s", "inspect");
|
343
|
+
rb_define_method(rb_bitarray_class, "each", rb_bitarray_each, 0);
|
344
|
+
|
345
|
+
rb_include_module(rb_bitarray_class, rb_mEnumerable);
|
346
|
+
}
|
347
|
+
|
data/ext/extconf.rb
ADDED
data/test/bitfield.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# NAME: BitField
|
2
|
+
# AUTHOR: Peter Cooper
|
3
|
+
# LICENSE: MIT ( http://www.opensource.org/licenses/mit-license.php )
|
4
|
+
# COPYRIGHT: (c) 2007 Peter Cooper (http://www.petercooper.co.uk/)
|
5
|
+
# VERSION: v4
|
6
|
+
# HISTORY: v4 (fixed bug where setting 0 bits to 0 caused a set to 1)
|
7
|
+
# v3 (supports dynamic bitwidths for array elements.. now doing 32 bit widths default)
|
8
|
+
# v2 (now uses 1 << y, rather than 2 ** y .. it's 21.8 times faster!)
|
9
|
+
# v1 (first release)
|
10
|
+
#
|
11
|
+
# DESCRIPTION: Basic, pure Ruby bit field. Pretty fast (for what it is) and memory efficient.
|
12
|
+
# I've written a pretty intensive test suite for it and it passes great.
|
13
|
+
# Works well for Bloom filters (the reason I wrote it).
|
14
|
+
#
|
15
|
+
# Create a bit field 1000 bits wide
|
16
|
+
# bf = BitField.new(1000)
|
17
|
+
#
|
18
|
+
# Setting and reading bits
|
19
|
+
# bf[100] = 1
|
20
|
+
# bf[100] .. => 1
|
21
|
+
# bf[100] = 0
|
22
|
+
#
|
23
|
+
# More
|
24
|
+
# bf.to_s = "10101000101010101" (example)
|
25
|
+
# bf.total_set .. => 10 (example - 10 bits are set to "1")
|
26
|
+
|
27
|
+
class BitField
|
28
|
+
attr_reader :size
|
29
|
+
include Enumerable
|
30
|
+
|
31
|
+
ELEMENT_WIDTH = 32
|
32
|
+
|
33
|
+
def initialize(size)
|
34
|
+
@size = size
|
35
|
+
@field = Array.new(((size - 1) / ELEMENT_WIDTH) + 1, 0)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set a bit (1/0)
|
39
|
+
def []=(position, value)
|
40
|
+
if value == 1
|
41
|
+
@field[position / ELEMENT_WIDTH] |= 1 << (position % ELEMENT_WIDTH)
|
42
|
+
elsif (@field[position / ELEMENT_WIDTH]) & (1 << (position % ELEMENT_WIDTH)) != 0
|
43
|
+
@field[position / ELEMENT_WIDTH] ^= 1 << (position % ELEMENT_WIDTH)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Read a bit (1/0)
|
48
|
+
def [](position)
|
49
|
+
@field[position / ELEMENT_WIDTH] & 1 << (position % ELEMENT_WIDTH) > 0 ? 1 : 0
|
50
|
+
end
|
51
|
+
|
52
|
+
# Iterate over each bit
|
53
|
+
def each(&block)
|
54
|
+
@size.times { |position| yield self[position] }
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the field as a string like "0101010100111100," etc.
|
58
|
+
def to_s
|
59
|
+
inject("") { |a, b| a + b.to_s }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the total number of bits that are set
|
63
|
+
# (The technique used here is about 6 times faster than using each or inject direct on the bitfield)
|
64
|
+
def total_set
|
65
|
+
@field.inject(0) { |a, byte| a += byte & 1 and byte >>= 1 until byte == 0; a }
|
66
|
+
end
|
67
|
+
end
|
data/test/bm.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'bitfield'
|
2
|
+
require 'bitarray'
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
Benchmark.bm(24) { |bm|
|
6
|
+
puts "------------------------ Object instantiation."
|
7
|
+
bm.report("BitField initialize") { 10000.times { BitField.new(256) } }
|
8
|
+
bm.report("BitArray initialize") { 10000.times { BitArray.new(256) } }
|
9
|
+
|
10
|
+
bf = BitField.new(256)
|
11
|
+
ba = BitArray.new(256)
|
12
|
+
|
13
|
+
puts "------------------------ Element Reading"
|
14
|
+
bm.report("BitField []") { 10000.times { bf[rand(256)] } }
|
15
|
+
bm.report("BitArray []") { 10000.times { ba[rand(256)] } }
|
16
|
+
|
17
|
+
puts "------------------------ Element Writing"
|
18
|
+
bm.report("BitField []=") { 10000.times { bf[rand(256)] = [0,1][rand(2)] } }
|
19
|
+
bm.report("BitArray []=") { 10000.times { ba[rand(256)] = [0,1][rand(2)] } }
|
20
|
+
|
21
|
+
puts "------------------------ Element Enumeration"
|
22
|
+
bm.report("BitField each") { 10000.times { bf.each {|b| b } } }
|
23
|
+
bm.report("BitArray each") { 10000.times { ba.each {|b| b } } }
|
24
|
+
|
25
|
+
puts "------------------------ To String"
|
26
|
+
bm.report("BitField to_s") { 10000.times { bf.to_s } }
|
27
|
+
bm.report("BitArray to_s") { 10000.times { ba.to_s } }
|
28
|
+
|
29
|
+
puts "------------------------ Set All"
|
30
|
+
bm.report("BitArray set_all_bits") { 10000.times { ba.set_all_bits} }
|
31
|
+
|
32
|
+
puts "------------------------ Clear All"
|
33
|
+
bm.report("BitArray clear_all_bits") { 10000.times { ba.clear_all_bits } }
|
34
|
+
|
35
|
+
puts "------------------------ Clone"
|
36
|
+
bm.report("BitArray clone") { 10000.times { ba.clone } }
|
37
|
+
}
|
38
|
+
|
data/test/test.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Peter Cooper's BitField test file, modified for BitArray.
|
2
|
+
# http://snippets.dzone.com/posts/show/4234
|
3
|
+
require "test/unit"
|
4
|
+
require "bitarray"
|
5
|
+
|
6
|
+
class TestLibraryFileName < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@public_ba = BitArray.new(1000)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_basic
|
12
|
+
assert_equal 0, BitArray.new(100)[0]
|
13
|
+
assert_equal 0, BitArray.new(100)[1]
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_setting_and_unsetting
|
17
|
+
@public_ba[100] = 1
|
18
|
+
assert_equal 1, @public_ba[100]
|
19
|
+
@public_ba[100] = 0
|
20
|
+
assert_equal 0, @public_ba[100]
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_random_setting_and_unsetting
|
24
|
+
100.times do
|
25
|
+
index = rand(1000)
|
26
|
+
@public_ba[index] = 1
|
27
|
+
assert_equal 1, @public_ba[index]
|
28
|
+
@public_ba[index] = 0
|
29
|
+
assert_equal 0, @public_ba[index]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_multiple_setting
|
34
|
+
1.upto(999) do |pos|
|
35
|
+
2.times { @public_ba[pos] = 1 }
|
36
|
+
assert_equal 1, @public_ba[pos]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_multiple_unsetting
|
41
|
+
1.upto(999) do |pos|
|
42
|
+
2.times { @public_ba[pos] = 0 }
|
43
|
+
assert_equal 0, @public_ba[pos]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_size
|
48
|
+
assert_equal 1000, @public_ba.size
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def test_to_s
|
53
|
+
ba = BitArray.new(10)
|
54
|
+
ba[1] = 1
|
55
|
+
ba[5] = 1
|
56
|
+
assert_equal "0100010000", ba.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def test_set_all_bits
|
61
|
+
ba = BitArray.new(10)
|
62
|
+
ba.set_all_bits
|
63
|
+
assert_equal "1111111111", ba.to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_clear_all_bits
|
67
|
+
ba = BitArray.new(10)
|
68
|
+
ba[1] = 1
|
69
|
+
ba[5] = 1
|
70
|
+
ba.clear_all_bits
|
71
|
+
assert_equal "0000000000", ba.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_clone
|
75
|
+
ba = BitArray.new(10)
|
76
|
+
ba[1] = 1
|
77
|
+
ba[5] = 1
|
78
|
+
ba_clone = ba.clone
|
79
|
+
assert_equal ba_clone.to_s, ba.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
# This method is not implemented yet.
|
83
|
+
|
84
|
+
#def test_total_set
|
85
|
+
# bf = BitArray.new(10)
|
86
|
+
# bf[1] = 1
|
87
|
+
# bf[5] = 1
|
88
|
+
# assert_equal 2, bf.total_set
|
89
|
+
#end
|
90
|
+
end
|
91
|
+
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ingramj-bitarray
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James E. Ingram
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-23 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A bit array class for Ruby, implemented as a C extension. Includes methods for setting and clearing individual bits, and all bits at once. Also has the standard array access methods, [] and []=, and it mixes in Enumerable.
|
17
|
+
email: ingramj@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions:
|
21
|
+
- ext/extconf.rb
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- LICENSE
|
28
|
+
- README
|
29
|
+
- Rakefile
|
30
|
+
- TODO
|
31
|
+
- VERSION
|
32
|
+
- bitarray.gemspec
|
33
|
+
- ext/bitarray.c
|
34
|
+
- ext/extconf.rb
|
35
|
+
- test/bitfield.rb
|
36
|
+
- test/bm.rb
|
37
|
+
- test/test.rb
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://github.com/ingramj/bitarray
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options:
|
42
|
+
- --charset=UTF-8
|
43
|
+
- --exclude
|
44
|
+
- ext/Makefile
|
45
|
+
- --title
|
46
|
+
- " BitArray Documentation"
|
47
|
+
require_paths:
|
48
|
+
- ext
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.9.1
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.2.0
|
65
|
+
signing_key:
|
66
|
+
specification_version: 2
|
67
|
+
summary: A bitarray class for Ruby, implemented as a C extension.
|
68
|
+
test_files:
|
69
|
+
- test/bitfield.rb
|
70
|
+
- test/test.rb
|
71
|
+
- test/bm.rb
|