github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/x/text/runes/cond.go (about) 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 5 package runes 6 7 import ( 8 "unicode/utf8" 9 10 "github.com/insionng/yougam/libraries/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. 34 func If(s Set, tIn, tNotIn transform.Transformer) Transformer { 35 if tIn == nil && tNotIn == nil { 36 return Transformer{transform.Nop} 37 } 38 if tIn == nil { 39 tIn = transform.Nop 40 } 41 if tNotIn == nil { 42 tNotIn = transform.Nop 43 } 44 a := &cond{ 45 tIn: tIn, 46 tNotIn: tNotIn, 47 f: s.Contains, 48 } 49 a.Reset() 50 return Transformer{a} 51 } 52 53 type cond struct { 54 tIn, tNotIn transform.Transformer 55 f func(rune) bool 56 check func(rune) bool // current check to perform 57 t transform.Transformer // current transformer to use 58 } 59 60 // Reset implements transform.Transformer. 61 func (t *cond) Reset() { 62 t.check = t.is 63 t.t = t.tIn 64 t.t.Reset() // notIn will be reset on first usage. 65 } 66 67 func (t *cond) is(r rune) bool { 68 if t.f(r) { 69 return true 70 } 71 t.check = t.isNot 72 t.t = t.tNotIn 73 t.tNotIn.Reset() 74 return false 75 } 76 77 func (t *cond) isNot(r rune) bool { 78 if !t.f(r) { 79 return true 80 } 81 t.check = t.is 82 t.t = t.tIn 83 t.tIn.Reset() 84 return false 85 } 86 87 func (t *cond) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 88 p := 0 89 for nSrc < len(src) && err == nil { 90 // Don't process too much at a time, as the work might be wasted if the 91 // destination buffer isn't large enough to hold the result or a 92 // transform returns an error early. 93 const maxChunk = 4096 94 max := len(src) 95 if n := nSrc + maxChunk; n < len(src) { 96 max = n 97 } 98 atEnd := false 99 size := 0 100 current := t.t 101 for ; p < max; p += size { 102 var r rune 103 r, size = utf8.DecodeRune(src[p:]) 104 if r == utf8.RuneError && size == 1 { 105 if !atEOF && !utf8.FullRune(src[p:]) { 106 err = transform.ErrShortSrc 107 break 108 } 109 } 110 if !t.check(r) { 111 // The next rune will be the start of a new run. 112 atEnd = true 113 break 114 } 115 } 116 nDst2, nSrc2, err2 := current.Transform(dst[nDst:], src[nSrc:p], atEnd || (atEOF && p == len(src))) 117 nDst += nDst2 118 nSrc += nSrc2 119 if err2 != nil { 120 return nDst, nSrc, err2 121 } 122 // At this point either err != nil or t.check will pass for the rune at p. 123 p = nSrc + size 124 } 125 return nDst, nSrc, err 126 }