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