File Coverage

blib/lib/Net/CIDR/Set/IPv4.pm
Criterion Covered Total %
statement 54 54 100.0
branch 30 32 93.7
condition 12 14 85.7
subroutine 14 14 100.0
pod 0 4 0.0
total 110 118 93.2


line stmt bran cond sub pod time code
1             package Net::CIDR::Set::IPv4;
2              
3 8     8   49 use warnings;
  8         18  
  8         362  
4 8     8   44 use strict;
  8         9  
  8         155  
5 8     8   54 use Carp;
  8         11  
  8         403  
6              
7 8     8   3119 use namespace::autoclean;
  8         133557  
  8         57  
8              
9             # ABSTRACT: Encode / decode IPv4 addresses
10              
11             our $VERSION = '0.20';
12              
13 91     91 0 207 sub new { bless \my $x, shift }
14              
15             sub _pack {
16 242     242   578 my @nums = split /[.]/, shift, -1;
17 242 100       4442 return unless @nums == 4;
18 205         272 for ( @nums ) {
19 817 100 66     3527 return unless /^\d{1,3}$/ and !/^0\d{1,2}$/ and $_ < 256;
      66        
20             }
21 204         625 return pack "CC*", 0, @nums;
22             }
23              
24 75     75   390 sub _unpack { join ".", unpack "xC*", shift }
25              
26             sub _width2bits {
27 108     108   153 my ( $width, $size ) = @_;
28 108         412 return pack 'B*',
29             ( '1' x ( $width + 8 ) ) . ( '0' x ( $size - $width ) );
30             }
31              
32             sub _ip2bits {
33 2 50   2   5 my $ip = shift or return;
34 2         6 vec( $ip, 0, 8 ) = 255;
35 2         5 my $bits = unpack 'B*', $ip;
36 2 100       158 return unless $bits =~ /^1*0*$/; # Valid mask?
37 1         3 return $ip;
38             }
39              
40             sub _is_cidr {
41 52     52   75 my ( $lo, $hi ) = @_;
42 52         85 my $mask = ~( $lo ^ $hi );
43 52         96 my $bits = unpack 'B*', $mask;
44 52 100       104 return unless $hi eq ($lo | $hi);
45 47 100       205 return unless $bits =~ /^(1*)0*$/;
46 36         98 return length( $1 ) - 8;
47             }
48              
49             sub _encode {
50 204     204   296 my ( $self, $ip ) = @_;
51 204 100       805 if ( $ip =~ m{^(.+?)/(.+)$} ) {
    100          
52 135         244 my $mask = $2;
53 135 100       219 return unless my $addr = _pack( $1 );
54             return
55 110 100       309 unless my $bits
    100          
56             = ( $mask =~ /^\d+$/ )
57             ? _width2bits( $mask, 32 )
58             : _ip2bits( _pack( $mask ) );
59 109         286 return ( $addr & $bits, Net::CIDR::Set::_inc( $addr | ~$bits ) );
60             }
61             elsif ( $ip =~ m{^(.+?)-(.+)$} ) {
62 59 100       99 return unless my $lo = _pack( $1 );
63 46 50       75 return unless my $hi = _pack( $2 );
64 46         84 return ( $lo, Net::CIDR::Set::_inc( $hi ) );
65             }
66             else {
67 10         29 return $self->_encode( "$ip/32" );
68             }
69             }
70              
71             sub encode {
72 194     194 0 4806 my ( $self, $ip ) = @_;
73 194 100       316 my @r = $self->_encode( $ip )
74             or croak "Can't decode $ip as an IPv4 address";
75 155         402 return @r;
76             }
77              
78             sub decode {
79 57     57 0 4026 my $self = shift;
80 57         82 my $lo = shift;
81 57         104 my $hi = Net::CIDR::Set::_dec( shift );
82 57   100     136 my $generic = shift || 0;
83 57 100 100     227 if ( $generic < 1 && $lo eq $hi ) {
    100 100        
84             # Single address
85 3         6 return _unpack( $lo );
86             }
87             elsif ( $generic < 2 && defined( my $w = _is_cidr( $lo, $hi ) ) ) {
88             # Valid CIDR range
89 36         56 return join '/', _unpack( $lo ), $w;
90             }
91             else {
92             # General range
93 18         36 return join '-', _unpack( $lo ), _unpack( $hi );
94             }
95             }
96              
97 38     38 0 121 sub nbits { 32 }
98              
99             1;
100              
101             __END__