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   85405 use strict;
  4         6  
  4         113  
4 4     4   13 use warnings;
  4         10  
  4         215  
5              
6             our $VERSION = '5.35';
7              
8 4     4   15 use Exporter 'import';
  4         4  
  4         197  
9             our @EXPORT = qw(encode_punycode decode_punycode);
10              
11 4     4   1711 use integer;
  4         62  
  4         23  
12              
13             our $DEBUG = 0;
14              
15 4     4   189 use constant BASE => 36;
  4         6  
  4         299  
16 4     4   18 use constant TMIN => 1;
  4         4  
  4         142  
17 4     4   14 use constant TMAX => 26;
  4         4  
  4         124  
18 4     4   15 use constant SKEW => 38;
  4         5  
  4         95  
19 4     4   12 use constant DAMP => 700;
  4         5  
  4         94  
20 4     4   11 use constant INITIAL_BIAS => 72;
  4         4  
  4         146  
21 4     4   35 use constant INITIAL_N => 128;
  4         4  
  4         4152  
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   147 my $code = shift;
30 158 100       226 return ord($code) - ord("A") if $code =~ /[A-Z]/;
31 155 100       244 return ord($code) - ord("a") if $code =~ /[a-z]/;
32 35 50       118 return ord($code) - ord("0") + 26 if $code =~ /[0-9]/;
33 0         0 return;
34             }
35              
36             sub _code_point {
37 309     309   275 my $digit = shift;
38 309 100 66     698 return $digit + ord('a') if 0 <= $digit && $digit <= 25;
39 36 50 33     78 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   347 my($delta, $numpoints, $firsttime) = @_;
45 296 100       312 $delta = $firsttime ? $delta / DAMP : $delta / 2;
46 296         261 $delta += $delta / $numpoints;
47 296         253 my $k = 0;
48 296         362 while ($delta > ((BASE - TMIN) * TMAX) / 2) {
49 22         21 $delta /= BASE - TMIN;
50 22         37 $k += BASE;
51             }
52 296         315 return $k + (((BASE - TMIN + 1) * $delta) / ($delta + SKEW));
53             }
54              
55             sub decode_punycode {
56 17     17 1 35 my $code = shift;
57              
58 17         19 my $n = INITIAL_N;
59 17         17 my $i = 0;
60 17         19 my $bias = INITIAL_BIAS;
61 17         18 my @output;
62              
63 17 100       122 if ($code =~ s/(.*)$Delimiter//o) {
64 11         67 push @output, map ord, split //, $1;
65 11 50       97 return _croak('non-basic code point') unless $1 =~ /^$BasicRE*$/o;
66             }
67              
68 17         32 while ($code) {
69 80         72 my $oldi = $i;
70 80         77 my $w = 1;
71             LOOP:
72 80         75 for (my $k = BASE; 1; $k += BASE) {
73 158         156 my $cp = substr($code, 0, 1, '');
74 158         167 my $digit = _digit_value($cp);
75 158 50       189 defined $digit or return _croak("invalid punycode input");
76 158         138 $i += $digit * $w;
77 158 100       235 my $t = ($k <= $bias) ? TMIN
    100          
78             : ($k >= $bias + TMAX) ? TMAX : $k - $bias;
79 158 100       194 last LOOP if $digit < $t;
80 78         83 $w *= (BASE - $t);
81             }
82 80         108 $bias = _adapt($i - $oldi, @output + 1, $oldi == 0);
83 80 50       102 warn "bias becomes $bias" if $DEBUG;
84 80         104 $n += $i / (@output + 1);
85 80         83 $i = $i % (@output + 1);
86 80         108 splice(@output, $i, 0, $n);
87 80 50       98 warn join " ", map sprintf('%04x', $_), @output if $DEBUG;
88 80         105 $i++;
89             }
90 17         168 return join '', map chr, @output;
91             }
92              
93             sub encode_punycode {
94 25     25 1 313808 my $input = shift;
95 25         83 my @input = split //, $input;
96              
97 25         28 my $n = INITIAL_N;
98 25         27 my $delta = 0;
99 25         29 my $bias = INITIAL_BIAS;
100              
101 25         29 my @output;
102 25         335 my @basic = grep /$BasicRE/, @input;
103 25         42 my $h = my $b = @basic;
104 25         45 push @output, @basic;
105 25 100 100     91 push @output, $Delimiter if $b && $h < @input;
106 25 50       42 warn "basic codepoints: (@output)" if $DEBUG;
107              
108 25         45 while ($h < @input) {
109 76         249 my $m = _min(grep { $_ >= $n } map ord, @input);
  1275         1377  
110 76 50       108 warn sprintf "next code point to insert is %04x", $m if $DEBUG;
111 76         87 $delta += ($m - $n) * ($h + 1);
112 76         70 $n = $m;
113 76         78 for my $i (@input) {
114 1275         1119 my $c = ord($i);
115 1275 100       1334 $delta++ if $c < $n;
116 1275 100       1457 if ($c == $n) {
117 216         179 my $q = $delta;
118             LOOP:
119 216         181 for (my $k = BASE; 1; $k += BASE) {
120 309 100       384 my $t = ($k <= $bias) ? TMIN :
    100          
121             ($k >= $bias + TMAX) ? TMAX : $k - $bias;
122 309 100       370 last LOOP if $q < $t;
123 93         143 my $cp = _code_point($t + (($q - $t) % (BASE - $t)));
124 93         134 push @output, chr($cp);
125 93         101 $q = ($q - $t) / (BASE - $t);
126             }
127 216         211 push @output, chr(_code_point($q));
128 216         277 $bias = _adapt($delta, $h + 1, $h == $b);
129 216 50       243 warn "bias becomes $bias" if $DEBUG;
130 216         215 $delta = 0;
131 216         211 $h++;
132             }
133             }
134 76         87 $delta++;
135 76         96 $n++;
136             }
137 25         154 return join '', @output;
138             }
139              
140             sub _min {
141 76     76   76 my $min = shift;
142 76 100       97 for (@_) { $min = $_ if $_ <= $min }
  490         550  
143 76         79 return $min;
144             }
145              
146             1;
147             __END__