92 lines
1.6 KiB
Go
92 lines
1.6 KiB
Go
package utf7
|
|
|
|
import (
|
|
"unicode/utf16"
|
|
"unicode/utf8"
|
|
|
|
"golang.org/x/text/transform"
|
|
)
|
|
|
|
type encoder struct{}
|
|
|
|
func (e *encoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
|
for i := 0; i < len(src); {
|
|
ch := src[i]
|
|
|
|
var b []byte
|
|
if min <= ch && ch <= max {
|
|
b = []byte{ch}
|
|
if ch == '&' {
|
|
b = append(b, '-')
|
|
}
|
|
|
|
i++
|
|
} else {
|
|
start := i
|
|
|
|
// Find the next printable ASCII code point
|
|
i++
|
|
for i < len(src) && (src[i] < min || src[i] > max) {
|
|
i++
|
|
}
|
|
|
|
if !atEOF && i == len(src) {
|
|
err = transform.ErrShortSrc
|
|
return
|
|
}
|
|
|
|
b = encode(src[start:i])
|
|
}
|
|
|
|
if nDst+len(b) > len(dst) {
|
|
err = transform.ErrShortDst
|
|
return
|
|
}
|
|
|
|
nSrc = i
|
|
|
|
for _, ch := range b {
|
|
dst[nDst] = ch
|
|
nDst++
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (e *encoder) Reset() {}
|
|
|
|
// Converts string s from UTF-8 to UTF-16-BE, encodes the result as base64,
|
|
// removes the padding, and adds UTF-7 shifts.
|
|
func encode(s []byte) []byte {
|
|
// len(s) is sufficient for UTF-8 to UTF-16 conversion if there are no
|
|
// control code points (see table below).
|
|
b := make([]byte, 0, len(s)+4)
|
|
for len(s) > 0 {
|
|
r, size := utf8.DecodeRune(s)
|
|
if r > utf8.MaxRune {
|
|
r, size = utf8.RuneError, 1 // Bug fix (issue 3785)
|
|
}
|
|
s = s[size:]
|
|
if r1, r2 := utf16.EncodeRune(r); r1 != repl {
|
|
b = append(b, byte(r1>>8), byte(r1))
|
|
r = r2
|
|
}
|
|
b = append(b, byte(r>>8), byte(r))
|
|
}
|
|
|
|
// Encode as base64
|
|
n := b64Enc.EncodedLen(len(b)) + 2
|
|
b64 := make([]byte, n)
|
|
b64Enc.Encode(b64[1:], b)
|
|
|
|
// Strip padding
|
|
n -= 2 - (len(b)+2)%3
|
|
b64 = b64[:n]
|
|
|
|
// Add UTF-7 shifts
|
|
b64[0] = '&'
|
|
b64[n-1] = '-'
|
|
return b64
|
|
}
|