mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-15 01:34:05 +00:00
c24bf7ea55
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4620
356 lines
8.5 KiB
Go
356 lines
8.5 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package runes provide transforms for UTF-8 encoded text.
|
|
package runes // import "golang.org/x/text/runes"
|
|
|
|
import (
|
|
"unicode"
|
|
"unicode/utf8"
|
|
|
|
"golang.org/x/text/transform"
|
|
)
|
|
|
|
// A Set is a collection of runes.
|
|
type Set interface {
|
|
// Contains returns true if r is contained in the set.
|
|
Contains(r rune) bool
|
|
}
|
|
|
|
type setFunc func(rune) bool
|
|
|
|
func (s setFunc) Contains(r rune) bool {
|
|
return s(r)
|
|
}
|
|
|
|
// Note: using funcs here instead of wrapping types result in cleaner
|
|
// documentation and a smaller API.
|
|
|
|
// In creates a Set with a Contains method that returns true for all runes in
|
|
// the given RangeTable.
|
|
func In(rt *unicode.RangeTable) Set {
|
|
return setFunc(func(r rune) bool { return unicode.Is(rt, r) })
|
|
}
|
|
|
|
// In creates a Set with a Contains method that returns true for all runes not
|
|
// in the given RangeTable.
|
|
func NotIn(rt *unicode.RangeTable) Set {
|
|
return setFunc(func(r rune) bool { return !unicode.Is(rt, r) })
|
|
}
|
|
|
|
// Predicate creates a Set with a Contains method that returns f(r).
|
|
func Predicate(f func(rune) bool) Set {
|
|
return setFunc(f)
|
|
}
|
|
|
|
// Transformer implements the transform.Transformer interface.
|
|
type Transformer struct {
|
|
t transform.SpanningTransformer
|
|
}
|
|
|
|
func (t Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
|
return t.t.Transform(dst, src, atEOF)
|
|
}
|
|
|
|
func (t Transformer) Span(b []byte, atEOF bool) (n int, err error) {
|
|
return t.t.Span(b, atEOF)
|
|
}
|
|
|
|
func (t Transformer) Reset() { t.t.Reset() }
|
|
|
|
// Bytes returns a new byte slice with the result of converting b using t. It
|
|
// calls Reset on t. It returns nil if any error was found. This can only happen
|
|
// if an error-producing Transformer is passed to If.
|
|
func (t Transformer) Bytes(b []byte) []byte {
|
|
b, _, err := transform.Bytes(t, b)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return b
|
|
}
|
|
|
|
// String returns a string with the result of converting s using t. It calls
|
|
// Reset on t. It returns the empty string if any error was found. This can only
|
|
// happen if an error-producing Transformer is passed to If.
|
|
func (t Transformer) String(s string) string {
|
|
s, _, err := transform.String(t, s)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return s
|
|
}
|
|
|
|
// TODO:
|
|
// - Copy: copying strings and bytes in whole-rune units.
|
|
// - Validation (maybe)
|
|
// - Well-formed-ness (maybe)
|
|
|
|
const runeErrorString = string(utf8.RuneError)
|
|
|
|
// Remove returns a Transformer that removes runes r for which s.Contains(r).
|
|
// Illegal input bytes are replaced by RuneError before being passed to f.
|
|
func Remove(s Set) Transformer {
|
|
if f, ok := s.(setFunc); ok {
|
|
// This little trick cuts the running time of BenchmarkRemove for sets
|
|
// created by Predicate roughly in half.
|
|
// TODO: special-case RangeTables as well.
|
|
return Transformer{remove(f)}
|
|
}
|
|
return Transformer{remove(s.Contains)}
|
|
}
|
|
|
|
// TODO: remove transform.RemoveFunc.
|
|
|
|
type remove func(r rune) bool
|
|
|
|
func (remove) Reset() {}
|
|
|
|
// Span implements transform.Spanner.
|
|
func (t remove) Span(src []byte, atEOF bool) (n int, err error) {
|
|
for r, size := rune(0), 0; n < len(src); {
|
|
if r = rune(src[n]); r < utf8.RuneSelf {
|
|
size = 1
|
|
} else if r, size = utf8.DecodeRune(src[n:]); size == 1 {
|
|
// Invalid rune.
|
|
if !atEOF && !utf8.FullRune(src[n:]) {
|
|
err = transform.ErrShortSrc
|
|
} else {
|
|
err = transform.ErrEndOfSpan
|
|
}
|
|
break
|
|
}
|
|
if t(r) {
|
|
err = transform.ErrEndOfSpan
|
|
break
|
|
}
|
|
n += size
|
|
}
|
|
return
|
|
}
|
|
|
|
// Transform implements transform.Transformer.
|
|
func (t remove) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
|
for r, size := rune(0), 0; nSrc < len(src); {
|
|
if r = rune(src[nSrc]); r < utf8.RuneSelf {
|
|
size = 1
|
|
} else if r, size = utf8.DecodeRune(src[nSrc:]); size == 1 {
|
|
// Invalid rune.
|
|
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
|
err = transform.ErrShortSrc
|
|
break
|
|
}
|
|
// We replace illegal bytes with RuneError. Not doing so might
|
|
// otherwise turn a sequence of invalid UTF-8 into valid UTF-8.
|
|
// The resulting byte sequence may subsequently contain runes
|
|
// for which t(r) is true that were passed unnoticed.
|
|
if !t(utf8.RuneError) {
|
|
if nDst+3 > len(dst) {
|
|
err = transform.ErrShortDst
|
|
break
|
|
}
|
|
dst[nDst+0] = runeErrorString[0]
|
|
dst[nDst+1] = runeErrorString[1]
|
|
dst[nDst+2] = runeErrorString[2]
|
|
nDst += 3
|
|
}
|
|
nSrc++
|
|
continue
|
|
}
|
|
if t(r) {
|
|
nSrc += size
|
|
continue
|
|
}
|
|
if nDst+size > len(dst) {
|
|
err = transform.ErrShortDst
|
|
break
|
|
}
|
|
for i := 0; i < size; i++ {
|
|
dst[nDst] = src[nSrc]
|
|
nDst++
|
|
nSrc++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Map returns a Transformer that maps the runes in the input using the given
|
|
// mapping. Illegal bytes in the input are converted to utf8.RuneError before
|
|
// being passed to the mapping func.
|
|
func Map(mapping func(rune) rune) Transformer {
|
|
return Transformer{mapper(mapping)}
|
|
}
|
|
|
|
type mapper func(rune) rune
|
|
|
|
func (mapper) Reset() {}
|
|
|
|
// Span implements transform.Spanner.
|
|
func (t mapper) Span(src []byte, atEOF bool) (n int, err error) {
|
|
for r, size := rune(0), 0; n < len(src); n += size {
|
|
if r = rune(src[n]); r < utf8.RuneSelf {
|
|
size = 1
|
|
} else if r, size = utf8.DecodeRune(src[n:]); size == 1 {
|
|
// Invalid rune.
|
|
if !atEOF && !utf8.FullRune(src[n:]) {
|
|
err = transform.ErrShortSrc
|
|
} else {
|
|
err = transform.ErrEndOfSpan
|
|
}
|
|
break
|
|
}
|
|
if t(r) != r {
|
|
err = transform.ErrEndOfSpan
|
|
break
|
|
}
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
// Transform implements transform.Transformer.
|
|
func (t mapper) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
|
var replacement rune
|
|
var b [utf8.UTFMax]byte
|
|
|
|
for r, size := rune(0), 0; nSrc < len(src); {
|
|
if r = rune(src[nSrc]); r < utf8.RuneSelf {
|
|
if replacement = t(r); replacement < utf8.RuneSelf {
|
|
if nDst == len(dst) {
|
|
err = transform.ErrShortDst
|
|
break
|
|
}
|
|
dst[nDst] = byte(replacement)
|
|
nDst++
|
|
nSrc++
|
|
continue
|
|
}
|
|
size = 1
|
|
} else if r, size = utf8.DecodeRune(src[nSrc:]); size == 1 {
|
|
// Invalid rune.
|
|
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
|
err = transform.ErrShortSrc
|
|
break
|
|
}
|
|
|
|
if replacement = t(utf8.RuneError); replacement == utf8.RuneError {
|
|
if nDst+3 > len(dst) {
|
|
err = transform.ErrShortDst
|
|
break
|
|
}
|
|
dst[nDst+0] = runeErrorString[0]
|
|
dst[nDst+1] = runeErrorString[1]
|
|
dst[nDst+2] = runeErrorString[2]
|
|
nDst += 3
|
|
nSrc++
|
|
continue
|
|
}
|
|
} else if replacement = t(r); replacement == r {
|
|
if nDst+size > len(dst) {
|
|
err = transform.ErrShortDst
|
|
break
|
|
}
|
|
for i := 0; i < size; i++ {
|
|
dst[nDst] = src[nSrc]
|
|
nDst++
|
|
nSrc++
|
|
}
|
|
continue
|
|
}
|
|
|
|
n := utf8.EncodeRune(b[:], replacement)
|
|
|
|
if nDst+n > len(dst) {
|
|
err = transform.ErrShortDst
|
|
break
|
|
}
|
|
for i := 0; i < n; i++ {
|
|
dst[nDst] = b[i]
|
|
nDst++
|
|
}
|
|
nSrc += size
|
|
}
|
|
return
|
|
}
|
|
|
|
// ReplaceIllFormed returns a transformer that replaces all input bytes that are
|
|
// not part of a well-formed UTF-8 code sequence with utf8.RuneError.
|
|
func ReplaceIllFormed() Transformer {
|
|
return Transformer{&replaceIllFormed{}}
|
|
}
|
|
|
|
type replaceIllFormed struct{ transform.NopResetter }
|
|
|
|
func (t replaceIllFormed) Span(src []byte, atEOF bool) (n int, err error) {
|
|
for n < len(src) {
|
|
// ASCII fast path.
|
|
if src[n] < utf8.RuneSelf {
|
|
n++
|
|
continue
|
|
}
|
|
|
|
r, size := utf8.DecodeRune(src[n:])
|
|
|
|
// Look for a valid non-ASCII rune.
|
|
if r != utf8.RuneError || size != 1 {
|
|
n += size
|
|
continue
|
|
}
|
|
|
|
// Look for short source data.
|
|
if !atEOF && !utf8.FullRune(src[n:]) {
|
|
err = transform.ErrShortSrc
|
|
break
|
|
}
|
|
|
|
// We have an invalid rune.
|
|
err = transform.ErrEndOfSpan
|
|
break
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
func (t replaceIllFormed) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
|
for nSrc < len(src) {
|
|
// ASCII fast path.
|
|
if r := src[nSrc]; r < utf8.RuneSelf {
|
|
if nDst == len(dst) {
|
|
err = transform.ErrShortDst
|
|
break
|
|
}
|
|
dst[nDst] = r
|
|
nDst++
|
|
nSrc++
|
|
continue
|
|
}
|
|
|
|
// Look for a valid non-ASCII rune.
|
|
if _, size := utf8.DecodeRune(src[nSrc:]); size != 1 {
|
|
if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
|
|
err = transform.ErrShortDst
|
|
break
|
|
}
|
|
nDst += size
|
|
nSrc += size
|
|
continue
|
|
}
|
|
|
|
// Look for short source data.
|
|
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
|
err = transform.ErrShortSrc
|
|
break
|
|
}
|
|
|
|
// We have an invalid rune.
|
|
if nDst+3 > len(dst) {
|
|
err = transform.ErrShortDst
|
|
break
|
|
}
|
|
dst[nDst+0] = runeErrorString[0]
|
|
dst[nDst+1] = runeErrorString[1]
|
|
dst[nDst+2] = runeErrorString[2]
|
|
nDst += 3
|
|
nSrc++
|
|
}
|
|
return nDst, nSrc, err
|
|
}
|