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   57 use warnings;
  8         21  
  8         482  
4 8     8   43 use strict;
  8         13  
  8         294  
5 8     8   38 use Carp;
  8         11  
  8         514  
6              
7 8     8   4764 use namespace::autoclean;
  8         186576  
  8         47  
8              
9             # ABSTRACT: Encode / decode IPv4 addresses
10              
11             our $VERSION = '0.19';
12              
13 91     91 0 237 sub new { bless \my $x, shift }
14              
15             sub _pack {
16 242     242   704 my @nums = split /[.]/, shift, -1;
17 242 100       5453 return unless @nums == 4;
18 205         344 for ( @nums ) {
19 817 100 66     4053 return unless /^\d{1,3}$/ and !/^0\d{1,2}$/ and $_ < 256;
      66        
20             }
21 204         806 return pack "CC*", 0, @nums;
22             }
23              
24 75     75   894 sub _unpack { join ".", unpack "xC*", shift }
25              
26             sub _width2bits {
27 108     108   152 my ( $width, $size ) = @_;
28 108         457 return pack 'B*',
29             ( '1' x ( $width + 8 ) ) . ( '0' x ( $size - $width ) );
30             }
31              
32             sub _ip2bits {
33 2 50   2   8 my $ip = shift or return;
34 2         10 vec( $ip, 0, 8 ) = 255;
35 2         10 my $bits = unpack 'B*', $ip;
36 2 100       251 return unless $bits =~ /^1*0*$/; # Valid mask?
37 1         4 return $ip;
38             }
39              
40             sub _is_cidr {
41 52     52   110 my ( $lo, $hi ) = @_;
42 52         107 my $mask = ~( $lo ^ $hi );
43 52         153 my $bits = unpack 'B*', $mask;
44 52 100       126 return unless $hi eq ($lo | $hi);
45 47 100       278 return unless $bits =~ /^(1*)0*$/;
46 36         134 return length( $1 ) - 8;
47             }
48              
49             sub _encode {
50 204     204   318 my ( $self, $ip ) = @_;
51 204 100       948 if ( $ip =~ m{^(.+?)/(.+)$} ) {
    100          
52 135         275 my $mask = $2;
53 135 100       258 return unless my $addr = _pack( $1 );
54             return
55 110 100       340 unless my $bits
    100          
56             = ( $mask =~ /^\d+$/ )
57             ? _width2bits( $mask, 32 )
58             : _ip2bits( _pack( $mask ) );
59 109         333 return ( $addr & $bits, Net::CIDR::Set::_inc( $addr | ~$bits ) );
60             }
61             elsif ( $ip =~ m{^(.+?)-(.+)$} ) {
62 59 100       127 return unless my $lo = _pack( $1 );
63 46 50       85 return unless my $hi = _pack( $2 );
64 46         114 return ( $lo, Net::CIDR::Set::_inc( $hi ) );
65             }
66             else {
67 10         60 return $self->_encode( "$ip/32" );
68             }
69             }
70              
71             sub encode {
72 194     194 0 5876 my ( $self, $ip ) = @_;
73 194 100       387 my @r = $self->_encode( $ip )
74             or croak "Can't decode $ip as an IPv4 address";
75 155         459 return @r;
76             }
77              
78             sub decode {
79 57     57 0 5465 my $self = shift;
80 57         81 my $lo = shift;
81 57         121 my $hi = Net::CIDR::Set::_dec( shift );
82 57   100     167 my $generic = shift || 0;
83 57 100 100     261 if ( $generic < 1 && $lo eq $hi ) {
    100 100        
84             # Single address
85 3         9 return _unpack( $lo );
86             }
87             elsif ( $generic < 2 && defined( my $w = _is_cidr( $lo, $hi ) ) ) {
88             # Valid CIDR range
89 36         76 return join '/', _unpack( $lo ), $w;
90             }
91             else {
92             # General range
93 18         58 return join '-', _unpack( $lo ), _unpack( $hi );
94             }
95             }
96              
97 38     38 0 145 sub nbits { 32 }
98              
99             1;
100              
101             __END__