File Coverage

blib/lib/Array/IntSpan/Fields.pm
Criterion Covered Total %
statement 91 99 91.9
branch 13 20 65.0
condition 5 11 45.4
subroutine 16 19 84.2
pod 4 10 40.0
total 129 159 81.1


line stmt bran cond sub pod time code
1             #
2             # This file is part of Array-IntSpan
3             #
4             # This software is Copyright (c) 2014 by Dominique Dumont.
5             #
6             # This is free software, licensed under:
7             #
8             # The Artistic License 2.0 (GPL Compatible)
9             #
10             ##########################################################################
11             #
12             # Array::IntSpan::Fields - IntSpan array using integer fields as indices
13             #
14             # Author: Dominique Dumont
15             ##########################################################################
16             # Copyright 2003 Dominique Dumont. All rights reserved.
17             #
18             # This module is distributed under the Artistic 2.0 License. See
19             # https://www.perlfoundation.org/artistic-license-20.html
20             #
21             # For comments, questions, bugs or general interest, feel free to
22             # contact Dominique Dumont ddumont@cpan.org
23             ##########################################################################
24              
25 1     1   67550 use strict;
  1         11  
  1         24  
26 1     1   4 use warnings;
  1         1  
  1         42  
27              
28             package Array::IntSpan::Fields;
29             $Array::IntSpan::Fields::VERSION = '2.004';
30 1     1   409 use Array::IntSpan;
  1         2  
  1         29  
31 1     1   5 use Carp ;
  1         2  
  1         75  
32              
33             use overload
34             # this emulate the usage of Intspan
35 0     0   0 '@{}' => sub { return shift->{range} ;} ,
36             # fallback on default behavior for all other operators
37 1     1   1056 fallback => 1 ;
  1         818  
  1         6  
38              
39             sub new
40             {
41 4     4 1 1196 my $proto = shift ;
42 4   33     20 my $class = ref($proto) || $proto;
43 4         7 my $format = shift ;
44              
45 4 100       9 if (ref $format)
46             {
47             # in fact the user want a regular IntSpan
48 1         6 return Array::IntSpan->new($format,@_);
49             }
50              
51 3         17 my @temp = @_ ;
52 3         5 my $self = {};
53 3         6 bless $self, $class;
54 3         9 $self->set_format($format) ;
55              
56 3         6 foreach my $i (@temp)
57             {
58 7         16 $i->[0] = $self->field_to_int($i->[0]);
59 6         13 $i->[1] = $self->field_to_int($i->[1]);
60             }
61              
62 2         10 $self->{range}= Array::IntSpan->new(@temp) ;
63              
64 2         6 return $self;
65             }
66              
67             sub set_format
68             {
69 4     4 1 9 my ($self,$format) = @_ ;
70 4 50       19 croak "Unexpected format : $format" unless
71             $format =~ /^[\d\.]+$/ ;
72              
73 4         29 $self->{format} = $format ;
74              
75 4         13 my @array = split /\./, $self->{format} ;
76             # store nb of bit and corresponding bit mask
77 4         7 $self->{fields} = [map { [$_, (1<<$_) -1 ]} @array ] ;
  8         29  
78             }
79              
80             sub int_to_field
81             {
82 9     9 1 1521 my $self = shift ;
83 9         18 my @all_int = @_ ;
84 9         10 my @result ;
85              
86 9         14 foreach my $int (@all_int)
87             {
88 11         13 my @res ;
89 11         12 foreach my $f (reverse @{$self->{fields}})
  11         23  
90             {
91 29 100       53 unshift @res, ($f->[0] < 32 ? ($int & $f->[1]) : $int ) ;
92 29         38 $int >>= $f->[0] ;
93             }
94 11         35 push @result, join('.',@res) ;
95             }
96              
97 9 100       41 return wantarray ? @result : $result[0];
98             }
99              
100             sub field_to_int
101             {
102 30     30 1 3447 my $self = shift ;
103              
104 30         50 my @all_field = @_;
105 30         36 my @result ;
106              
107 30         37 foreach my $field (@all_field)
108             {
109 33         57 my $f = $self->{fields};
110 33         71 my @array = split /\./,$field ;
111              
112 33 50       64 croak "Expected ",scalar @$f,
113             " fields for format $self->{format}, got ",
114             scalar @array," in '$field'\n" unless @array == @$f ;
115              
116 33         42 my $res = 0 ;
117              
118 33         35 my $i =0 ;
119              
120 33         55 while ($i <= $#array)
121             {
122 79         111 my $shift = $f->[$i][0] ;
123 79 100 100     541 croak "Field value $array[$i] too great. ",
124             "Max is $f->[$i][1] (bit width is $shift)"
125             if $shift<32 and $array[$i] >> $shift ;
126              
127 76         143 $res = ($res << $shift) + $array[$i++] ;
128             }
129             #print "field_to_int: changed $field to $res for format $self->{format}\n";
130 30         60 push @result, $res ;
131             }
132              
133 27 100       57 return wantarray ? @result : $result[0];
134             }
135              
136             sub get_range
137             {
138 1     1 0 4 my ($self,$s_field,$e_field) = splice @_,0,3 ;
139 1         4 my ($s, $e) = $self->field_to_int($s_field,$e_field) ;
140 1         3 my @newcb = $self->adapt_range_in_cb(@_) ;
141              
142 1         5 my $got = $self->{range}->get_range($s,$e,@newcb) ;
143              
144 1         4 my $ret = bless {range => $got }, ref($self) ;
145 1         4 $ret->set_format($self->{format}) ;
146 1         2 return $ret ;
147             }
148              
149             sub lookup
150             {
151 5     5 0 346 my $self = shift;
152 5         10 my @keys = $self->field_to_int(@_);
153 5         17 $self->{range}->lookup(@keys) ;
154             }
155              
156             sub clear
157             {
158 0     0 0 0 my $self = shift;
159 0         0 @{$self->{range}} = () ;
  0         0  
160             }
161              
162             sub consolidate
163             {
164 0     0 0 0 my ($self,$s_field,$e_field) = splice @_,0,3 ;
165 0 0 0     0 my ($s, $e) = $self->field_to_int($s_field,$e_field)
166             if defined $s_field and defined $e_field;
167 0 0       0 my @newcb = $self->adapt_range_in_cb(@_) if @_;
168              
169 0         0 return $self->{range}->consolidate($s,$e,@newcb) ;
170             }
171              
172              
173             foreach my $method (qw/set_range set_consolidate_range/)
174             {
175 1     1   876 no strict 'refs' ;
  1         2  
  1         342  
176             *$method = sub
177             {
178 2     2   482 my ($self,$s_field,$e_field,$value) = splice @_,0,4 ;
179 2         5 my ($s, $e) = $self->field_to_int($s_field,$e_field) ;
180 2         5 my @newcb = $self->adapt_range_in_cb(@_) ;
181              
182 2         11 return $self->{range}->$method ($s, $e, $value, @newcb);
183             };
184             }
185              
186             sub adapt_range_in_cb
187             {
188 3     3 0 6 my $self = shift;
189              
190             # the callbacks will be called with ($start, $end,$payload) or
191             # ($start,$end)
192 3         4 my @callbacks = @_ ;
193              
194             return map
195             {
196 3         8 my $old_cb = $_; # required for closure to work
  1         3  
197             defined $old_cb ?
198             sub
199             {
200 1     1   14 my ($s_int,$e_int,$value) = @_ ;
201 1         6 my ($s,$e) = $self->int_to_field($s_int,$e_int) ;
202 1         9 $old_cb->($s,$e,$value);
203             }
204 1 50       7 : undef ;
205             } @callbacks ;
206             }
207              
208             sub get_element
209             {
210 1     1 0 446 my ($self,$idx) = @_;
211 1   50     5 my $elt = $self->{range}[$idx] || return () ;
212 1         3 my ($s_int,$e_int,$value) = @$elt ;
213 1         4 my ($s,$e) = $self->int_to_field($s_int,$e_int) ;
214              
215 1         8 return ($s,$e, $value) ;
216             }
217              
218             1;
219              
220             __END__