File Coverage

blib/lib/Net/CIDR/Set/IPv4.pm
Criterion Covered Total %
statement 51 51 100.0
branch 27 32 84.3
condition 9 11 81.8
subroutine 13 13 100.0
pod 0 4 0.0
total 100 111 90.0


line stmt bran cond sub pod time code
1             package Net::CIDR::Set::IPv4;
2              
3 6     6   32 use warnings;
  6         10  
  6         163  
4 6     6   30 use strict;
  6         11  
  6         3722  
5 6     6   40 use Carp;
  6         23  
  6         8280  
6              
7             =head1 NAME
8              
9             Net::CIDR::Set::IPv4 - Encode / decode IPv4 addresses
10              
11             =head1 VERSION
12              
13             This document describes Net::CIDR::Set::IPv4 version 0.13
14              
15             =cut
16              
17             our $VERSION = '0.13';
18              
19 18     18 0 69 sub new { bless \my $x, shift }
20              
21             sub _pack {
22 68     68   280 my @nums = split /[.]/, shift, -1;
23 68 100       1112 return unless @nums == 4;
24 61         111 for ( @nums ) {
25 244 50 33     1430 return unless /^\d{1,3}$/ and $_ < 256;
26             }
27 61         310 return pack "CC*", 0, @nums;
28             }
29              
30 37     37   335 sub _unpack { join ".", unpack "xC*", shift }
31              
32             sub _width2bits {
33 37     37   63 my ( $width, $size ) = @_;
34 37         206 return pack 'B*',
35             ( '1' x ( $width + 8 ) ) . ( '0' x ( $size - $width ) );
36             }
37              
38             sub _ip2bits {
39 2 50   2   8 my $ip = shift or return;
40 2         7 vec( $ip, 0, 8 ) = 255;
41 2         9 my $bits = unpack 'B*', $ip;
42 2 100       211 return unless $bits =~ /^1*0*$/; # Valid mask?
43 1         5 return $ip;
44             }
45              
46             sub _is_cidr {
47 27     27   47 my ( $lo, $hi ) = @_;
48 27         53 my $mask = ~( $lo ^ $hi );
49 27         92 my $bits = unpack 'B*', $mask;
50 27 50       74 return unless $hi eq ($lo | $hi);
51 27 100       123 return unless $bits =~ /^(1*)0*$/;
52 24         111 return length( $1 ) - 8;
53             }
54              
55             sub _encode {
56 66     66   81 my ( $self, $ip ) = @_;
57 66 100       303 if ( $ip =~ m{^(.+?)/(.+)$} ) {
    100          
58 46         121 my $mask = $2;
59 46 100       88 return unless my $addr = _pack( $1 );
60             return
61 39 100       160 unless my $bits
    100          
62             = ( $mask =~ /^\d+$/ )
63             ? _width2bits( $mask, 32 )
64             : _ip2bits( _pack( $mask ) );
65 38         371 return ( $addr & $bits, Net::CIDR::Set::_inc( $addr | ~$bits ) );
66             }
67             elsif ( $ip =~ m{^(.+?)-(.+)$} ) {
68 10 50       39 return unless my $lo = _pack( $1 );
69 10 50       22 return unless my $hi = _pack( $2 );
70 10         36 return ( $lo, Net::CIDR::Set::_inc( $hi ) );
71             }
72             else {
73 10         44 return $self->_encode( "$ip/32" );
74             }
75             }
76              
77             sub encode {
78 56     56 0 6422 my ( $self, $ip ) = @_;
79 56 100       127 my @r = $self->_encode( $ip )
80             or croak "Can't decode $ip as an IPv4 address";
81 48         238 return @r;
82             }
83              
84             sub decode {
85 32     32 0 3966 my $self = shift;
86 32         40 my $lo = shift;
87 32         88 my $hi = Net::CIDR::Set::_dec( shift );
88 32   100     121 my $generic = shift || 0;
89 32 100 100     186 if ( $generic < 1 && $lo eq $hi ) {
    100 100        
90             # Single address
91 3         18 return _unpack( $lo );
92             }
93             elsif ( $generic < 2 && defined( my $w = _is_cidr( $lo, $hi ) ) ) {
94             # Valid CIDR range
95 24         45 return join '/', _unpack( $lo ), $w;
96             }
97             else {
98             # General range
99 5         12 return join '-', _unpack( $lo ), _unpack( $hi );
100             }
101             }
102              
103 38     38 0 195 sub nbits { 32 }
104              
105             1;
106             __END__