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   74317 use strict;
  4         16  
  4         122  
4 4     4   18 use warnings;
  4         8  
  4         188  
5              
6             our $VERSION = '5.21';
7              
8 4     4   32 use Exporter 'import';
  4         10  
  4         208  
9             our @EXPORT = qw(encode_punycode decode_punycode);
10              
11 4     4   2204 use integer;
  4         58  
  4         20  
12              
13             our $DEBUG = 0;
14              
15 4     4   211 use constant BASE => 36;
  4         9  
  4         436  
16 4     4   26 use constant TMIN => 1;
  4         13  
  4         191  
17 4     4   23 use constant TMAX => 26;
  4         9  
  4         185  
18 4     4   29 use constant SKEW => 38;
  4         8  
  4         153  
19 4     4   20 use constant DAMP => 700;
  4         8  
  4         160  
20 4     4   20 use constant INITIAL_BIAS => 72;
  4         8  
  4         202  
21 4     4   25 use constant INITIAL_N => 128;
  4         16  
  4         5250  
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   207 my $code = shift;
30 158 100       298 return ord($code) - ord("A") if $code =~ /[A-Z]/;
31 155 100       346 return ord($code) - ord("a") if $code =~ /[a-z]/;
32 35 50       92 return ord($code) - ord("0") + 26 if $code =~ /[0-9]/;
33 0         0 return;
34             }
35              
36             sub _code_point {
37 309     309   363 my $digit = shift;
38 309 100 66     1056 return $digit + ord('a') if 0 <= $digit && $digit <= 25;
39 36 50 33     151 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   475 my($delta, $numpoints, $firsttime) = @_;
45 296 100       474 $delta = $firsttime ? $delta / DAMP : $delta / 2;
46 296         366 $delta += $delta / $numpoints;
47 296         331 my $k = 0;
48 296         536 while ($delta > ((BASE - TMIN) * TMAX) / 2) {
49 22         30 $delta /= BASE - TMIN;
50 22         36 $k += BASE;
51             }
52 296         466 return $k + (((BASE - TMIN + 1) * $delta) / ($delta + SKEW));
53             }
54              
55             sub decode_punycode {
56 17     17 1 56 my $code = shift;
57              
58 17         28 my $n = INITIAL_N;
59 17         19 my $i = 0;
60 17         22 my $bias = INITIAL_BIAS;
61 17         25 my @output;
62              
63 17 100       125 if ($code =~ s/(.*)$Delimiter//o) {
64 11         85 push @output, map ord, split //, $1;
65 11 50       220 return _croak('non-basic code point') unless $1 =~ /^$BasicRE*$/o;
66             }
67              
68 17         43 while ($code) {
69 80         92 my $oldi = $i;
70 80         91 my $w = 1;
71             LOOP:
72 80         113 for (my $k = BASE; 1; $k += BASE) {
73 158         272 my $cp = substr($code, 0, 1, '');
74 158         218 my $digit = _digit_value($cp);
75 158 50       278 defined $digit or return _croak("invalid punycode input");
76 158         189 $i += $digit * $w;
77 158 100       290 my $t = ($k <= $bias) ? TMIN
    100          
78             : ($k >= $bias + TMAX) ? TMAX : $k - $bias;
79 158 100       267 last LOOP if $digit < $t;
80 78         119 $w *= (BASE - $t);
81             }
82 80         147 $bias = _adapt($i - $oldi, @output + 1, $oldi == 0);
83 80 50       143 warn "bias becomes $bias" if $DEBUG;
84 80         100 $n += $i / (@output + 1);
85 80         128 $i = $i % (@output + 1);
86 80         123 splice(@output, $i, 0, $n);
87 80 50       124 warn join " ", map sprintf('%04x', $_), @output if $DEBUG;
88 80         176 $i++;
89             }
90 17         196 return join '', map chr, @output;
91             }
92              
93             sub encode_punycode {
94 25     25 1 3771 my $input = shift;
95 25         106 my @input = split //, $input;
96              
97 25         42 my $n = INITIAL_N;
98 25         31 my $delta = 0;
99 25         29 my $bias = INITIAL_BIAS;
100              
101 25         38 my @output;
102 25         361 my @basic = grep /$BasicRE/, @input;
103 25         52 my $h = my $b = @basic;
104 25         58 push @output, @basic;
105 25 100 100     108 push @output, $Delimiter if $b && $h < @input;
106 25 50       51 warn "basic codepoints: (@output)" if $DEBUG;
107              
108 25         52 while ($h < @input) {
109 76         353 my $m = _min(grep { $_ >= $n } map ord, @input);
  1275         1879  
110 76 50       157 warn sprintf "next code point to insert is %04x", $m if $DEBUG;
111 76         109 $delta += ($m - $n) * ($h + 1);
112 76         96 $n = $m;
113 76         108 for my $i (@input) {
114 1275         1466 my $c = ord($i);
115 1275 100       2235 $delta++ if $c < $n;
116 1275 100       2082 if ($c == $n) {
117 216         240 my $q = $delta;
118             LOOP:
119 216         264 for (my $k = BASE; 1; $k += BASE) {
120 309 100       559 my $t = ($k <= $bias) ? TMIN :
    100          
121             ($k >= $bias + TMAX) ? TMAX : $k - $bias;
122 309 100       545 last LOOP if $q < $t;
123 93         196 my $cp = _code_point($t + (($q - $t) % (BASE - $t)));
124 93         182 push @output, chr($cp);
125 93         156 $q = ($q - $t) / (BASE - $t);
126             }
127 216         321 push @output, chr(_code_point($q));
128 216         402 $bias = _adapt($delta, $h + 1, $h == $b);
129 216 50       365 warn "bias becomes $bias" if $DEBUG;
130 216         255 $delta = 0;
131 216         323 $h++;
132             }
133             }
134 76         96 $delta++;
135 76         134 $n++;
136             }
137 25         168 return join '', @output;
138             }
139              
140             sub _min {
141 76     76   100 my $min = shift;
142 76 100       131 for (@_) { $min = $_ if $_ <= $min }
  490         761  
143 76         105 return $min;
144             }
145              
146             1;
147             __END__