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             #
3             # Array::IntSpan::Fields - IntSpan array using integer fields as indices
4             #
5             # Author: Dominique Dumont
6             ##########################################################################
7             # Copyright 2003 Dominique Dumont. All rights reserved.
8             #
9             # This file is distributed under the Artistic License. See
10             # http://www.ActiveState.com/corporate/artistic_license.htm or
11             # the license that comes with your perl distribution.
12             #
13             # For comments, questions, bugs or general interest, feel free to
14             # contact Dominique Dumont dominique.dumont@hp.com
15             ##########################################################################
16              
17             # $Author: domi $
18             # $Date: 2003/05/26 12:38:32 $
19             # $Name: $
20             # $Revision: 1.5 $
21              
22 1     1   51850 use strict;
  1         2  
  1         48  
23 1     1   6 use warnings;
  1         2  
  1         98  
24              
25             package Array::IntSpan::Fields;
26              
27             our $VERSION = sprintf "%d.%03d", q$Revision: 1.5 $ =~ /(\d+)\.(\d+)/;
28              
29 1     1   667 use Array::IntSpan;
  1         3  
  1         34  
30 1     1   6 use Carp ;
  1         2  
  1         116  
31              
32             use overload
33             # this emulate the usage of Intspan
34 0     0   0 '@{}' => sub { return shift->{range} ;} ,
35             # fallback on default behavior for all other operators
36 1     1   6 fallback => 1 ;
  1         1  
  1         15  
37              
38             sub new
39             {
40 4     4 1 1017 my $proto = shift ;
41 4   33     26 my $class = ref($proto) || $proto;
42 4         6 my $format = shift ;
43              
44 4 100       13 if (ref $format)
45             {
46             # in fact the user want a regular IntSpan
47 1         11 return Array::IntSpan->new($format,@_);
48             }
49              
50 3         7 my @temp = @_ ;
51 3         6 my $self = {};
52 3         11 bless $self, $class;
53 3         11 $self->set_format($format) ;
54              
55 3         6 foreach my $i (@temp)
56             {
57 7         21 $i->[0] = $self->field_to_int($i->[0]);
58 6         17 $i->[1] = $self->field_to_int($i->[1]);
59             }
60              
61 2         15 $self->{range}= Array::IntSpan->new(@temp) ;
62              
63 2         7 return $self;
64             }
65              
66             sub set_format
67             {
68 4     4 1 9 my ($self,$format) = @_ ;
69 4 50       26 croak "Unexpected format : $format" unless
70             $format =~ /^[\d\.]+$/ ;
71              
72 4         57 $self->{format} = $format ;
73              
74 4         20 my @array = split /\./, $self->{format} ;
75             # store nb of bit and corresponding bit mask
76 4         14 $self->{fields} = [map { [$_, (1<<$_) -1 ]} @array ] ;
  8         45  
77             }
78              
79             sub int_to_field
80             {
81 9     9 1 2917 my $self = shift ;
82 9         20 my @all_int = @_ ;
83 9         12 my @result ;
84              
85 9         17 foreach my $int (@all_int)
86             {
87 11         10 my @res ;
88 11         13 foreach my $f (reverse @{$self->{fields}})
  11         34  
89             {
90 29 100       64 unshift @res, ($f->[0] < 32 ? ($int & $f->[1]) : $int ) ;
91 29         48 $int >>= $f->[0] ;
92             }
93 11         50 push @result, join('.',@res) ;
94             }
95              
96 9 100       55 return wantarray ? @result : $result[0];
97             }
98              
99             sub field_to_int
100             {
101 30     30 1 3971 my $self = shift ;
102              
103 30         54 my @all_field = @_;
104 30         36 my @result ;
105              
106 30         43 foreach my $field (@all_field)
107             {
108 33         64 my $f = $self->{fields};
109 33         90 my @array = split /\./,$field ;
110              
111 33 50       90 croak "Expected ",scalar @$f,
112             " fields for format $self->{format}, got ",
113             scalar @array," in '$field'\n" unless @array == @$f ;
114              
115 33         38 my $res = 0 ;
116              
117 33         40 my $i =0 ;
118              
119 33         63 while ($i <= $#array)
120             {
121 79         151 my $shift = $f->[$i][0] ;
122 79 100 100     778 croak "Field value $array[$i] too great. ",
123             "Max is $f->[$i][1] (bit width is $shift)"
124             if $shift<32 and $array[$i] >> $shift ;
125              
126 76         200 $res = ($res << $shift) + $array[$i++] ;
127             }
128             #print "field_to_int: changed $field to $res for format $self->{format}\n";
129 30         92 push @result, $res ;
130             }
131              
132 27 100       148 return wantarray ? @result : $result[0];
133             }
134              
135             sub get_range
136             {
137 1     1 0 5 my ($self,$s_field,$e_field) = splice @_,0,3 ;
138 1         4 my ($s, $e) = $self->field_to_int($s_field,$e_field) ;
139 1         7 my @newcb = $self->adapt_range_in_cb(@_) ;
140              
141 1         9 my $got = $self->{range}->get_range($s,$e,@newcb) ;
142              
143 1         5 my $ret = bless {range => $got }, ref($self) ;
144 1         6 $ret->set_format($self->{format}) ;
145 1         4 return $ret ;
146             }
147              
148             sub lookup
149             {
150 5     5 0 603 my $self = shift;
151 5         13 my @keys = $self->field_to_int(@_);
152 5         24 $self->{range}->lookup(@keys) ;
153             }
154              
155             sub clear
156             {
157 0     0 0 0 my $self = shift;
158 0         0 @{$self->{range}} = () ;
  0         0  
159             }
160              
161             sub consolidate
162             {
163 0     0 0 0 my ($self,$s_field,$e_field) = splice @_,0,3 ;
164 0 0 0     0 my ($s, $e) = $self->field_to_int($s_field,$e_field)
165             if defined $s_field and defined $e_field;
166 0 0       0 my @newcb = $self->adapt_range_in_cb(@_) if @_;
167              
168 0         0 return $self->{range}->consolidate($s,$e,@newcb) ;
169             }
170              
171              
172             foreach my $method (qw/set_range set_consolidate_range/)
173             {
174 1     1   1171 no strict 'refs' ;
  1         16  
  1         377  
175             *$method = sub
176             {
177 2     2   588 my ($self,$s_field,$e_field,$value) = splice @_,0,4 ;
178 2         7 my ($s, $e) = $self->field_to_int($s_field,$e_field) ;
179 2         10 my @newcb = $self->adapt_range_in_cb(@_) ;
180              
181 2         14 return $self->{range}->$method ($s, $e, $value, @newcb);
182             };
183             }
184              
185             sub adapt_range_in_cb
186             {
187 3     3 0 5 my $self = shift;
188              
189             # the callbacks will be called with ($start, $end,$payload) or
190             # ($start,$end)
191 3         7 my @callbacks = @_ ;
192              
193 1         2 return map
194             {
195 3         7 my $old_cb = $_; # required for closure to work
196             defined $old_cb ?
197             sub
198             {
199 1     1   2 my ($s_int,$e_int,$value) = @_ ;
200 1         3 my ($s,$e) = $self->int_to_field($s_int,$e_int) ;
201 1         4 $old_cb->($s,$e,$value);
202             }
203 1 50       9 : undef ;
204             } @callbacks ;
205             }
206              
207             sub get_element
208             {
209 1     1 0 834 my ($self,$idx) = @_;
210 1   50     7 my $elt = $self->{range}[$idx] || return () ;
211 1         4 my ($s_int,$e_int,$value) = @$elt ;
212 1         4 my ($s,$e) = $self->int_to_field($s_int,$e_int) ;
213              
214 1         9 return ($s,$e, $value) ;
215             }
216              
217             1;
218              
219             __END__
220              
221             =head1 NAME
222              
223             Array::IntSpan::Fields - IntSpan array using integer fields as indices
224              
225             =head1 SYNOPSIS
226              
227             use Array::IntSpan::Fields;
228              
229             my $foo = Array::IntSpan::Fields
230             ->new( '1.2.4',
231             ['0.0.1','0.1.0','ab'],
232             ['1.0.0','1.0.3','cd']);
233              
234             print "Address 0.0.15 has ".$foo->lookup("0.0.15").".\n";
235              
236             $foo->set_range('1.0.4','1.1.0','ef') ;
237              
238             =head1 DESCRIPTION
239              
240             C<Array::IntSpan::Fields> brings the advantages of C<Array::IntSpan>
241             to indices made of integer fields like an IP address and an ANSI SS7 point code.
242              
243             The number of integer and their maximum value is defined when calling
244             the constructor (or the C<set_format> method). The example in the
245             synopsis defines an indice with 3 fields where their maximum values
246             are 1,3,15 (or 0x1,0x3,0xf).
247              
248             This module converts the fields into integer before storing them into
249             the L<Array::IntSpan> module.
250              
251             =head1 CONSTRUCTOR
252              
253             =head2 new (...)
254              
255             The first parameter defines the size of the integer of the fields, in
256             number of bits. For an IP address, the field definition would be
257             C<8,8,8,8>.
258              
259             =head1 METHODS
260              
261             All methods of L<Array::IntSpan> are available.
262              
263             =head2 set_format( field_description )
264              
265             Set another field description. Beware: no conversion or checking is
266             done. When changing the format, old indices may become illegal.
267              
268             =head2 int_to_field ( integer )
269              
270             Returns the field representation of the integer.
271              
272             =head2 field_to_int ( field )
273              
274             Returns the integer value of the field. May craok if the fields values
275             are too great with respect to the filed description.
276              
277             =head1 AUTHOR
278              
279             Dominique Dumont, dominique.dumont@hp.com
280              
281             Copyright (c) 2003 Dominique Dumont. All rights reserved.
282             This program is free software; you can redistribute it and/or
283             modify it under the same terms as Perl itself.
284              
285             =cut
286