File Coverage

blib/lib/Net/CIDR/Set/IPv4.pm
Criterion Covered Total %
statement 55 55 100.0
branch 30 34 88.2
condition 10 11 90.9
subroutine 14 14 100.0
pod 0 4 0.0
total 109 118 92.3


line stmt bran cond sub pod time code
1             package Net::CIDR::Set::IPv4;
2              
3 9     9   50 use warnings;
  9         14  
  9         363  
4 9     9   31 use strict;
  9         10  
  9         113  
5 9     9   22 use Carp;
  9         9  
  9         388  
6              
7 9     9   3548 use namespace::autoclean;
  9         138765  
  9         47  
8              
9             # ABSTRACT: Encode / decode IPv4 addresses
10              
11             our $VERSION = '0.22';
12              
13 96     96 0 193 sub new { bless \my $x, shift }
14              
15             my $DEC = "(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})";
16             my $IP = "(?:${DEC}\.){3}${DEC}";
17              
18             sub _pack {
19 207     207   433 my @nums = split /[.]/, shift, -1;
20 207 50       302 return unless @nums == 4;
21 207         248 for ( @nums ) {
22 825 100 66     3271 return unless /\A${DEC}\z/ and !/\A0[1-9]+/;
23             }
24 206         693 return pack "CC*", 0, @nums;
25             }
26              
27 75     75   388 sub _unpack { join ".", unpack "xC*", shift }
28              
29             sub _width2bits {
30 110     110   141 my ( $width, $size ) = @_;
31 110         377 return pack 'B*',
32             ( '1' x ( $width + 8 ) ) . ( '0' x ( $size - $width ) );
33             }
34              
35             sub _ip2bits {
36 2 50   2   6 my $ip = shift or return;
37 2         5 vec( $ip, 0, 8 ) = 255;
38 2         6 my $bits = unpack 'B*', $ip;
39 2 100       165 return unless $bits =~ /\A1*0*\z/; # Valid mask?
40 1         3 return $ip;
41             }
42              
43             sub _is_cidr {
44 52     52   70 my ( $lo, $hi ) = @_;
45 52         89 my $mask = ~( $lo ^ $hi );
46 52         80 my $bits = unpack 'B*', $mask;
47 52 100       92 return unless $hi eq ($lo | $hi);
48 47 100       189 return unless $bits =~ /\A(1*)0*\z/;
49 36         93 return length( $1 ) - 8;
50             }
51              
52             sub _encode {
53 206     206   272 my ( $self, $ip ) = @_;
54 206 100       4241 if ( $ip =~ m{\A(${IP})/((?:3[0-2]|[12]?[0-9])|${IP})\z} ) {
    100          
    100          
55 113         197 my $mask = $2;
56 113 100       156 return unless my $addr = _pack( $1 );
57             return
58 112 100       283 unless my $bits
    100          
59             = ( $mask =~ /\A[0-9]+\z/ )
60             ? _width2bits( $mask, 32 )
61             : _ip2bits( _pack( $mask ) );
62 111         286 return ( $addr & $bits, Net::CIDR::Set::_inc( $addr | ~$bits ) );
63             }
64             elsif ( $ip =~ m{\A(${IP})-(${IP})\z} ) {
65 46 50       82 return unless my $lo = _pack( $1 );
66 46 50       60 return unless my $hi = _pack( $2 );
67 46         80 return ( $lo, Net::CIDR::Set::_inc( $hi ) );
68             }
69             elsif ( $ip =~ m{\A${IP}\z} ) {
70 5         20 return $self->_encode( "$ip/32" );
71             }
72             else {
73 42         3241 return;
74             }
75             }
76              
77             sub encode {
78 201     201 0 4606 my ( $self, $ip ) = @_;
79 201 100       329 my @r = $self->_encode( $ip )
80             or croak "Can't decode $ip as an IPv4 address";
81 157         409 return @r;
82             }
83              
84             sub decode {
85 57     57 0 3914 my $self = shift;
86 57         75 my $lo = shift;
87 57         107 my $hi = Net::CIDR::Set::_dec( shift );
88 57   100     115 my $generic = shift || 0;
89 57 100 100     233 if ( $generic < 1 && $lo eq $hi ) {
    100 100        
90             # Single address
91 3         15 return _unpack( $lo );
92             }
93             elsif ( $generic < 2 && defined( my $w = _is_cidr( $lo, $hi ) ) ) {
94             # Valid CIDR range
95 36         64 return join '/', _unpack( $lo ), $w;
96             }
97             else {
98             # General range
99 18         30 return join '-', _unpack( $lo ), _unpack( $hi );
100             }
101             }
102              
103 38     38 0 98 sub nbits { 32 }
104              
105             1;
106              
107             __END__