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  }