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   68680 use strict;
  4         17  
  4         125  
4 4     4   21 use warnings;
  4         10  
  4         173  
5              
6             our $VERSION = '5.19';
7              
8 4     4   20 use Exporter 'import';
  4         8  
  4         239  
9             our @EXPORT = qw(encode_punycode decode_punycode);
10              
11 4     4   2105 use integer;
  4         57  
  4         27  
12              
13             our $DEBUG = 0;
14              
15 4     4   194 use constant BASE => 36;
  4         13  
  4         441  
16 4     4   24 use constant TMIN => 1;
  4         14  
  4         188  
17 4     4   23 use constant TMAX => 26;
  4         4  
  4         163  
18 4     4   25 use constant SKEW => 38;
  4         9  
  4         151  
19 4     4   21 use constant DAMP => 700;
  4         8  
  4         169  
20 4     4   22 use constant INITIAL_BIAS => 72;
  4         7  
  4         205  
21 4     4   23 use constant INITIAL_N => 128;
  4         7  
  4         5111  
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   202 my $code = shift;
30 158 100       312 return ord($code) - ord("A") if $code =~ /[A-Z]/;
31 155 100       347 return ord($code) - ord("a") if $code =~ /[a-z]/;
32 35 50       101 return ord($code) - ord("0") + 26 if $code =~ /[0-9]/;
33 0         0 return;
34             }
35              
36             sub _code_point {
37 309     309   371 my $digit = shift;
38 309 100 66     1036 return $digit + ord('a') if 0 <= $digit && $digit <= 25;
39 36 50 33     121 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   469 my($delta, $numpoints, $firsttime) = @_;
45 296 100       448 $delta = $firsttime ? $delta / DAMP : $delta / 2;
46 296         373 $delta += $delta / $numpoints;
47 296         330 my $k = 0;
48 296         497 while ($delta > ((BASE - TMIN) * TMAX) / 2) {
49 22         28 $delta /= BASE - TMIN;
50 22         38 $k += BASE;
51             }
52 296         463 return $k + (((BASE - TMIN + 1) * $delta) / ($delta + SKEW));
53             }
54              
55             sub decode_punycode {
56 17     17 1 60 my $code = shift;
57              
58 17         25 my $n = INITIAL_N;
59 17         23 my $i = 0;
60 17         25 my $bias = INITIAL_BIAS;
61 17         23 my @output;
62              
63 17 100       106 if ($code =~ s/(.*)$Delimiter//o) {
64 11         97 push @output, map ord, split //, $1;
65 11 50       130 return _croak('non-basic code point') unless $1 =~ /^$BasicRE*$/o;
66             }
67              
68 17         47 while ($code) {
69 80         103 my $oldi = $i;
70 80         98 my $w = 1;
71             LOOP:
72 80         114 for (my $k = BASE; 1; $k += BASE) {
73 158         263 my $cp = substr($code, 0, 1, '');
74 158         236 my $digit = _digit_value($cp);
75 158 50       288 defined $digit or return _croak("invalid punycode input");
76 158         195 $i += $digit * $w;
77 158 100       317 my $t = ($k <= $bias) ? TMIN
    100          
78             : ($k >= $bias + TMAX) ? TMAX : $k - $bias;
79 158 100       280 last LOOP if $digit < $t;
80 78         111 $w *= (BASE - $t);
81             }
82 80         159 $bias = _adapt($i - $oldi, @output + 1, $oldi == 0);
83 80 50       144 warn "bias becomes $bias" if $DEBUG;
84 80         109 $n += $i / (@output + 1);
85 80         105 $i = $i % (@output + 1);
86 80         127 splice(@output, $i, 0, $n);
87 80 50       144 warn join " ", map sprintf('%04x', $_), @output if $DEBUG;
88 80         150 $i++;
89             }
90 17         186 return join '', map chr, @output;
91             }
92              
93             sub encode_punycode {
94 25     25 1 3830 my $input = shift;
95 25         106 my @input = split //, $input;
96              
97 25         39 my $n = INITIAL_N;
98 25         33 my $delta = 0;
99 25         34 my $bias = INITIAL_BIAS;
100              
101 25         35 my @output;
102 25         337 my @basic = grep /$BasicRE/, @input;
103 25         53 my $h = my $b = @basic;
104 25         54 push @output, @basic;
105 25 100 100     108 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         355 my $m = _min(grep { $_ >= $n } map ord, @input);
  1275         1949  
110 76 50       162 warn sprintf "next code point to insert is %04x", $m if $DEBUG;
111 76         114 $delta += ($m - $n) * ($h + 1);
112 76         87 $n = $m;
113 76         104 for my $i (@input) {
114 1275         1507 my $c = ord($i);
115 1275 100       1909 $delta++ if $c < $n;
116 1275 100       1961 if ($c == $n) {
117 216         264 my $q = $delta;
118             LOOP:
119 216         272 for (my $k = BASE; 1; $k += BASE) {
120 309 100       561 my $t = ($k <= $bias) ? TMIN :
    100          
121             ($k >= $bias + TMAX) ? TMAX : $k - $bias;
122 309 100       532 last LOOP if $q < $t;
123 93         165 my $cp = _code_point($t + (($q - $t) % (BASE - $t)));
124 93         185 push @output, chr($cp);
125 93         143 $q = ($q - $t) / (BASE - $t);
126             }
127 216         309 push @output, chr(_code_point($q));
128 216         391 $bias = _adapt($delta, $h + 1, $h == $b);
129 216 50       373 warn "bias becomes $bias" if $DEBUG;
130 216         255 $delta = 0;
131 216         314 $h++;
132             }
133             }
134 76         86 $delta++;
135 76         127 $n++;
136             }
137 25         174 return join '', @output;
138             }
139              
140             sub _min {
141 76     76   111 my $min = shift;
142 76 100       125 for (@_) { $min = $_ if $_ <= $min }
  490         804  
143 76         143 return $min;
144             }
145              
146             1;
147             __END__