Path: blob/main/vendor/golang.org/x/text/runes/cond.go
3548 views
// Copyright 2015 The Go Authors. All rights reserved.1// Use of this source code is governed by a BSD-style2// license that can be found in the LICENSE file.34package runes56import (7"unicode/utf8"89"golang.org/x/text/transform"10)1112// Note: below we pass invalid UTF-8 to the tIn and tNotIn transformers as is.13// This is done for various reasons:14// - To retain the semantics of the Nop transformer: if input is passed to a Nop15// one would expect it to be unchanged.16// - It would be very expensive to pass a converted RuneError to a transformer:17// a transformer might need more source bytes after RuneError, meaning that18// the only way to pass it safely is to create a new buffer and manage the19// intermingling of RuneErrors and normal input.20// - Many transformers leave ill-formed UTF-8 as is, so this is not21// inconsistent. Generally ill-formed UTF-8 is only replaced if it is a22// logical consequence of the operation (as for Map) or if it otherwise would23// pose security concerns (as for Remove).24// - An alternative would be to return an error on ill-formed UTF-8, but this25// would be inconsistent with other operations.2627// If returns a transformer that applies tIn to consecutive runes for which28// s.Contains(r) and tNotIn to consecutive runes for which !s.Contains(r). Reset29// is called on tIn and tNotIn at the start of each run. A Nop transformer will30// substitute a nil value passed to tIn or tNotIn. Invalid UTF-8 is translated31// to RuneError to determine which transformer to apply, but is passed as is to32// the respective transformer.33func If(s Set, tIn, tNotIn transform.Transformer) Transformer {34if tIn == nil && tNotIn == nil {35return Transformer{transform.Nop}36}37if tIn == nil {38tIn = transform.Nop39}40if tNotIn == nil {41tNotIn = transform.Nop42}43sIn, ok := tIn.(transform.SpanningTransformer)44if !ok {45sIn = dummySpan{tIn}46}47sNotIn, ok := tNotIn.(transform.SpanningTransformer)48if !ok {49sNotIn = dummySpan{tNotIn}50}5152a := &cond{53tIn: sIn,54tNotIn: sNotIn,55f: s.Contains,56}57a.Reset()58return Transformer{a}59}6061type dummySpan struct{ transform.Transformer }6263func (d dummySpan) Span(src []byte, atEOF bool) (n int, err error) {64return 0, transform.ErrEndOfSpan65}6667type cond struct {68tIn, tNotIn transform.SpanningTransformer69f func(rune) bool70check func(rune) bool // current check to perform71t transform.SpanningTransformer // current transformer to use72}7374// Reset implements transform.Transformer.75func (t *cond) Reset() {76t.check = t.is77t.t = t.tIn78t.t.Reset() // notIn will be reset on first usage.79}8081func (t *cond) is(r rune) bool {82if t.f(r) {83return true84}85t.check = t.isNot86t.t = t.tNotIn87t.tNotIn.Reset()88return false89}9091func (t *cond) isNot(r rune) bool {92if !t.f(r) {93return true94}95t.check = t.is96t.t = t.tIn97t.tIn.Reset()98return false99}100101// This implementation of Span doesn't help all too much, but it needs to be102// there to satisfy this package's Transformer interface.103// TODO: there are certainly room for improvements, though. For example, if104// t.t == transform.Nop (which will a common occurrence) it will save a bundle105// to special-case that loop.106func (t *cond) Span(src []byte, atEOF bool) (n int, err error) {107p := 0108for n < len(src) && err == nil {109// Don't process too much at a time as the Spanner that will be110// called on this block may terminate early.111const maxChunk = 4096112max := len(src)113if v := n + maxChunk; v < max {114max = v115}116atEnd := false117size := 0118current := t.t119for ; p < max; p += size {120r := rune(src[p])121if r < utf8.RuneSelf {122size = 1123} else if r, size = utf8.DecodeRune(src[p:]); size == 1 {124if !atEOF && !utf8.FullRune(src[p:]) {125err = transform.ErrShortSrc126break127}128}129if !t.check(r) {130// The next rune will be the start of a new run.131atEnd = true132break133}134}135n2, err2 := current.Span(src[n:p], atEnd || (atEOF && p == len(src)))136n += n2137if err2 != nil {138return n, err2139}140// At this point either err != nil or t.check will pass for the rune at p.141p = n + size142}143return n, err144}145146func (t *cond) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {147p := 0148for nSrc < len(src) && err == nil {149// Don't process too much at a time, as the work might be wasted if the150// destination buffer isn't large enough to hold the result or a151// transform returns an error early.152const maxChunk = 4096153max := len(src)154if n := nSrc + maxChunk; n < len(src) {155max = n156}157atEnd := false158size := 0159current := t.t160for ; p < max; p += size {161r := rune(src[p])162if r < utf8.RuneSelf {163size = 1164} else if r, size = utf8.DecodeRune(src[p:]); size == 1 {165if !atEOF && !utf8.FullRune(src[p:]) {166err = transform.ErrShortSrc167break168}169}170if !t.check(r) {171// The next rune will be the start of a new run.172atEnd = true173break174}175}176nDst2, nSrc2, err2 := current.Transform(dst[nDst:], src[nSrc:p], atEnd || (atEOF && p == len(src)))177nDst += nDst2178nSrc += nSrc2179if err2 != nil {180return nDst, nSrc, err2181}182// At this point either err != nil or t.check will pass for the rune at p.183p = nSrc + size184}185return nDst, nSrc, err186}187188189