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