Acme-Bitfield
view release on metacpan or search on metacpan
lib/Acme/Bitfield.pm view on Meta::CPAN
# Bits set in self but NOT in other
my $new = __CLASS__->new( size => $size );
$new->set_data( $data&.~.$other->data );
return $new;
}
method _clean () {
# internal method to automatically handle data truncation, padding, and bit masking
my $expected_len = int( ( $size + 7 ) / 8 );
if ( length($data) > $expected_len ) {
substr( $data, $expected_len ) = "";
}
elsif ( length($data) < $expected_len ) {
$data .= "\0" x ( $expected_len - length($data) );
}
my $bits_in_last_byte = $size % 8;
if ( $bits_in_last_byte != 0 && $expected_len > 0 ) {
my $mask = ( 0xFF << ( 8 - $bits_in_last_byte ) ) & 0xFF;
substr( $data, -1, 1 ) &.= chr($mask);
}
}
method fill () {
$data = "\xFF" x length($data);
$self->_clean;
}
method find_missing () {
my $index = index( unpack( 'B*', $data ), '0' );
return ( $index >= 0 && $index < $size ) ? $index : ();
}
method inverse () {
my $inverted = __CLASS__->new( size => $size );
subtest 'Edge Cases' => sub {
subtest 'Zero Size' => sub {
my $bf = Acme::Bitfield->new( size => 0 );
is( $bf->data, '', 'Data is empty string' );
is( $bf->count, 0, 'Count is 0' );
ok( $bf->is_full, 'Zero size is technically full' );
};
subtest 'Mismatched Data Length' => sub {
my $bf = Acme::Bitfield->new( size => 8 );
$bf->set_data("\xFF\xFF\xFF");
is( length( $bf->data ), 1, 'Data truncated to 1 byte' );
is( $bf->count, 8, 'Count is 8' );
$bf->set_data("");
is( length( $bf->data ), 1, 'Data padded to 1 byte' );
is( ord( $bf->data ), 0, 'Padded with zeros' );
};
subtest 'Last Byte Masking' => sub {
my $bf = Acme::Bitfield->new( size => 10 );
# 10 bits = 2 bytes. Last byte should only have 2 bits.
$bf->set_data("\xFF\xFF");
is( ord( substr( $bf->data, 1, 1 ) ), 0xC0, 'Last byte masked to 0xC0' );
is( $bf->count, 10, 'Count is 10' );
};
( run in 2.534 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )