github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/net/http/cookiejar/punycode.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cookiejar 6 7 // This file implements the Punycode algorithm from RFC 3492. 8 9 import ( 10 "fmt" 11 "net/http/internal/ascii" 12 "strings" 13 "unicode/utf8" 14 ) 15 16 // These parameter values are specified in section 5. 17 // 18 // All computation is done with int32s, so that overflow behavior is identical 19 // regardless of whether int is 32-bit or 64-bit. 20 const ( 21 base int32 = 36 22 damp int32 = 700 23 initialBias int32 = 72 24 initialN int32 = 128 25 skew int32 = 38 26 tmax int32 = 26 27 tmin int32 = 1 28 ) 29 30 // encode encodes a string as specified in section 6.3 and prepends prefix to 31 // the result. 32 // 33 // The "while h < length(input)" line in the specification becomes "for 34 // remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes. 35 func encode(prefix, s string) (string, error) { 36 output := make([]byte, len(prefix), len(prefix)+1+2*len(s)) 37 copy(output, prefix) 38 delta, n, bias := int32(0), initialN, initialBias 39 b, remaining := int32(0), int32(0) 40 for _, r := range s { 41 if r < utf8.RuneSelf { 42 b++ 43 output = append(output, byte(r)) 44 } else { 45 remaining++ 46 } 47 } 48 h := b 49 if b > 0 { 50 output = append(output, '-') 51 } 52 for remaining != 0 { 53 m := int32(0x7fffffff) 54 for _, r := range s { 55 if m > r && r >= n { 56 m = r 57 } 58 } 59 delta += (m - n) * (h + 1) 60 if delta < 0 { 61 return "", fmt.Errorf("cookiejar: invalid label %q", s) 62 } 63 n = m 64 for _, r := range s { 65 if r < n { 66 delta++ 67 if delta < 0 { 68 return "", fmt.Errorf("cookiejar: invalid label %q", s) 69 } 70 continue 71 } 72 if r > n { 73 continue 74 } 75 q := delta 76 for k := base; ; k += base { 77 t := k - bias 78 if t < tmin { 79 t = tmin 80 } else if t > tmax { 81 t = tmax 82 } 83 if q < t { 84 break 85 } 86 output = append(output, encodeDigit(t+(q-t)%(base-t))) 87 q = (q - t) / (base - t) 88 } 89 output = append(output, encodeDigit(q)) 90 bias = adapt(delta, h+1, h == b) 91 delta = 0 92 h++ 93 remaining-- 94 } 95 delta++ 96 n++ 97 } 98 return string(output), nil 99 } 100 101 func encodeDigit(digit int32) byte { 102 switch { 103 case 0 <= digit && digit < 26: 104 return byte(digit + 'a') 105 case 26 <= digit && digit < 36: 106 return byte(digit + ('0' - 26)) 107 } 108 panic("cookiejar: internal error in punycode encoding") 109 } 110 111 // adapt is the bias adaptation function specified in section 6.1. 112 func adapt(delta, numPoints int32, firstTime bool) int32 { 113 if firstTime { 114 delta /= damp 115 } else { 116 delta /= 2 117 } 118 delta += delta / numPoints 119 k := int32(0) 120 for delta > ((base-tmin)*tmax)/2 { 121 delta /= base - tmin 122 k += base 123 } 124 return k + (base-tmin+1)*delta/(delta+skew) 125 } 126 127 // Strictly speaking, the remaining code below deals with IDNA (RFC 5890 and 128 // friends) and not Punycode (RFC 3492) per se. 129 130 // acePrefix is the ASCII Compatible Encoding prefix. 131 const acePrefix = "xn--" 132 133 // toASCII converts a domain or domain label to its ASCII form. For example, 134 // toASCII("bücher.example.com") is "xn--bcher-kva.example.com", and 135 // toASCII("golang") is "golang". 136 func toASCII(s string) (string, error) { 137 if ascii.Is(s) { 138 return s, nil 139 } 140 labels := strings.Split(s, ".") 141 for i, label := range labels { 142 if !ascii.Is(label) { 143 a, err := encode(acePrefix, label) 144 if err != nil { 145 return "", err 146 } 147 labels[i] = a 148 } 149 } 150 return strings.Join(labels, "."), nil 151 }