File Coverage

blib/lib/Net/IP/Match/Trie/PP.pm
Criterion Covered Total %
statement 83 83 100.0
branch 10 12 83.3
condition 2 5 40.0
subroutine 12 12 100.0
pod 2 9 22.2
total 109 121 90.0


line stmt bran cond sub pod time code
1             # -*- mode: coding: utf-8; -*-
2             package Net::IP::Match::Trie;
3              
4 1     1   3 use strict;
  1         1  
  1         26  
5 1     1   3 use warnings;
  1         2  
  1         28  
6              
7 1     1   546 use Socket qw(inet_aton);
  1         2641  
  1         759  
8              
9             our $VERSION = '0.01_01';
10              
11             our $CIDR_TABLE_BITS = 8;
12             our $CIDR_TABLE_SIZE = (1 << $CIDR_TABLE_BITS);
13              
14             # helper
15             sub itonetmask($) {
16 6     6 0 6 my($n, $netmask) = @_;
17              
18 6 50 33     30 return () if ($n < 0 || 32 < $n);
19              
20 6         8 my $m = 1 << (32 - $n);
21 6         5 --$m;
22 6         3 $netmask = ~$m;
23 6         8 return $netmask & 0xFFFFFFFF;
24             }
25              
26             sub is_leaf($) {
27 262175     262175 0 157578 my($pt) = @_;
28 262175         310624 return $pt->{child}[0] == $pt;
29             }
30              
31             sub new_trie_node() {
32 14     14 0 32 my $node = { name => "", bits => 0, child => [] };
33 14         25 for (my $i = 0; $i < $CIDR_TABLE_SIZE; $i++) {
34 3584         3974 $node->{child}[$i] = $node;
35             }
36 14         15 return $node;
37             }
38              
39             sub digg_trie($) {
40 6     6 0 5 my($child) = @_;
41 6         6 my $parent = new_trie_node;
42 6         13 for (my $i = 0; $i < $CIDR_TABLE_SIZE; $i++) {
43 1536         1650 $parent->{child}[$i] = $child;
44             }
45 6         8 return $parent;
46             }
47              
48             sub update_leaf($$) {
49 1024     1024 0 712 my($pt, $leaf) = @_;
50 1024         634 my $used = 0;
51              
52 1024         1253 for (my $i = 0; $i < $CIDR_TABLE_SIZE; $i++) {
53 262144         166198 my $next = $pt->{child}[$i];
54 262144 100       205601 if (is_leaf($next)) {
55 261376 100       466097 if ($next->{bits} < $leaf->{bits}) {
56 1003         622 $pt->{child}[$i] = $leaf;
57 1003         1156 $used = 1;
58             }
59             } else {
60 768         787 $used |= &update_leaf($next, $leaf);
61             }
62             }
63              
64 1024         1948 return $used;
65             }
66              
67              
68             sub new {
69 1     1 0 7 my($class, %opt) = @_;
70              
71 1         2 my $self = bless {
72             }, $class;
73              
74 1         2 my $root = new_trie_node;
75 1         2 $root->{name} = "R";
76 1         2 my $nullnode = new_trie_node;
77 1         5 for (my $i=0; $i < $CIDR_TABLE_SIZE; $i++) {
78 256         268 $root->{child}[$i] = $nullnode;
79             }
80 1         5 $self->{root} = $root;
81              
82 1         3 return $self;
83             }
84              
85             # name => [ cidr1, cidr2, ... ]
86             sub add {
87 4     4 1 23 my($self, $name, $cidrs) = @_;
88              
89 4         4 my $ad;
90 4         2 my $nm = 0xFFFFFFFF;
91              
92             ### name: $name
93 4         7 for my $cidr (@$cidrs) {
94 6         17 my($ip, $len) = split m{/}, $cidr, 2;
95 6   50     9 $len ||= 32;
96             ### cidr, ip, len: join ', ', $cidr, $ip, $len
97              
98 6         38 $ad = unpack "N", inet_aton($ip);
99 6         9 $nm = itonetmask($len);
100             ### ad : sprintf "%08X", $ad
101             ### nm : sprintf "%08X", $nm
102              
103 6         6 $ad = $ad & ($nm & 0xFFFFFFFF);
104             ### ad&nm: sprintf "%08X", $ad
105              
106 6         7 my $pt = $self->{root};
107 6         8 my $p_leaf = new_trie_node;
108              
109 6         8 $p_leaf->{name} = $name;
110 6         7 $p_leaf->{bits} = $len;
111              
112 6         12 while ($len > $CIDR_TABLE_BITS) {
113             ### ad : sprintf "%08X", $ad
114 10         10 my $b = $ad >> (32 - $CIDR_TABLE_BITS);
115             ### b: $b
116 10         11 my $next = $pt->{child}[$b];
117 10 100       10 if (is_leaf($next)) {
118 6         9 $pt->{child}[$b] = $next = digg_trie($next);
119             }
120 10         11 $pt = $next;
121 10         9 $ad = $ad << $CIDR_TABLE_BITS & 0xFFFFFFFF;
122 10         13 $len -= $CIDR_TABLE_BITS;
123             }
124              
125             {
126 6         5 my $bmin = $ad >> (32 - $CIDR_TABLE_BITS);
  6         7  
127 6         5 my $bmax = $bmin + (1 << ($CIDR_TABLE_BITS - $len));
128 6         5 my $used = 0;
129 6         11 for (my $i = $bmin; $i < $bmax; $i++) {
130 21         14 my $target = $pt->{child}[$i];
131 21 100       23 if (is_leaf($target)) {
132 20 50       27 if ($target->{bits} < $p_leaf->{bits}) {
133 20         13 $pt->{child}[$i] = $p_leaf;
134 20         34 $used = 1;
135             }
136             } else {
137 1         3 for (my $j = 0; $j < $CIDR_TABLE_SIZE; $j++) {
138 256         321 $used |= update_leaf($target, $p_leaf);
139             }
140             }
141             }
142             }
143             }
144             }
145              
146             sub match_ip {
147 10     10 1 32 my($self, $ip) = @_;
148              
149 10         29 my @addrs = split /\./, $ip, 4;
150 10         68 return $self->{root}{child}[$addrs[0]]->{child}[$addrs[1]]->{child}[$addrs[2]]->{child}[$addrs[3]]->{name};
151             }
152              
153             sub impl {
154 1     1 0 6 my($self) = @_;
155 1         3 return "PP";
156             }
157              
158             1;