podman
1// Copyright 2015 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
5package runes6
7import (8"unicode/utf8"9
10"golang.org/x/text/transform"11)
12
13// Note: below we pass invalid UTF-8 to the tIn and tNotIn transformers as is.
14// This is done for various reasons:
15// - To retain the semantics of the Nop transformer: if input is passed to a Nop
16// one would expect it to be unchanged.
17// - It would be very expensive to pass a converted RuneError to a transformer:
18// a transformer might need more source bytes after RuneError, meaning that
19// the only way to pass it safely is to create a new buffer and manage the
20// intermingling of RuneErrors and normal input.
21// - Many transformers leave ill-formed UTF-8 as is, so this is not
22// inconsistent. Generally ill-formed UTF-8 is only replaced if it is a
23// logical consequence of the operation (as for Map) or if it otherwise would
24// pose security concerns (as for Remove).
25// - An alternative would be to return an error on ill-formed UTF-8, but this
26// would be inconsistent with other operations.
27
28// If returns a transformer that applies tIn to consecutive runes for which
29// s.Contains(r) and tNotIn to consecutive runes for which !s.Contains(r). Reset
30// is called on tIn and tNotIn at the start of each run. A Nop transformer will
31// substitute a nil value passed to tIn or tNotIn. Invalid UTF-8 is translated
32// to RuneError to determine which transformer to apply, but is passed as is to
33// the respective transformer.
34func If(s Set, tIn, tNotIn transform.Transformer) Transformer {35if tIn == nil && tNotIn == nil {36return Transformer{transform.Nop}37}38if tIn == nil {39tIn = transform.Nop40}41if tNotIn == nil {42tNotIn = transform.Nop43}44sIn, ok := tIn.(transform.SpanningTransformer)45if !ok {46sIn = dummySpan{tIn}47}48sNotIn, ok := tNotIn.(transform.SpanningTransformer)49if !ok {50sNotIn = dummySpan{tNotIn}51}52
53a := &cond{54tIn: sIn,55tNotIn: sNotIn,56f: s.Contains,57}58a.Reset()59return Transformer{a}60}
61
62type dummySpan struct{ transform.Transformer }63
64func (d dummySpan) Span(src []byte, atEOF bool) (n int, err error) {65return 0, transform.ErrEndOfSpan66}
67
68type cond struct {69tIn, tNotIn transform.SpanningTransformer70f func(rune) bool71check func(rune) bool // current check to perform72t transform.SpanningTransformer // current transformer to use73}
74
75// Reset implements transform.Transformer.
76func (t *cond) Reset() {77t.check = t.is78t.t = t.tIn79t.t.Reset() // notIn will be reset on first usage.80}
81
82func (t *cond) is(r rune) bool {83if t.f(r) {84return true85}86t.check = t.isNot87t.t = t.tNotIn88t.tNotIn.Reset()89return false90}
91
92func (t *cond) isNot(r rune) bool {93if !t.f(r) {94return true95}96t.check = t.is97t.t = t.tIn98t.tIn.Reset()99return false100}
101
102// This implementation of Span doesn't help all too much, but it needs to be
103// there to satisfy this package's Transformer interface.
104// TODO: there are certainly room for improvements, though. For example, if
105// t.t == transform.Nop (which will a common occurrence) it will save a bundle
106// to special-case that loop.
107func (t *cond) Span(src []byte, atEOF bool) (n int, err error) {108p := 0109for n < len(src) && err == nil {110// Don't process too much at a time as the Spanner that will be111// called on this block may terminate early.112const maxChunk = 4096113max := len(src)114if v := n + maxChunk; v < max {115max = v116}117atEnd := false118size := 0119current := t.t120for ; p < max; p += size {121r := rune(src[p])122if r < utf8.RuneSelf {123size = 1124} else if r, size = utf8.DecodeRune(src[p:]); size == 1 {125if !atEOF && !utf8.FullRune(src[p:]) {126err = transform.ErrShortSrc127break128}129}130if !t.check(r) {131// The next rune will be the start of a new run.132atEnd = true133break134}135}136n2, err2 := current.Span(src[n:p], atEnd || (atEOF && p == len(src)))137n += n2138if err2 != nil {139return n, err2140}141// At this point either err != nil or t.check will pass for the rune at p.142p = n + size143}144return n, err145}
146
147func (t *cond) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {148p := 0149for nSrc < len(src) && err == nil {150// Don't process too much at a time, as the work might be wasted if the151// destination buffer isn't large enough to hold the result or a152// transform returns an error early.153const maxChunk = 4096154max := len(src)155if n := nSrc + maxChunk; n < len(src) {156max = n157}158atEnd := false159size := 0160current := t.t161for ; p < max; p += size {162r := rune(src[p])163if r < utf8.RuneSelf {164size = 1165} else if r, size = utf8.DecodeRune(src[p:]); size == 1 {166if !atEOF && !utf8.FullRune(src[p:]) {167err = transform.ErrShortSrc168break169}170}171if !t.check(r) {172// The next rune will be the start of a new run.173atEnd = true174break175}176}177nDst2, nSrc2, err2 := current.Transform(dst[nDst:], src[nSrc:p], atEnd || (atEOF && p == len(src)))178nDst += nDst2179nSrc += nSrc2180if err2 != nil {181return nDst, nSrc, err2182}183// At this point either err != nil or t.check will pass for the rune at p.184p = nSrc + size185}186return nDst, nSrc, err187}
188