File Coverage

blib/lib/Acme/Bitfield.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


line stmt bran cond sub pod time code
1 1     1   271801 use v5.42;
  1         5  
2 1     1   10 use feature 'class';
  1         2  
  1         211  
3 1     1   28 no warnings 'experimental::class';
  1         3  
  1         2240  
4             #
5             class Acme::Bitfield v1.1.0 {
6             field $size : reader : param;
7             field $data : reader : param = "\0" x int( ( $size + 7 ) / 8 );
8             ADJUST {
9             $self->_clean;
10             }
11              
12             method set_data ($val) {
13             $data = $val;
14             $self->_clean; # We can't use the :writer because we must call this
15             }
16              
17             # Internal helper to map BitTorrent bit index to vec index
18             # BT: bit 0 is 0x80, bit 7 is 0x01
19             # vec: bit 0 is 0x01, bit 7 is 0x80
20 36     36   81 sub _map ($index) { ( $index & ~7 ) | ( 7 - ( $index & 7 ) ) }
  36         55  
  36         53  
  36         214  
21              
22             method get ($index) {
23             return 0 if $index < 0 || $index >= $size;
24             vec $data, _map($index), 1;
25             }
26              
27             method set ($index) {
28             return if $index < 0 || $index >= $size;
29             vec( $data, _map($index), 1 ) = 1;
30             }
31              
32             method clear ($index) {
33             return if $index < 0 || $index >= $size;
34             vec( $data, _map($index), 1 ) = 0;
35             }
36              
37             method count () {
38             return unpack( '%32b*', $data );
39             }
40              
41             method is_full () {
42             return $self->count == $size;
43             }
44              
45             method is_empty () {
46             return $data =~ tr/\0//c ? 0 : 1;
47             }
48              
49             method union ($other) {
50             my $new = __CLASS__->new( size => $size );
51             $new->set_data( $data|.$other->data );
52             return $new;
53             }
54              
55             method intersection ($other) {
56             my $new = __CLASS__->new( size => $size );
57             $new->set_data( $data&.$other->data );
58             return $new;
59             }
60              
61             method difference ($other) {
62              
63             # Bits set in self but NOT in other
64             my $new = __CLASS__->new( size => $size );
65             $new->set_data( $data&.~.$other->data );
66             return $new;
67             }
68              
69             method _clean () {
70              
71             # internal method to automatically handle data truncation, padding, and bit masking
72             my $expected_len = int( ( $size + 7 ) / 8 );
73             if ( length($data) > $expected_len ) {
74             substr( $data, $expected_len ) = "";
75             }
76             elsif ( length($data) < $expected_len ) {
77             $data .= "\0" x ( $expected_len - length($data) );
78             }
79             my $bits_in_last_byte = $size % 8;
80             if ( $bits_in_last_byte != 0 && $expected_len > 0 ) {
81             my $mask = ( 0xFF << ( 8 - $bits_in_last_byte ) ) & 0xFF;
82             substr( $data, -1, 1 ) &.= chr($mask);
83             }
84             }
85              
86             method fill () {
87             $data = "\xFF" x length($data);
88             $self->_clean;
89             }
90              
91             method find_missing () {
92             my $index = index( unpack( 'B*', $data ), '0' );
93             return ( $index >= 0 && $index < $size ) ? $index : ();
94             }
95              
96             method inverse () {
97             my $inverted = __CLASS__->new( size => $size );
98             $inverted->set_data( ~.$data );
99             return $inverted;
100             }
101             };
102             #
103             1;