ingramj-bitarray 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|