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   51 use warnings;
  9         15  
  9         390  
4 9     9   30 use strict;
  9         26  
  9         130  
5 9     9   25 use Carp;
  9         10  
  9         443  
6              
7 9     9   3612 use namespace::autoclean;
  9         139582  
  9         45  
8              
9             # ABSTRACT: Encode / decode IPv4 addresses
10              
11             our $VERSION = '0.21';
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   437 my @nums = split /[.]/, shift, -1;
20 207 50       333 return unless @nums == 4;
21 207         240 for ( @nums ) {
22 825 100 66     3312 return unless /\A${DEC}\z/ and !/\A0[1-9]+/;
23             }
24 206         705 return pack "CC*", 0, @nums;
25             }
26              
27 75     75   359 sub _unpack { join ".", unpack "xC*", shift }
28              
29             sub _width2bits {
30 110     110   165 my ( $width, $size ) = @_;
31 110         408 return pack 'B*',
32             ( '1' x ( $width + 8 ) ) . ( '0' x ( $size - $width ) );
33             }
34              
35             sub _ip2bits {
36 2 50   2   5 my $ip = shift or return;
37 2         6 vec( $ip, 0, 8 ) = 255;
38 2         7 my $bits = unpack 'B*', $ip;
39 2 100       155 return unless $bits =~ /\A1*0*\z/; # Valid mask?
40 1         2 return $ip;
41             }
42              
43             sub _is_cidr {
44 52     52   72 my ( $lo, $hi ) = @_;
45 52         82 my $mask = ~( $lo ^ $hi );
46 52         86 my $bits = unpack 'B*', $mask;
47 52 100       89 return unless $hi eq ($lo | $hi);
48 47 100       196 return unless $bits =~ /\A(1*)0*\z/;
49 36         112 return length( $1 ) - 8;
50             }
51              
52             sub _encode {
53 206     206   247 my ( $self, $ip ) = @_;
54 206 100       4192 if ( $ip =~ m{\A(${IP})/((?:3[0-2]|[12]?[0-9])|${IP})\z} ) {
    100          
    100          
55 113         208 my $mask = $2;
56 113 100       158 return unless my $addr = _pack( $1 );
57             return
58 112 100       280 unless my $bits
    100          
59             = ( $mask =~ /\A[0-9]+\z/ )
60             ? _width2bits( $mask, 32 )
61             : _ip2bits( _pack( $mask ) );
62 111         312 return ( $addr & $bits, Net::CIDR::Set::_inc( $addr | ~$bits ) );
63             }
64             elsif ( $ip =~ m{\A(${IP})-(${IP})\z} ) {
65 46 50       85 return unless my $lo = _pack( $1 );
66 46 50       61 return unless my $hi = _pack( $2 );
67 46         81 return ( $lo, Net::CIDR::Set::_inc( $hi ) );
68             }
69             elsif ( $ip =~ m{\A${IP}\z} ) {
70 5         22 return $self->_encode( "$ip/32" );
71             }
72             else {
73 42         3247 return;
74             }
75             }
76              
77             sub encode {
78 201     201 0 4908 my ( $self, $ip ) = @_;
79 201 100       333 my @r = $self->_encode( $ip )
80             or croak "Can't decode $ip as an IPv4 address";
81 157         373 return @r;
82             }
83              
84             sub decode {
85 57     57 0 4225 my $self = shift;
86 57         58 my $lo = shift;
87 57         89 my $hi = Net::CIDR::Set::_dec( shift );
88 57   100     134 my $generic = shift || 0;
89 57 100 100     207 if ( $generic < 1 && $lo eq $hi ) {
    100 100        
90             # Single address
91 3         9 return _unpack( $lo );
92             }
93             elsif ( $generic < 2 && defined( my $w = _is_cidr( $lo, $hi ) ) ) {
94             # Valid CIDR range
95 36         49 return join '/', _unpack( $lo ), $w;
96             }
97             else {
98             # General range
99 18         35 return join '-', _unpack( $lo ), _unpack( $hi );
100             }
101             }
102              
103 38     38 0 124 sub nbits { 32 }
104              
105             1;
106              
107             __END__