File Coverage

blib/lib/FTN/Bit_flags.pm
Criterion Covered Total %
statement 88 94 93.6
branch 47 78 60.2
condition 6 12 50.0
subroutine 12 12 100.0
pod 8 8 100.0
total 161 204 78.9


line stmt bran cond sub pod time code
1 2     2   66664 use strict;
  2         3  
  2         57  
2 2     2   8 use warnings;
  2         3  
  2         53  
3 2     2   1258 use utf8;
  2         23  
  2         9  
4              
5             package FTN::Bit_flags;
6             $FTN::Bit_flags::VERSION = '20160324';
7              
8 2     2   1196 use Log::Log4perl ();
  2         49779  
  2         2562  
9              
10             =head1 NAME
11              
12             FTN::Bit_flags - Object-oriented module for working with bit flags.
13              
14             =head1 VERSION
15              
16             version 20160324
17              
18             =head1 SYNOPSIS
19              
20             use Log::Log4perl ();
21             use FTN::Bit_flags ();
22              
23             Log::Log4perl -> easy_init( $Log::Log4perl::INFO );
24              
25             # let's work with message attributes
26             my $attribute = FTN::Bit_flags -> new( { abbr => 'PVT',
27             name => 'PRIVATE',
28             },
29             { abbr => 'CRA',
30             name => 'CRASH',
31             },
32             { abbr => 'RCV',
33             name => 'READ',
34             },
35             { abbr => 'SNT',
36             name => 'SENT',
37             },
38             { abbr => 'FIL',
39             name => 'FILEATT',
40             },
41             { name => 'TRANSIT',
42             },
43             { name => 'ORPHAN',
44             },
45             { abbr => 'K/S',
46             name => 'KILL',
47             },
48             { name => 'LOCAL',
49             },
50             { abbr => 'HLD',
51             name => 'HOLD',
52             },
53             { abbr => 'XX2',
54             },
55             { abbr => 'FRQ',
56             abbr => 'FREQ',
57             },
58             { abbr => 'RRQ',
59             name => 'Receipt REQ',
60             },
61             { abbr => 'CPT',
62             },
63             { abbr => 'ARQ',
64             },
65             { abbr => 'URQ',
66             },
67             );
68              
69             $attribute -> set_from_number( get_attribute_from_message() );
70              
71             print join ', ', $attribute -> list_of_set;
72              
73             print 'this is a private message'
74             if $attribute -> is_set( 'PVT' );
75              
76             # make sure it is local and its flavour is crash
77             $attribute -> set( 'LOCAL', 'CRASH' );
78              
79             # though we don't need it to be killed after sent
80             $attribute -> clear( 'K/S' );
81              
82             update_message_attribute_field( $attribute -> as_number );
83              
84             $attribute -> set_from_number( get_attribute_from_another_message() );
85              
86             # work with new attribute value the same way as above
87              
88             =head1 DESCRIPTION
89              
90             FTN::Bit_flags module is for working with bit flags commonly used in FTN messages.
91              
92             =head1 OBJECT CREATION
93              
94             =head2 new
95              
96             my $bit_flags = FTN::Bit_flags -> new( { abbr => 'flag 1' },
97             { name => 'second lowest bit' },
98             { abbr => 'flag 2',
99             name => 'flag numeric mask is 4'
100             }
101             );
102              
103             Parameters are hash references representing bit in order from low to high. At least one parameter is required.
104             Each hash reference should have 'abbr' and/or 'name' fields. Dies in case of error.
105              
106             =cut
107              
108             sub new {
109 2     2 1 1966 my $logger = Log::Log4perl -> get_logger( __PACKAGE__ );
110              
111 2 50       197 ref( my $class = shift ) and $logger -> logcroak( "I'm only a class method!" );
112              
113 2         7 my %self = ( abbr => {},
114             name => {},
115             list => [],
116             value => 0,
117             );
118              
119 2 50       5 $logger -> logdie( 'attribute list was not passed to constructor' )
120             unless @_;
121              
122 2         5 for my $i ( 0 .. $#_ ) {
123 19 50 33     63 $logger -> logdie( sprintf 'attribute # %d is not a hashref',
124             $i,
125             )
126             unless defined $_[ $i ]
127             && ref $_[ $i ] eq 'HASH';
128              
129             $logger -> logdie( sprintf 'attribute # %d misses abbr and/or name',
130             $i,
131             )
132             unless exists $_[ $i ]{abbr}
133 19 50 66     31 || exists $_[ $i ]{name};
134              
135 19         11 my @new_to_list;
136              
137 19         28 for my $f ( [ abbr => 0 ],
138             [ name => 1 ],
139             ) {
140 38 100       50 next unless exists $_[ $i ]{ $f -> [ 0 ] };
141              
142 28         20 my $val = $_[ $i ]{ $f -> [ 0 ] };
143 28 50       29 $logger -> logdie( sprintf 'attribute # %d has undefined %s',
144             $i,
145             $f -> [ 0 ],
146             )
147             unless defined $val;
148              
149             $logger -> logdie( sprintf 'attribute with %s %s is already defined',
150             $f -> [ 0 ],
151             $val,
152             )
153 28 50       37 if exists $self{ $f -> [ 0 ] }{ $val };
154              
155 28         24 $new_to_list[ $f -> [ 1 ] ] = $val;
156 28         41 $self{ $f -> [ 0 ] }{ $val } = 1 << $i;
157             }
158              
159 19 50       29 if ( exists $_[ $i ]{descr} ) {
160 0         0 my $descr = $_[ $i ]{descr};
161 0 0       0 $logger -> logdie( sprintf 'attribute # %d has undefined description',
162             $i,
163             )
164             unless defined $descr;
165              
166 0         0 $new_to_list[ 2 ] = $descr;
167             }
168              
169 19         10 push @{ $self{list} }, \ @new_to_list;
  19         26  
170             }
171              
172 2         5 bless \ %self, $class;
173             }
174              
175             =head2 set_from_number
176              
177             After object describing all possible fields is created we can use it to work with already defined value:
178              
179             $bit_flags -> set_from_number( 3 );
180              
181             =cut
182              
183             sub set_from_number {
184 2     2 1 13 my $logger = Log::Log4perl -> get_logger( __PACKAGE__ );
185              
186 2 50       31 ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" );
187              
188 2 50       5 $logger -> logdie( 'no value was passed to set from number' )
189             unless @_;
190              
191 2 0 33     14 $logger -> logdie( sprintf 'incorrect numeric value: %s',
    50          
192             defined $_[ 0 ] ? $_[ 0 ] : 'undef',
193             )
194             unless defined $_[ 0 ]
195             && $_[ 0 ] =~ m/^\d+$/;
196              
197             # let's check that it is not bigger than we have attributes
198             $logger -> logdie( sprintf 'numeric value is too big %d',
199             $_[ 0 ],
200             )
201 2 50       2 if $_[ 0 ] >> @{ $self -> {list} };
  2         7  
202              
203 2         4 $self -> {value} = $_[ 0 ];
204              
205 2         3 $self;
206             }
207              
208             =head2 clear_all
209              
210             We can clear all bitfields (setting numeric value to 0):
211              
212             $bit_flags -> clear_all;
213              
214             =cut
215              
216             sub clear_all {
217 1     1 1 6 my $logger = Log::Log4perl -> get_logger( __PACKAGE__ );
218              
219 1 50       13 ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" );
220              
221 1         2 $self -> {value} = 0;
222              
223 1         2 $self;
224             }
225              
226             =head2 set
227              
228             To set one (or more) fields:
229              
230             $bit_flags -> set( 'second lowest bit', 'flag 2' );
231              
232             If you have equal 'abbr' for one field and 'name' for another field, then 'abbr' has higher priority here.
233              
234             =cut
235              
236             sub set {
237 2     2 1 7 my $logger = Log::Log4perl -> get_logger( __PACKAGE__ );
238              
239 2 50       28 ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" );
240              
241 2 50       4 $logger -> logdie( 'no attribute abbr/name was passed to set' )
242             unless @_;
243              
244 2         4 for my $i ( 0 .. $#_ ) {
245 4         4 my $t = $_[ $i ];
246 4 50       7 $logger -> logdie( sprintf 'passed attribute abbr/name to be set with index %d is undefined',
247             $i,
248             )
249             unless defined $t;
250              
251 4 100       9 if ( exists $self -> {abbr}{ $t } ) {
    50          
252 1         2 $self -> {value} |= $self -> {abbr}{ $t };
253             } elsif ( exists $self -> {name}{ $t } ) {
254 3         5 $self -> {value} |= $self -> {name}{ $t };
255             } else {
256 0         0 $logger -> logdie( sprintf 'unknown abbr/name %s was passed to set',
257             $t,
258             );
259             }
260             }
261              
262 2         3 $self;
263             }
264              
265             =head2 clear
266              
267             To clear one (or more) fields:
268              
269             $bit_flags -> clear( 'second lowest bit' );
270              
271             If you have equal 'abbr' for one field and 'name' for another field, then 'abbr' has higher priority here.
272              
273             =cut
274              
275             sub clear {
276 2     2 1 6 my $logger = Log::Log4perl -> get_logger( __PACKAGE__ );
277              
278 2 50       24 ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" );
279              
280 2 50       5 $logger -> logdie( 'no attribute abbr/name was passed to clear' )
281             unless @_;
282              
283 2         4 for my $i ( 0 .. $#_ ) {
284 2         2 my $t = $_[ $i ];
285 2 50       9 $logger -> logdie( sprintf 'passed attribute abbr/name to be cleared with index %d is undefined',
286             $i,
287             )
288             unless defined $t;
289              
290 2 100       5 if ( exists $self -> {abbr}{ $t } ) {
    50          
291 1         5 $self -> {value} &= ~ $self -> {abbr}{ $t };
292             } elsif ( exists $self -> {name}{ $t } ) {
293 1         2 $self -> {value} &= ~ $self -> {name}{ $t };
294             } else {
295 0         0 $logger -> logdie( sprintf 'unknown abbr/name %s was passed to clear',
296             $t,
297             );
298             }
299             }
300              
301 2         3 $self;
302             }
303              
304             =head2 is_set
305              
306             To check if some field is set:
307              
308             print 'it is set'
309             if $bit_flags -> is_set( 'second lowest bit' );
310              
311             If you have equal 'abbr' for one field and 'name' for another field, then 'abbr' has higher priority here.
312              
313             =cut
314              
315             sub is_set {
316 2     2 1 8 my $logger = Log::Log4perl -> get_logger( __PACKAGE__ );
317              
318 2 50       28 ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" );
319              
320 2 50       4 $logger -> logdie( 'no attribute abbr/name was passed to check if it is set' )
321             unless @_;
322              
323 2         3 my $t = shift;
324              
325 2 50       16 $logger -> logdie( 'passed attribute abbr/name to check if it is set is undefined' )
326             unless defined $t;
327              
328 2 100       7 if ( exists $self -> {abbr}{ $t } ) {
    50          
329 1         5 $self -> {value} & $self -> {abbr}{ $t };
330             } elsif ( exists $self -> {name}{ $t } ) {
331 1         3 $self -> {value} & $self -> {name}{ $t };
332             } else {
333 0         0 $logger -> logdie( sprintf 'unknown abbr/name %s was passed to check if it is set',
334             $t,
335             );
336             }
337             }
338              
339             =head2 as_number
340              
341             To get numeric value after you set or cleared some flags:
342              
343             print $bit_flags -> as_number;
344              
345             =cut
346              
347             sub as_number {
348 2     2 1 4 my $logger = Log::Log4perl -> get_logger( __PACKAGE__ );
349              
350 2 50       30 ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" );
351              
352 2         7 $self -> {value};
353             }
354              
355             =head2 list_of_set
356              
357             To get list of set flags:
358              
359             print join ' ', $bit_flags -> list_of_set;
360              
361             By default it tries to return 'abbr' field value for each set bit and if there is none, then return 'name' field value. If 'name' field is preferable, pass optional parameter 'name'.
362              
363             print join ' ', $bit_flags -> list_of_set( 'name' );
364              
365             =cut
366              
367             sub list_of_set {
368 3     3 1 11 my $logger = Log::Log4perl -> get_logger( __PACKAGE__ );
369              
370 3 50       46 ref( my $self = shift ) or $logger -> logcroak( "I'm only an object method!" );
371              
372 3         2 my @res;
373              
374 3   66     17 my $prefer_abbr = ! ( @_ && defined $_[ 0 ] && $_[ 0 ] eq 'name' );
375              
376 3         3 for my $b ( @{ $self -> {list} } ) {
  3         6  
377 22         16 my $a;
378             my $v;
379 22 100       18 if ( $prefer_abbr ) {
380 19 100       22 $a = defined $b -> [ 0 ] ? $b -> [ 0 ] : $b -> [ 1 ];
381             $v = defined $b -> [ 0 ] ?
382             $self -> {abbr}{ $b -> [ 0 ] }
383 19 100       22 : $self -> {name}{ $b -> [ 1 ] };
384             } else {
385 3 100       6 $a = defined $b -> [ 1 ] ? $b -> [ 1 ] : $b -> [ 0 ];
386             $v = defined $b -> [ 1 ] ?
387             $self -> {name}{ $b -> [ 1 ] }
388 3 100       7 : $self -> {abbr}{ $b -> [ 0 ] };
389             }
390              
391             push @res, $a
392 22 100       35 if $self -> {value} & $v;
393             }
394              
395             wantarray ?
396             @res
397 3 50       17 : \ @res;
398             }
399              
400             1;
401              
402             =head1 AUTHOR
403              
404             Valery Kalesnik, C<< >>
405              
406             =head1 BUGS
407              
408             Please report any bugs or feature requests to C, or through
409             the web interface at L.
410             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
411              
412             =head1 SUPPORT
413              
414             You can find documentation for this module with the perldoc command.
415              
416             perldoc FTN::Bit_flags
417              
418             =cut
419              
420             __END__