aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/golang.org/x/text/runes/cond.go
blob: ae7a921585b14ef3e7ccd3f6b2fe084111de5339 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright 2015 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

import (
    "unicode/utf8"

    "golang.org/x/text/transform"
)

// Note: below we pass invalid UTF-8 to the tIn and tNotIn transformers as is.
// This is done for various reasons:
// - To retain the semantics of the Nop transformer: if input is passed to a Nop
//   one would expect it to be unchanged.
// - It would be very expensive to pass a converted RuneError to a transformer:
//   a transformer might need more source bytes after RuneError, meaning that
//   the only way to pass it safely is to create a new buffer and manage the
//   intermingling of RuneErrors and normal input.
// - Many transformers leave ill-formed UTF-8 as is, so this is not
//   inconsistent. Generally ill-formed UTF-8 is only replaced if it is a
//   logical consequence of the operation (as for Map) or if it otherwise would
//   pose security concerns (as for Remove).
// - An alternative would be to return an error on ill-formed UTF-8, but this
//   would be inconsistent with other operations.

// If returns a transformer that applies tIn to consecutive runes for which
// s.Contains(r) and tNotIn to consecutive runes for which !s.Contains(r). Reset
// is called on tIn and tNotIn at the start of each run. A Nop transformer will
// substitute a nil value passed to tIn or tNotIn. Invalid UTF-8 is translated
// to RuneError to determine which transformer to apply, but is passed as is to
// the respective transformer.
func If(s Set, tIn, tNotIn transform.Transformer) Transformer {
    if tIn == nil && tNotIn == nil {
        return Transformer{transform.Nop}
    }
    if tIn == nil {
        tIn = transform.Nop
    }
    if tNotIn == nil {
        tNotIn = transform.Nop
    }
    a := &cond{
        tIn:    tIn,
        tNotIn: tNotIn,
        f:      s.Contains,
    }
    a.Reset()
    return Transformer{a}
}

type cond struct {
    tIn, tNotIn transform.Transformer
    f           func(rune) bool
    check       func(rune) bool       // current check to perform
    t           transform.Transformer // current transformer to use
}

// Reset implements transform.Transformer.
func (t *cond) Reset() {
    t.check = t.is
    t.t = t.tIn
    t.t.Reset() // notIn will be reset on first usage.
}

func (t *cond) is(r rune) bool {
    if t.f(r) {
        return true
    }
    t.check = t.isNot
    t.t = t.tNotIn
    t.tNotIn.Reset()
    return false
}

func (t *cond) isNot(r rune) bool {
    if !t.f(r) {
        return true
    }
    t.check = t.is
    t.t = t.tIn
    t.tIn.Reset()
    return false
}

func (t *cond) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
    p := 0
    for nSrc < len(src) && err == nil {
        // Don't process too much at a time, as the work might be wasted if the
        // destination buffer isn't large enough to hold the result or a
        // transform returns an error early.
        const maxChunk = 4096
        max := len(src)
        if n := nSrc + maxChunk; n < len(src) {
            max = n
        }
        atEnd := false
        size := 0
        current := t.t
        for ; p < max; p += size {
            var r rune
            r, size = utf8.DecodeRune(src[p:])
            if r == utf8.RuneError && size == 1 {
                if !atEOF && !utf8.FullRune(src[p:]) {
                    err = transform.ErrShortSrc
                    break
                }
            }
            if !t.check(r) {
                // The next rune will be the start of a new run.
                atEnd = true
                break
            }
        }
        nDst2, nSrc2, err2 := current.Transform(dst[nDst:], src[nSrc:p], atEnd || (atEOF && p == len(src)))
        nDst += nDst2
        nSrc += nSrc2
        if err2 != nil {
            return nDst, nSrc, err2
        }
        // At this point either err != nil or t.check will pass for the rune at p.
        p = nSrc + size
    }
    return nDst, nSrc, err
}