| 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__ |