NAME

Bitmask::Data - Handle bitmasks in an easy and flexible way

SYNOPSIS

# Create a simple bitmask class
packacke MyBitmask;
use base qw(Bitmask::Data);
__PACKAGE__->bitmask_length(18);
__PACKAGE__->bitmask_default(0b000000000000000011);
__PACKAGE__->init(
   'value1' => 0b000000000000000001,
   'value2' => 0b000000000000000010,
   'value2' => 0b000000000000000100,
   'value4' => 0b000000000000001000,
   'value5' => 0b000000000000010000,
   ...
);

## Somewhere else in your code
use MyBitmask;
my $bm = MyBitmask->new('value1','value3');
$bm->mask;

DESCRIPTION

This package helps you dealing with bitmasks. First you need to subclass Bitmask::Data and set the bitmask values and length. (If you are only working with a single bitmask in a simple application you might also initialize the bitmask directly in the Bitmask::Data module).

After the initialization you can create an arbitrary number of bitmask objects which can be accessed and manipulated with convenient methods.

METHODS

Class Methods

bitmask_length

Set/Get the length of the bitmask. Changing this value after the initialization is not recommended.

Default: 16

bitmask_default

Set/Get the default bitmask for empty Bitmask::Data objects.

Default: undef

bitmask_complex

Boolean value that enables/disables checks for composed bitmasks. If false init will only accept bitmask bit values that are powers of 2.

Default: 0

Complex bitmask also allow the creation of overlapping bitmask values:

packacke LocaleBitmask;
use base qw(Bitmask::Data);
__PACKAGE__->bitmask_length(8); # 8 bits
__PACKAGE__->bitmask_complex(1); # enable overlapping bitmasks
__PACKAGE__->init(
   AT      => 0b000_00001, # Austria
   CH      => 0b000_00010, # Switzerland
   DE      => 0b000_00100, # Germany
   FR      => 0b000_01000, # France
   IT      => 0b000_10000, # Italy
   
   de      => 0b001_00000, # German
   fr      => 0b010_00000, # French
   it      => 0b100_00000, # Italian
   
   de_AT   => 0b001_00001, # German / Austria
   de_CH   => 0b001_00010, # German / Switzerland
   de_DE   => 0b001_00100, # German / Germany
   fr_CH   => 0b010_00010, # French / Switzerland
   fr_FR   => 0b010_01000, # French / France
   it_CH   => 0b100_00010, # Italian / Switzerland    
   it_IT   => 0b100_10000, # Italian / Italy
);

# Somewhere else

LocaleBitmask->new('de')->hasany('de'); # true
LocaleBitmask->new('de')->hasany('de_DE'); # true ('de' matches)
LocaleBitmask->new('de')->hasall('de_DE'); # false
LocaleBitmask->new('de_DE','de_AT','de_CH')->hasexact('de','AT','DE','CH'); # true
LocaleBitmask->new('de_DE','de_AT','de_CH')->list # de,DE,de_DE,de_AT,AT,de_CH,CH

bitmask_lazyinit

Boolean value that enables/disables warnings for lazy initialization. ( Lazy initialization = call of init without bitmask bit values)

Default: 0

__PACKAGE__->bitmask_lazyinit(1);
__PACKAGE__->init(
   'value1', # will be 0b001
   'value2', # will be 0b010
   'value3'  # will be 0b100
);

bitmask_items

HASHREF of all bitmask items. Don't mess around here unless you know what you are doing.

init

CLASS->init(LIST of VALUES);

Initializes the bitmask class. You can supply a list of possible values. Optionally you can also specify the bits for the mask by adding bit values after the value.

CLASS->init(
    'value1' => 0b000001,
    'value2' => 0b000010,
    'value3' => 0b001000,
    'value4' => 0b010000,
);

With bitmask_lazyinit enabled you can also skip the bitmask bit values

CLASS->bitmask_lazyinit(1);
CLASS->init(
    'value1',
    'value2',
    'value3',
    'value4',
);

data2bit

CLASS->data2bit(VALUE);

Returns the corresponding bit for the given value.

bit2data

CLASS->bit2data(BIT);

Returns the corresponding value for the given bit.

bm2data

CLASS->bm2data(BITMASK);

Returns all the value for the given bitmask.

any2data

CLASS->any2data(124); # Bitmask
CLASS->any2data('de_DE'); # Value
CLASS->any2data(0b110001001); # Bitmask in bit notation
CLASS->any2data('0B110001001'); # Bitmask string
CLASS->any2data('0b111000001'); # Bitmask string

Turns a single value (bit, bitmask,value, bitmask string) into a value.

_parse_params

CLASS->_parse_params(LIST)

Helper method for parsing params passed to various methods.

Overloaded operators

Bitmask::Data uses overload by default.

  • Numeric context

    Returns bitmask integer value (see mask method)

  • Scalar context

    Returns bitmask string representation (see string method)

  • String comparison

  • Numeric comparison

  • -=

    Removes bitmask value (see remove method)

  • +=

    Adds bitmask value (see ass method)

  • ~, ^, &, |

    Performs the bit operations on the bitmask

Public Methods

new

my $bm = MyBitmask->new();
my $bm = MyBitmask->new('value1');
my $bm = MyBitmask->new('value2', 'value3');
my $bm = MyBitmask->new('0b00010000010000');
my $bm = MyBitmask->new(124);
my $bm = MyBitmask->new(0b00010000010000);
my $bm = MyBitmask->new(0x2);
my $bm = MyBitmask->new([32, 'value1', 0b00010000010000]);

Create a new bitmask object. You can supply almost any combination of bits, bitmasks and values, even mix different types.

  • LIST or ARRAYREF of values

  • LIST or ARRAYREF of strings representing bits or bitmasks (starting with '0b')

  • LIST or ARRAYREF of bitmasks

  • Any combination of the above

clone

$bm2 = $bm->clone();

Clones a bitmask object

set

$bm->set(ITEMS);

This method takes the same arguments as new. It resets the current bitmask and sets the supplied arguments.

Returns the object.

list

$bm->list()

In list context, this returns a list of the set values in scalar context, this returns an array reference to the list of values.

length

$bm->length()

Number of set bitmask values.

first

$bm->first()

Returns the first set value (the order of the values is determied by the sequence of the addition)

remove

$bm->remove(ITEMS);

This method takes the same arguments as new. The values supplied in the arguments will be unset.

Returns the object.

reset

$bm->reset();

Unsets all values, leaving an empty list.

Returns the object.

setall

$bm->setall();

Sets all values.

Returns the object.

add

$bm->add(ITEMS);

This method takes the same arguments as new. The specified values will be set.

Returns the object.

neg

$bm->neg();

Negative. Sets all unset values and vice versa.

Returns the object.

mask

$bm->mask();

Returns the integer representing the bitmask of all the set values.

string

$bm->string();

Retuns the string representing the bitmask.

sqlfilter_all

$bm->sqlfilter_all($field);

This method can be used for database searches in conjunction with SQL::Abstract an POSTGRESQL (SQL::Abstract is used by DBIx::Class for generating searches). The search will find all database rows with bitmask that have at least the given values set. (use the sql method for an exact match)

Example how to use sqlfilter with SQL::Abstract:

my($stmt, @bind) = $sql->select(
    'mytable', 
    \@fields,
    {
        $bm->sqlfilter_all('mytable.bitmaskfield'),
    }
);

Example how to use sqlfilter with DBIx::Class:

my $list = $resultset->search(
    { 
        $bm->sqlfilter_all('me.bitmaskfield'), 
    },
);

sqlfilter

Shortcut for sqlfilter_all

sqlfilter_any

$bm->sqlfilter_any($field);

Works like sqlfilter_all but checks for any bit matching

hasall

$bm->hasall(ITEMS);

This method takes the same arguments as new. Checks if all requestes items are set and returns true or false.

hasexact

$bm->hasexact(ITEMS);

This method takes the same arguments as new. Checks if the requestes items exactly match the set values.

hasany

$bm->hasany(ITEMS);

This method takes the same arguments as new. Checks if at least one set value matches the supplied value list and returns true or false

CAVEATS

Since Bitmask::Data is very liberal with input data you cannot use numbers as bitmask values.

Bitmask::Data adds a considerable processing overhead (especially when the bitmask_complex option is enabled) to bitmask manipulations. If you don't need the extra comfort please use the perl built in bit operators.

SUBCLASSING

Bitmask::Data was designed to be subclassed.

package MyBitmask;
use base qw(Bitmask::Data);
__PACKAGE__->bitmask_length(20); # Default length is 16
__PACKAGE__->init(
    'value1' => 0b000000000000000001,
    'value2' => 0x2,
    'value2' => 4,
    'value4', # lazy initlialization
    'value5', # lazy initlialization
);

WORKING WITH DATABASES

This module comes with support for POSTGRESQL databases (patches for other database vendors are welcome).

First you need to create the correct column types:

CREATE TABLE bitmaskexample ( 
    id integer DEFAULT nextval('pkey_seq'::regclass) NOT NULL,
    bitmask bit(14),
    otherfields character varying
);

The length of the bitmask field must match CLASS->bitmask_length.

This module provides three convenient methods to work with databases:

  • sqlfilter_all: Search for matching bitmasks

  • sqlfilter_any: Search for bitmasks with matching bits

  • string: Print the bitmask string as used by postgres database

If you are working with DBIx::Class you might also install de- and inflators for Bitmask objects:

__PACKAGE__->inflate_column('fieldname',{
    inflate => sub {
        my $value = shift;
        return MyBitmask->new($value);
    },
    deflate => sub {
        my $value = shift;
        undef $value 
            unless ref($value) && $value->isa('MyBitmask');
        $value //= MyBitmask->new();
        return $value->string;
    },
});

SUPPORT

Please report any bugs or feature requests to bug-bitmask-data@rt.cpan.org, or through the web interface at http://rt.cpan.org/Public/Bug/Report.html?Queue=Bitmask::Data. I will be notified and then you'll automatically be notified of the progress on your report as I make changes.

AUTHOR

Klaus Ita
koki [at] worstofall.com

Maroš Kollár
CPAN ID: MAROS
maros [at] k-1.com

L<http://www.revdev.at>

ACKNOWLEDGEMENTS

This module was originally written by Klaus Ita (Koki) for Revdev http://www.revdev.at, a nice litte software company I (Maros) run with Koki and Domm (http://search.cpan.org/~domm/).

COPYRIGHT

Bitmask::Data is Copyright (c) 2008 Klaus Ita, Maroš Kollár - http://www.revdev.at

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the LICENSE file included with this module.