File Coverage

blib/lib/Net/IDN/Punycode/PP.pm
Criterion Covered Total %
statement 135 135 100.0
branch 38 48 79.1
condition 1 3 33.3
subroutine 21 21 100.0
pod 0 2 0.0
total 195 209 93.3


line stmt bran cond sub pod time code
1             package Net::IDN::Punycode::PP;
2              
3 2     2   145605 use 5.008;
  2         8  
4              
5 2     2   13 use strict;
  2         4  
  2         75  
6 2     2   11 use utf8;
  2         4  
  2         16  
7 2     2   55 use warnings;
  2         4  
  2         151  
8              
9 2     2   13 use Carp;
  2         5  
  2         174  
10 2     2   13 use Exporter;
  2         4  
  2         304  
11              
12             our $VERSION = "2.501";
13              
14             our @ISA = qw(Exporter);
15             our @EXPORT = ();
16             our @EXPORT_OK = qw(encode_punycode decode_punycode);
17             our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK );
18              
19 2     2   1248 use integer;
  2         34  
  2         30  
20              
21 2     2   92 use constant BASE => 36;
  2         4  
  2         260  
22 2     2   14 use constant TMIN => 1;
  2         4  
  2         106  
23 2     2   11 use constant TMAX => 26;
  2         5  
  2         126  
24 2     2   12 use constant SKEW => 38;
  2         3  
  2         87  
25 2     2   25 use constant DAMP => 700;
  2         4  
  2         97  
26 2     2   10 use constant INITIAL_BIAS => 72;
  2         4  
  2         96  
27 2     2   11 use constant INITIAL_N => 128;
  2         4  
  2         83  
28              
29 2     2   25 use constant UNICODE_MIN => 0;
  2         4  
  2         85  
30 2     2   10 use constant UNICODE_MAX => 0x10FFFF;
  2         4  
  2         586  
31              
32             my $Delimiter = chr 0x2D;
33             my $BasicRE = "\x00-\x7f";
34             my $PunyRE = "A-Za-z0-9";
35              
36             sub _adapt {
37 464     464   1015 my($delta, $numpoints, $firsttime) = @_;
38 464 100       937 $delta = int($firsttime ? $delta / DAMP : $delta / 2);
39 464         799 $delta += int($delta / $numpoints);
40 464         692 my $k = 0;
41 464         1071 while ($delta > int(((BASE - TMIN) * TMAX) / 2)) {
42 86         142 $delta /= BASE - TMIN;
43 86         188 $k += BASE;
44             }
45 464         1060 return $k + (((BASE - TMIN + 1) * $delta) / ($delta + SKEW));
46             }
47              
48             sub decode_punycode {
49 21 50   21 0 75 die("Usage: Net::IDN::Punycode::decode_punycode(input)") unless @_;
50 2     2   15 no warnings 'utf8';
  2         4  
  2         1301  
51              
52 21         44 my $input = shift;
53              
54 21         42 my $n = INITIAL_N;
55 21         35 my $i = 0;
56 21         35 my $bias = INITIAL_BIAS;
57 21         34 my @output;
58              
59 21 50       53 return undef unless defined $input;
60 21 50       56 return '' unless length $input;
61              
62 21 100       164 if($input =~ s/(.*)$Delimiter//os) {
63 10         34 my $base_chars = $1;
64 10 50       55 croak("non-base character in input for decode_punycode")
65             if $base_chars =~ m/[^$BasicRE]/os;
66 10         74 push @output, split //, $base_chars;
67             }
68 21         49 my $code = $input;
69              
70 21 50       108 croak('invalid digit in input for decode_punycode') if $code =~ m/[^$PunyRE]/os;
71              
72 21         75 utf8::downgrade($input); ## handling failure of downgrade is more expensive than
73             ## doing the above regexp w/ utf8 semantics
74              
75 21         54 while(length $code)
76             {
77 232         364 my $oldi = $i;
78 232         377 my $w = 1;
79             LOOP:
80 232         376 for (my $k = BASE; 1; $k += BASE) {
81 431         862 my $cp = substr($code, 0, 1, '');
82 431 50       962 croak("incomplete encoded code point in decode_punycode") if !defined $cp;
83 431         674 my $digit = ord $cp;
84            
85             ## NB: this depends on the PunyRE catching invalid digit characters
86             ## before they turn up here
87             ##
88 431 100       907 $digit = $digit < 0x40 ? $digit + (26-0x30) : ($digit & 0x1f) -1;
89              
90 431         692 $i += $digit * $w;
91 431         679 my $t = $k - $bias;
92 431 100       908 $t = $t < TMIN ? TMIN : $t > TMAX ? TMAX : $t;
    100          
93              
94 431 100       1009 last LOOP if $digit < $t;
95 199         444 $w *= (BASE - $t);
96             }
97 232         557 $bias = _adapt($i - $oldi, @output + 1, $oldi == 0);
98 232         469 $n += $i / (@output + 1);
99 232         419 $i = $i % (@output + 1);
100 232 50 33     872 croak('invalid code point') if $n < UNICODE_MIN or $n > UNICODE_MAX;
101 232         636 splice(@output, $i, 0, chr($n));
102 232         566 $i++;
103             }
104 21         237 return join '', @output;
105             }
106              
107             sub encode_punycode {
108 21 50   21 0 237396 die("Usage: Net::IDN::Punycode::encode_punycode(input)") unless @_;
109 2     2   17 no warnings 'utf8';
  2         5  
  2         1212  
110              
111 21         50 my $input = shift;
112 21         63 my $input_length = length $input;
113              
114             ## my $output = join '', $input =~ m/([$BasicRE]+)/og; ## slower
115 21         41 my $output = $input; $output =~ s/[^$BasicRE]+//ogs;
  21         199  
116              
117 21         88 my $h = my $bb = length $output;
118 21 100       70 $output .= $Delimiter if $bb > 0;
119 21         70 utf8::downgrade($output); ## no unnecessary use of utf8 semantics
120              
121 21         313 my @input = map ord, split //, $input;
122 21         83 my @chars = sort { $a<=> $b } grep { $_ >= INITIAL_N } @input;
  648         1083  
  368         860  
123              
124 21         39 my $n = INITIAL_N;
125 21         65 my $delta = 0;
126 21         33 my $bias = INITIAL_BIAS;
127              
128 21         51 foreach my $m (@chars) {
129 232 100       542 next if $m < $n;
130 178         325 $delta += ($m - $n) * ($h + 1);
131 178         299 $n = $m;
132 178         447 for(my $i = 0; $i < $input_length; $i++)
133             {
134 3432         5512 my $c = $input[$i];
135 3432 100       7132 $delta++ if $c < $n;
136 3432 100       9211 if ($c == $n) {
137 232         366 my $q = $delta;
138             LOOP:
139 232         457 for (my $k = BASE; 1; $k += BASE) {
140 431         728 my $t = $k - $bias;
141 431 100       1044 $t = $t < TMIN ? TMIN : $t > TMAX ? TMAX : $t;
    100          
142              
143 431 100       1078 last LOOP if $q < $t;
144              
145 199         360 my $o = $t + (($q - $t) % (BASE - $t));
146 199 100       498 $output .= chr $o + ($o < 26 ? 0x61 : 0x30-26);
147              
148 199         444 $q = int(($q - $t) / (BASE - $t));
149             }
150 232 50       530 croak("input exceeds punycode limit") if $q > BASE;
151 232 50       606 $output .= chr $q + ($q < 26 ? 0x61 : 0x30-26);
152              
153 232         555 $bias = _adapt($delta, $h + 1, $h == $bb);
154 232         442 $delta = 0;
155 232         594 $h++;
156             }
157             }
158 178         279 $delta++;
159 178         337 $n++;
160             }
161 21         194 return $output;
162             }
163              
164             1;
165             __END__