File Coverage

blib/lib/URI/_punycode.pm
Criterion Covered Total %
statement 115 119 96.6
branch 39 48 81.2
condition 6 9 66.6
subroutine 17 18 94.4
pod 2 2 100.0
total 179 196 91.3


line stmt bran cond sub pod time code
1             package URI::_punycode;
2              
3 4     4   71732 use strict;
  4         17  
  4         127  
4 4     4   18 use warnings;
  4         8  
  4         158  
5              
6             our $VERSION = '5.20';
7              
8 4     4   29 use Exporter 'import';
  4         22  
  4         227  
9             our @EXPORT = qw(encode_punycode decode_punycode);
10              
11 4     4   2193 use integer;
  4         60  
  4         21  
12              
13             our $DEBUG = 0;
14              
15 4     4   209 use constant BASE => 36;
  4         8  
  4         468  
16 4     4   29 use constant TMIN => 1;
  4         8  
  4         183  
17 4     4   21 use constant TMAX => 26;
  4         8  
  4         193  
18 4     4   25 use constant SKEW => 38;
  4         7  
  4         172  
19 4     4   21 use constant DAMP => 700;
  4         7  
  4         202  
20 4     4   23 use constant INITIAL_BIAS => 72;
  4         12  
  4         196  
21 4     4   33 use constant INITIAL_N => 128;
  4         8  
  4         4980  
22              
23             my $Delimiter = chr 0x2D;
24             my $BasicRE = qr/[\x00-\x7f]/;
25              
26 0     0   0 sub _croak { require Carp; Carp::croak(@_); }
  0         0  
27              
28             sub _digit_value {
29 158     158   233 my $code = shift;
30 158 100       301 return ord($code) - ord("A") if $code =~ /[A-Z]/;
31 155 100       393 return ord($code) - ord("a") if $code =~ /[a-z]/;
32 35 50       98 return ord($code) - ord("0") + 26 if $code =~ /[0-9]/;
33 0         0 return;
34             }
35              
36             sub _code_point {
37 309     309   449 my $digit = shift;
38 309 100 66     1007 return $digit + ord('a') if 0 <= $digit && $digit <= 25;
39 36 50 33     115 return $digit + ord('0') - 26 if 26 <= $digit && $digit <= 36;
40 0         0 die 'NOT COME HERE';
41             }
42              
43             sub _adapt {
44 296     296   471 my($delta, $numpoints, $firsttime) = @_;
45 296 100       478 $delta = $firsttime ? $delta / DAMP : $delta / 2;
46 296         350 $delta += $delta / $numpoints;
47 296         333 my $k = 0;
48 296         549 while ($delta > ((BASE - TMIN) * TMAX) / 2) {
49 22         24 $delta /= BASE - TMIN;
50 22         42 $k += BASE;
51             }
52 296         475 return $k + (((BASE - TMIN + 1) * $delta) / ($delta + SKEW));
53             }
54              
55             sub decode_punycode {
56 17     17 1 51 my $code = shift;
57              
58 17         25 my $n = INITIAL_N;
59 17         23 my $i = 0;
60 17         22 my $bias = INITIAL_BIAS;
61 17         28 my @output;
62              
63 17 100       125 if ($code =~ s/(.*)$Delimiter//o) {
64 11         87 push @output, map ord, split //, $1;
65 11 50       133 return _croak('non-basic code point') unless $1 =~ /^$BasicRE*$/o;
66             }
67              
68 17         45 while ($code) {
69 80         94 my $oldi = $i;
70 80         93 my $w = 1;
71             LOOP:
72 80         104 for (my $k = BASE; 1; $k += BASE) {
73 158         264 my $cp = substr($code, 0, 1, '');
74 158         220 my $digit = _digit_value($cp);
75 158 50       278 defined $digit or return _croak("invalid punycode input");
76 158         196 $i += $digit * $w;
77 158 100       288 my $t = ($k <= $bias) ? TMIN
    100          
78             : ($k >= $bias + TMAX) ? TMAX : $k - $bias;
79 158 100       277 last LOOP if $digit < $t;
80 78         111 $w *= (BASE - $t);
81             }
82 80         145 $bias = _adapt($i - $oldi, @output + 1, $oldi == 0);
83 80 50       152 warn "bias becomes $bias" if $DEBUG;
84 80         99 $n += $i / (@output + 1);
85 80         112 $i = $i % (@output + 1);
86 80         134 splice(@output, $i, 0, $n);
87 80 50       129 warn join " ", map sprintf('%04x', $_), @output if $DEBUG;
88 80         148 $i++;
89             }
90 17         193 return join '', map chr, @output;
91             }
92              
93             sub encode_punycode {
94 25     25 1 4509 my $input = shift;
95 25         120 my @input = split //, $input;
96              
97 25         50 my $n = INITIAL_N;
98 25         34 my $delta = 0;
99 25         34 my $bias = INITIAL_BIAS;
100              
101 25         34 my @output;
102 25         363 my @basic = grep /$BasicRE/, @input;
103 25         54 my $h = my $b = @basic;
104 25         52 push @output, @basic;
105 25 100 100     114 push @output, $Delimiter if $b && $h < @input;
106 25 50       52 warn "basic codepoints: (@output)" if $DEBUG;
107              
108 25         56 while ($h < @input) {
109 76         322 my $m = _min(grep { $_ >= $n } map ord, @input);
  1275         1824  
110 76 50       159 warn sprintf "next code point to insert is %04x", $m if $DEBUG;
111 76         120 $delta += ($m - $n) * ($h + 1);
112 76         91 $n = $m;
113 76         112 for my $i (@input) {
114 1275         1475 my $c = ord($i);
115 1275 100       1917 $delta++ if $c < $n;
116 1275 100       2029 if ($c == $n) {
117 216         245 my $q = $delta;
118             LOOP:
119 216         267 for (my $k = BASE; 1; $k += BASE) {
120 309 100       551 my $t = ($k <= $bias) ? TMIN :
    100          
121             ($k >= $bias + TMAX) ? TMAX : $k - $bias;
122 309 100       514 last LOOP if $q < $t;
123 93         163 my $cp = _code_point($t + (($q - $t) % (BASE - $t)));
124 93         188 push @output, chr($cp);
125 93         145 $q = ($q - $t) / (BASE - $t);
126             }
127 216         312 push @output, chr(_code_point($q));
128 216         369 $bias = _adapt($delta, $h + 1, $h == $b);
129 216 50       391 warn "bias becomes $bias" if $DEBUG;
130 216         253 $delta = 0;
131 216         325 $h++;
132             }
133             }
134 76         90 $delta++;
135 76         133 $n++;
136             }
137 25         166 return join '', @output;
138             }
139              
140             sub _min {
141 76     76   97 my $min = shift;
142 76 100       125 for (@_) { $min = $_ if $_ <= $min }
  490         801  
143 76         102 return $min;
144             }
145              
146             1;
147             __END__