ingramj-bitarray 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/Rakefile +1 -1
  2. data/VERSION +1 -1
  3. data/bitarray.gemspec +3 -3
  4. data/ext/bitarray.c +175 -30
  5. data/test/bm.rb +20 -17
  6. data/test/test.rb +28 -4
  7. metadata +3 -3
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ EOF
14
14
  gem.require_paths = ["ext"]
15
15
  gem.extensions = ["ext/extconf.rb"]
16
16
  gem.required_ruby_version = ">= 1.9.1"
17
- gem.rdoc_options << '--exclude' << 'ext/Makefile' << '--title' << ' BitArray Documentation'
17
+ gem.rdoc_options << '--exclude' << 'ext/Makefile' << '--title' << 'BitArray Documentation'
18
18
  end
19
19
  rescue LoadError
20
20
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.1
data/bitarray.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{bitarray}
5
- s.version = "0.2.0"
5
+ s.version = "0.3.1"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["James E. Ingram"]
9
- s.date = %q{2009-05-23}
9
+ s.date = %q{2009-05-24}
10
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
11
  s.email = %q{ingramj@gmail.com}
12
12
  s.extensions = ["ext/extconf.rb"]
@@ -30,7 +30,7 @@ Gem::Specification.new do |s|
30
30
  ]
31
31
  s.has_rdoc = true
32
32
  s.homepage = %q{http://github.com/ingramj/bitarray}
33
- s.rdoc_options = ["--charset=UTF-8", "--exclude", "ext/Makefile", "--title", " BitArray Documentation"]
33
+ s.rdoc_options = ["--charset=UTF-8", "--exclude", "ext/Makefile", "--title", "BitArray Documentation"]
34
34
  s.require_paths = ["ext"]
35
35
  s.required_ruby_version = Gem::Requirement.new(">= 1.9.1")
36
36
  s.rubygems_version = %q{1.3.1}
data/ext/bitarray.c CHANGED
@@ -183,7 +183,12 @@ rb_bitarray_initialize(VALUE self, VALUE size)
183
183
  struct bit_array *ba;
184
184
  Data_Get_Struct(self, struct bit_array, ba);
185
185
 
186
- size_t bits = NUM2SIZET(size);
186
+ ptrdiff_t bits = NUM2SSIZET(size);
187
+ if (bits <= 0) {
188
+ ba->bits = 0;
189
+ ba->array_size = 0;
190
+ return self;
191
+ }
187
192
  size_t array_size = ((bits - 1) / UINT_BITS) + 1;
188
193
 
189
194
  ba->bits = bits;
@@ -201,21 +206,62 @@ rb_bitarray_initialize(VALUE self, VALUE size)
201
206
  * Produces a copy of _bitarray_.
202
207
  */
203
208
  static VALUE
204
- rb_bitarray_clone(VALUE self)
209
+ rb_bitarray_initialize_copy(VALUE self, VALUE orig)
205
210
  {
206
- struct bit_array *ba, *new_ba;
207
- VALUE rb_new_ba;
208
- Data_Get_Struct(self, struct bit_array, ba);
209
- rb_new_ba = Data_Make_Struct(rb_bitarray_class, struct bit_array, NULL,
210
- rb_bitarray_free, new_ba);
211
+ struct bit_array *new_ba, *orig_ba;
212
+ Data_Get_Struct(self, struct bit_array, new_ba);
213
+ Data_Get_Struct(orig, struct bit_array, orig_ba);
211
214
 
212
- new_ba->bits = ba->bits;
213
- new_ba->array_size = ba->array_size;
215
+ new_ba->bits = orig_ba->bits;
216
+ new_ba->array_size = orig_ba->array_size;
214
217
  new_ba->array = ruby_xcalloc(new_ba->array_size, UINT_BYTES);
215
218
 
216
- memcpy(new_ba->array, ba->array, (ba->array_size * UINT_BYTES));
219
+ memcpy(new_ba->array, orig_ba->array, (new_ba->array_size * UINT_BYTES));
217
220
 
218
- return rb_new_ba;
221
+ return self;
222
+ }
223
+
224
+
225
+ /* call-seq:
226
+ * bitarray + other_bitarray -> a_bitarray
227
+ *
228
+ * Concatenation---Return a new BitArray built by concatenating the two
229
+ * BitArrays.
230
+ */
231
+ static VALUE
232
+ rb_bitarray_concat(VALUE x, VALUE y)
233
+ {
234
+ /* Get the bit_arrays from x and y */
235
+ struct bit_array *x_ba, *y_ba;
236
+ Data_Get_Struct(x, struct bit_array, x_ba);
237
+ Data_Get_Struct(y, struct bit_array, y_ba);
238
+
239
+ /* Create a new BitArray, and its bit_array */
240
+ VALUE z;
241
+ struct bit_array *z_ba;
242
+ z = rb_bitarray_alloc(rb_bitarray_class);
243
+ rb_bitarray_initialize(z, SIZET2NUM(x_ba->bits + y_ba->bits));
244
+ Data_Get_Struct(z, struct bit_array, z_ba);
245
+
246
+ /* For each bit set in x and y, set the corresponding bit in z.
247
+ *
248
+ * It might be faster to memcpy x_ba->array to the beginning of
249
+ * z_ba->array, then set the rest of the bits by looping over y...
250
+ */
251
+ size_t z_index = 0;
252
+ size_t i;
253
+ for (i = 0; i < x_ba->bits; i++, z_index++) {
254
+ if (get_bit(x_ba, i) == 1) {
255
+ set_bit(z_ba, z_index);
256
+ }
257
+ }
258
+ for (i = 0; i < y_ba->bits; i++, z_index++) {
259
+ if (get_bit(y_ba, i) == 1) {
260
+ set_bit(z_ba, z_index);
261
+ }
262
+ }
263
+
264
+ return z;
219
265
  }
220
266
 
221
267
 
@@ -376,22 +422,13 @@ rb_bitarray_toggle_all_bits(VALUE self)
376
422
  }
377
423
  }
378
424
 
379
-
380
- /* call-seq:
381
- * bitarray[index] -> value
382
- *
383
- * Bit Reference---Returns the bit at _index_. Negative indices count backwards
384
- * from the end of _bitarray_. If _index_ is greater than the capacity of
385
- * _bitarray_, an +IndexError+ is raised.
386
- */
425
+ /* Return an individual bit. */
387
426
  static VALUE
388
- rb_bitarray_get_bit(VALUE self, VALUE bit)
427
+ rb_bitarray_get_bit(VALUE self, ptrdiff_t index)
389
428
  {
390
429
  struct bit_array *ba;
391
430
  Data_Get_Struct(self, struct bit_array, ba);
392
431
 
393
- ptrdiff_t index = NUM2SSIZET(bit);
394
-
395
432
  int bit_value = get_bit(ba, index);
396
433
 
397
434
  if (bit_value >= 0) {
@@ -402,6 +439,117 @@ rb_bitarray_get_bit(VALUE self, VALUE bit)
402
439
  }
403
440
 
404
441
 
442
+ /* Create a new BitArray from a subsequence of x. */
443
+ static VALUE
444
+ rb_bitarray_subseq(VALUE x, ptrdiff_t beg, ptrdiff_t len)
445
+ {
446
+
447
+ struct bit_array *x_ba;
448
+ Data_Get_Struct(x, struct bit_array, x_ba);
449
+
450
+ /* Quick exit - a negative length, or a beginning past the end of the
451
+ * array returns nil.
452
+ */
453
+ if (beg < 0) {
454
+ beg += x_ba->bits;
455
+ }
456
+ if (len < 0 || beg > x_ba->bits) {
457
+ return Qnil;
458
+ }
459
+
460
+ /* Make sure that we don't try getting more bits than x has. We handle this
461
+ * the same way as Array; if beg+len is past the end of x, shorten len.
462
+ */
463
+ if (x_ba->bits < len || x_ba->bits < (beg + len)) {
464
+ len = x_ba->bits - beg;
465
+ }
466
+
467
+ /* Create a new BitArray of the appropriate size. */
468
+ VALUE y;
469
+ y = rb_bitarray_alloc(rb_bitarray_class);
470
+ rb_bitarray_initialize(y, SSIZET2NUM(len));
471
+ /* If our length is 0, we can just return now. */
472
+ if (len == 0) {
473
+ return y;
474
+ }
475
+ struct bit_array *y_ba;
476
+ Data_Get_Struct(y, struct bit_array, y_ba);
477
+
478
+ /* For each set bit in x[beg..len], set the corresponding bit in y. */
479
+ size_t x_index, y_index;
480
+ for (x_index = beg, y_index = 0;
481
+ x_index < beg + len;
482
+ x_index++, y_index++)
483
+ {
484
+ if (get_bit(x_ba, x_index) == 1) {
485
+ set_bit(y_ba, y_index);
486
+ }
487
+ }
488
+
489
+ return y;
490
+ }
491
+
492
+
493
+ /* call-seq:
494
+ * bitarray[index] -> value
495
+ * bitarray[beg, len] -> a_bitarray
496
+ * bitarray[range] -> a_bitarray
497
+ *
498
+ * Bit Reference---Returns the bit at _index_, or returns a subarray starting
499
+ * at _beg_, and continuing for _len_ bits, or returns a subarray specified by
500
+ * _range_. _Negative indices count backwards from the end of _bitarray_. If
501
+ * _index_ is greater than the capacity of _bitarray_, an +IndexError+ is
502
+ * raised.
503
+ */
504
+
505
+ static VALUE
506
+ rb_bitarray_bitref(int argc, VALUE *argv, VALUE self)
507
+ {
508
+ /* We follow a form similar to rb_ary_aref in array.c */
509
+
510
+ /* Two arguments means we have a beginning and a length */
511
+ if (argc == 2) {
512
+ ptrdiff_t beg = NUM2SSIZET(argv[0]);
513
+ ptrdiff_t len = NUM2SSIZET(argv[1]);
514
+ return rb_bitarray_subseq(self, beg, len);
515
+ }
516
+
517
+ /* Make sure we have either 1 or 2 arguments. */
518
+ if (argc != 1) {
519
+ rb_scan_args(argc, argv, "11", 0, 0);
520
+ }
521
+
522
+ /* If we have a single argument, it can be either an index, or a range. */
523
+ VALUE arg = argv[0];
524
+
525
+ /* rb_ary_aref treats a fixnum argument specially, for a speedup in the
526
+ * most common case. We'll do the same.
527
+ */
528
+ if (FIXNUM_P(arg)) {
529
+ return rb_bitarray_get_bit(self, FIX2LONG(arg));
530
+ }
531
+
532
+ struct bit_array *ba;
533
+ Data_Get_Struct(self, struct bit_array, ba);
534
+ /* Next we see if arg is a range. rb_range_beg_len is defined in range.c
535
+ * If arg is not a range, it returns Qfalse. If arg is a range, but it
536
+ * refers to invalid indices, it returns Qnil. Otherwise, it sets beg and
537
+ * end to the appropriate values.
538
+ */
539
+ long beg, len;
540
+ switch (rb_range_beg_len(arg, &beg, &len, ba->bits, 0)) {
541
+ case Qfalse:
542
+ break;
543
+ case Qnil:
544
+ return Qnil;
545
+ default:
546
+ return rb_bitarray_subseq(self, beg, len);
547
+ }
548
+
549
+ return rb_bitarray_get_bit(self, NUM2SSIZET(arg));
550
+ }
551
+
552
+
405
553
  /* call-seq:
406
554
  * bitarray[index] = value -> value
407
555
  *
@@ -498,15 +646,11 @@ Init_bitarray()
498
646
  {
499
647
  rb_bitarray_class = rb_define_class("BitArray", rb_cObject);
500
648
  rb_define_alloc_func(rb_bitarray_class, rb_bitarray_alloc);
501
-
502
649
  rb_define_method(rb_bitarray_class, "initialize",
503
650
  rb_bitarray_initialize, 1);
504
-
505
- /* TODO: apparently, we need to define an #initialize_copy method instead
506
- * of clone.
507
- */
508
- rb_define_method(rb_bitarray_class, "clone", rb_bitarray_clone, 0);
509
- rb_define_alias(rb_bitarray_class, "dup", "clone");
651
+ rb_define_method(rb_bitarray_class, "initialize_copy",
652
+ rb_bitarray_initialize_copy, 1);
653
+ rb_define_method(rb_bitarray_class, "+", rb_bitarray_concat, 1);
510
654
  rb_define_method(rb_bitarray_class, "size", rb_bitarray_size, 0);
511
655
  rb_define_alias(rb_bitarray_class, "length", "size");
512
656
  rb_define_method(rb_bitarray_class, "total_set", rb_bitarray_total_set, 0);
@@ -520,7 +664,8 @@ Init_bitarray()
520
664
  rb_bitarray_toggle_bit, 1);
521
665
  rb_define_method(rb_bitarray_class, "toggle_all_bits",
522
666
  rb_bitarray_toggle_all_bits, 0);
523
- rb_define_method(rb_bitarray_class, "[]", rb_bitarray_get_bit, 1);
667
+ rb_define_method(rb_bitarray_class, "[]", rb_bitarray_bitref, -1);
668
+ rb_define_alias(rb_bitarray_class, "slice", "[]");
524
669
  rb_define_method(rb_bitarray_class, "[]=", rb_bitarray_assign_bit, 2);
525
670
  rb_define_method(rb_bitarray_class, "inspect", rb_bitarray_inspect, 0);
526
671
  rb_define_alias(rb_bitarray_class, "to_s", "inspect");
data/test/bm.rb CHANGED
@@ -3,44 +3,47 @@ require 'bitarray'
3
3
  require 'benchmark'
4
4
 
5
5
  Benchmark.bm(28) { |bm|
6
- puts "---------------------------- Object instantiation."
6
+ puts "---------------------------- Object instantiation (10,000 iterations)"
7
7
  bm.report("BitField initialize") { 10000.times { BitField.new(256) } }
8
8
  bm.report("BitArray initialize") { 10000.times { BitArray.new(256) } }
9
9
 
10
10
  bf = BitField.new(256)
11
11
  ba = BitArray.new(256)
12
12
 
13
- puts "---------------------------- Element Reading"
13
+ puts "---------------------------- Element Reading (10,000 iterations)"
14
14
  bm.report("BitField []") { 10000.times { bf[rand(256)] } }
15
15
  bm.report("BitArray []") { 10000.times { ba[rand(256)] } }
16
16
 
17
- puts "---------------------------- Element Writing"
17
+ puts "---------------------------- Element Writing (10,000 iterations)"
18
18
  bm.report("BitField []=") { 10000.times { bf[rand(256)] = [0,1][rand(2)] } }
19
19
  bm.report("BitArray []=") { 10000.times { ba[rand(256)] = [0,1][rand(2)] } }
20
20
 
21
- puts "---------------------------- Element Enumeration"
21
+ puts "---------------------------- Element Enumeration (10,000 iterations)"
22
22
  bm.report("BitField each") { 10000.times { bf.each {|b| b } } }
23
23
  bm.report("BitArray each") { 10000.times { ba.each {|b| b } } }
24
24
 
25
- puts "---------------------------- To String"
25
+ puts "---------------------------- To String (10,000 iterations)"
26
26
  bm.report("BitField to_s") { 10000.times { bf.to_s } }
27
27
  bm.report("BitArray to_s") { 10000.times { ba.to_s } }
28
28
 
29
- puts "---------------------------- Total Set"
29
+ puts "---------------------------- Total Set (100,000 iterations)"
30
30
  bf = BitField.new(256)
31
31
  ba = BitArray.new(256)
32
- bm.report("BitField total_set (none)") { 10000.times { bf.total_set } }
33
- bm.report("BitArray total_set (none)") { 10000.times { ba.total_set } }
32
+ bm.report("BitField total_set (none)") { 100000.times { bf.total_set } }
33
+ bm.report("BitArray total_set (none)") { 100000.times { ba.total_set } }
34
34
  bf.each {|b| b = 1}
35
35
  ba.set_all_bits
36
- bm.report("BitField total_set (all)") { 10000.times { bf.total_set } }
37
- bm.report("BitArray total_set (all)") { 10000.times { ba.total_set } }
38
-
39
- puts "---------------------------- BitArray methods"
40
- bm.report("BitArray set_all_bits") { 10000.times { ba.set_all_bits} }
41
- bm.report("BitArray clear_all_bits") { 10000.times { ba.clear_all_bits } }
42
- bm.report("BitArray toggle_bit") { 10000.times { ba.toggle_bit(1) } }
43
- bm.report("BitArray toggle_all_bits") { 10000.times { ba.toggle_all_bits } }
44
- bm.report("BitArray clone") { 10000.times { ba.clone } }
36
+ bm.report("BitField total_set (all)") { 100000.times { bf.total_set } }
37
+ bm.report("BitArray total_set (all)") { 100000.times { ba.total_set } }
38
+
39
+ puts "---------------------------- BitArray methods (100,000 iterations)"
40
+ bm.report("BitArray set_all_bits") { 100000.times { ba.set_all_bits} }
41
+ bm.report("BitArray clear_all_bits") { 100000.times { ba.clear_all_bits } }
42
+ bm.report("BitArray toggle_bit") { 100000.times { ba.toggle_bit(1) } }
43
+ bm.report("BitArray toggle_all_bits") { 100000.times { ba.toggle_all_bits } }
44
+ bm.report("BitArray clone") { 100000.times { ba.clone } }
45
+ bm.report("BitArray slice (beg,len)") { 100000.times { ba[17, 230] } }
46
+ bm.report("BitArray slice (range)") { 100000.times { ba[17..247] } }
47
+ bm.report("BitArray +") { 100000.times { ba + ba } }
45
48
  }
46
49
 
data/test/test.rb CHANGED
@@ -92,10 +92,34 @@ class TestLibraryFileName < Test::Unit::TestCase
92
92
  end
93
93
 
94
94
  def test_total_set
95
- bf = BitArray.new(10)
96
- bf[1] = 1
97
- bf[5] = 1
98
- assert_equal 2, bf.total_set
95
+ ba = BitArray.new(10)
96
+ ba[1] = 1
97
+ ba[5] = 1
98
+ assert_equal 2, ba.total_set
99
+ end
100
+
101
+ def test_slice_beg_len
102
+ ba = BitArray.new(10)
103
+ ba[1] = 1
104
+ ba[5] = 1
105
+ assert_equal "10001", ba[1,5].to_s
106
+ assert_equal "10000", ba[-5,5].to_s
107
+ end
108
+
109
+ def test_slice_range
110
+ ba = BitArray.new(10)
111
+ ba[1] = 1
112
+ ba[5] = 1
113
+ assert_equal "10001", ba[1..5].to_s
114
+ assert_equal "10000", ba[-5..-1].to_s
115
+ end
116
+
117
+ def test_concatenation
118
+ ba1 = BitArray.new(5)
119
+ ba2 = BitArray.new(5)
120
+ ba2.set_all_bits
121
+ ba3 = ba1 + ba2
122
+ assert_equal "0000011111", ba3.to_s
99
123
  end
100
124
  end
101
125
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ingramj-bitarray
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - James E. Ingram
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-23 00:00:00 -07:00
12
+ date: 2009-05-24 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -43,7 +43,7 @@ rdoc_options:
43
43
  - --exclude
44
44
  - ext/Makefile
45
45
  - --title
46
- - " BitArray Documentation"
46
+ - BitArray Documentation
47
47
  require_paths:
48
48
  - ext
49
49
  required_ruby_version: !ruby/object:Gem::Requirement