File Coverage

blib/lib/IP/Country/Fast.pm
Criterion Covered Total %
statement 36 40 90.0
branch 13 16 81.2
condition 2 6 33.3
subroutine 7 8 87.5
pod 0 4 0.0
total 58 74 78.3


line stmt bran cond sub pod time code
1             package IP::Country::Fast;
2 4     4   12352 use strict;
  4         8  
  4         128  
3 4     4   21 use warnings;
  4         6  
  4         133  
4 4     4   3283 use Socket qw ( inet_aton );
  4         12859  
  4         715  
5              
6 4     4   31 use vars qw ( $VERSION );
  4         5  
  4         7609  
7             $VERSION = '604.001'; # APR 2006, version 001
8              
9             my $singleton = undef;
10             my $ip_db;
11             my $null = substr(pack('N',0),0,1);
12             my $nullnullnull = $null . $null . $null;
13             my %cc;
14             my $tld_match = qr/\.([a-zA-Z][a-zA-Z])$/o;
15             my $ip_match = qr/^(\d|[01]?\d\d|2[0-4]\d|25[0-5])\.(\d|[01]?\d\d|2[0-4]\d|25[0-5])\.(\d|[01]?\d\d|2[0-4]\d|25[0-5])\.(\d|[01]?\d\d|2[0-4]\d|25[0-5])$/o;
16              
17             my $bit0;
18             my $bit1;
19             my @mask;
20             my @dtoc;
21             {
22             $bit0 = substr(pack('N',2 ** 31),0,1);
23             $bit1 = substr(pack('N',2 ** 30),0,1);
24              
25             for (my $i = 0; $i <= 31; $i++){
26             $mask[$i] = pack('N',2 ** (31 - $i));
27             }
28              
29             for (my $i = 0; $i <= 255; $i++){
30             $dtoc[$i] = substr(pack('N',$i),3,1);
31             }
32             (my $module_dir = __FILE__) =~ s/\.pm$//;
33              
34             local $/; # set it so <> reads all the file at once
35              
36             open (CC, "< $module_dir/cc.gif")
37             or die ("couldn't read country database: $!");
38             binmode CC;
39             my $cc_ultra = ; # read in the file
40             close CC;
41             my $cc_num = (length $cc_ultra) / 3;
42             for (my $i = 0; $i < $cc_num; $i++){
43             my $cc = substr($cc_ultra,3 * $i + 1,2);
44             $cc = undef if ($cc eq '--');
45             $cc{substr($cc_ultra,3 * $i,1)} = $cc;
46             }
47              
48             open (IP, "< $module_dir/ip.gif")
49             or die ("couldn't read IP database: $!");
50             binmode IP;
51             $ip_db = ;
52             close IP;
53             }
54              
55             sub new ()
56             {
57 4     4 0 105554 my $caller = shift;
58 4 100       23 unless (defined $singleton){
59 3   33     30 my $class = ref($caller) || $caller;
60 3         14 $singleton = bless {}, $class;
61             }
62 4         21 return $singleton;
63             }
64              
65             sub db_time
66             {
67 0     0 0 0 return unpack("N",substr($ip_db,0,4));
68             }
69              
70             sub inet_atocc
71             {
72 65563     65563 0 299714 my $inet_a = $_[1];
73 65563 50       403214 if ($inet_a =~ $ip_match){
74 65563         311499 return inet_ntocc($dtoc[$1].$dtoc[$2].$dtoc[$3].$dtoc[$4]);
75             } else {
76 0 0       0 if (defined (my $n = inet_aton($inet_a))){
77 0         0 return inet_ntocc($n);
78             } else {
79 0         0 return undef;
80             }
81             }
82             }
83              
84             sub inet_ntocc
85             {
86             # FORMATTING OF EACH NODE IN $ip_db
87             # bit0 - true if this is a country code, false if this
88             # is a jump to the next node
89             #
90             # country codes:
91             # bit1 - true if the country code is stored in bits 2-7
92             # of this byte, false if the country code is
93             # stored in bits 0-7 of the next byte
94             # bits 2-7 or bits 0-7 of next byte contain country code
95             #
96             # jumps:
97             # bytes 0-3 jump distance (only first byte used if
98             # distance < 64)
99              
100 65592   33 65592 0 173253 my $inet_n = $_[1] || $_[0];
101              
102 65592         72648 my $pos = 4;
103 65592         88425 my $byte_zero = substr($ip_db,$pos,1);
104             # loop through bits of IP address
105 65592         143282 for (my $i = 0; $i <= 31; $i++){
106              
107 738094 100       1347810 if (($inet_n & $mask[$i]) eq $mask[$i]){
108             # bit[$i] is set [binary one]
109             # - jump to next node
110             # (start of child[1] node)
111 369614 100       598430 if (($byte_zero & $bit1) eq $bit1){
112 85974         2279162 $pos = $pos + 1 + unpack('N', $nullnullnull . ($byte_zero ^ $bit1));
113             } else {
114 283640         10442606 $pos = $pos + 3 + unpack('N', $null . substr($ip_db,$pos,3));
115             }
116             } else {
117             # bit[$i] is unset [binary zero]
118             # jump to end of this node
119             # (start of child[0] node)
120 368480 100       576188 if (($byte_zero & $bit1) eq $bit1){
121 85600         112969 $pos = $pos + 1;
122             } else {
123 282880         327600 $pos = $pos + 3;
124             }
125             }
126            
127             # all terminal nodes of the tree start with zeroth bit
128             # set to zero. the first bit can then be used to indicate
129             # whether we're using the first or second byte to store the
130             # country code
131 738094         923873 $byte_zero = substr($ip_db,$pos,1);
132 738094 100       3566327 if (($byte_zero & $bit0) eq $bit0){ # country code
133 65592 100       104577 if (($byte_zero & $bit1) eq $bit1){
134             # unpopular country code - stored in second byte
135 2410         10647 return $cc{substr($ip_db,$pos+1,1)};
136             } else {
137             # popular country code - stored in bits 2-7
138             # (we already know that bit 1 is not set, so
139             # just need to unset bit 1)
140 63182         1994361 return $cc{$byte_zero ^ $bit0};
141             }
142             }
143             }
144             }
145              
146             1;
147             __END__