VOL-381 add unum container to support ONOS cluster formation under swarm
Change-Id: Ic260edda19bb199ed040f05164ab605f28c919d0
diff --git a/unum/vendor/golang.org/x/text/transform/examples_test.go b/unum/vendor/golang.org/x/text/transform/examples_test.go
new file mode 100644
index 0000000..f2e284d
--- /dev/null
+++ b/unum/vendor/golang.org/x/text/transform/examples_test.go
@@ -0,0 +1,37 @@
+// Copyright 2013 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 transform_test
+
+import (
+ "fmt"
+ "unicode"
+
+ "golang.org/x/text/transform"
+ "golang.org/x/text/unicode/norm"
+)
+
+func ExampleRemoveFunc() {
+ input := []byte(`tschüß; до свидания`)
+
+ b := make([]byte, len(input))
+
+ t := transform.RemoveFunc(unicode.IsSpace)
+ n, _, _ := t.Transform(b, input, true)
+ fmt.Println(string(b[:n]))
+
+ t = transform.RemoveFunc(func(r rune) bool {
+ return !unicode.Is(unicode.Latin, r)
+ })
+ n, _, _ = t.Transform(b, input, true)
+ fmt.Println(string(b[:n]))
+
+ n, _, _ = t.Transform(b, norm.NFD.Bytes(input), true)
+ fmt.Println(string(b[:n]))
+
+ // Output:
+ // tschüß;досвидания
+ // tschüß
+ // tschuß
+}
diff --git a/unum/vendor/golang.org/x/text/transform/transform.go b/unum/vendor/golang.org/x/text/transform/transform.go
new file mode 100644
index 0000000..fe47b9b
--- /dev/null
+++ b/unum/vendor/golang.org/x/text/transform/transform.go
@@ -0,0 +1,705 @@
+// Copyright 2013 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 transform provides reader and writer wrappers that transform the
+// bytes passing through as well as various transformations. Example
+// transformations provided by other packages include normalization and
+// conversion between character sets.
+package transform // import "golang.org/x/text/transform"
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "unicode/utf8"
+)
+
+var (
+ // ErrShortDst means that the destination buffer was too short to
+ // receive all of the transformed bytes.
+ ErrShortDst = errors.New("transform: short destination buffer")
+
+ // ErrShortSrc means that the source buffer has insufficient data to
+ // complete the transformation.
+ ErrShortSrc = errors.New("transform: short source buffer")
+
+ // ErrEndOfSpan means that the input and output (the transformed input)
+ // are not identical.
+ ErrEndOfSpan = errors.New("transform: input and output are not identical")
+
+ // errInconsistentByteCount means that Transform returned success (nil
+ // error) but also returned nSrc inconsistent with the src argument.
+ errInconsistentByteCount = errors.New("transform: inconsistent byte count returned")
+
+ // errShortInternal means that an internal buffer is not large enough
+ // to make progress and the Transform operation must be aborted.
+ errShortInternal = errors.New("transform: short internal buffer")
+)
+
+// Transformer transforms bytes.
+type Transformer interface {
+ // Transform writes to dst the transformed bytes read from src, and
+ // returns the number of dst bytes written and src bytes read. The
+ // atEOF argument tells whether src represents the last bytes of the
+ // input.
+ //
+ // Callers should always process the nDst bytes produced and account
+ // for the nSrc bytes consumed before considering the error err.
+ //
+ // A nil error means that all of the transformed bytes (whether freshly
+ // transformed from src or left over from previous Transform calls)
+ // were written to dst. A nil error can be returned regardless of
+ // whether atEOF is true. If err is nil then nSrc must equal len(src);
+ // the converse is not necessarily true.
+ //
+ // ErrShortDst means that dst was too short to receive all of the
+ // transformed bytes. ErrShortSrc means that src had insufficient data
+ // to complete the transformation. If both conditions apply, then
+ // either error may be returned. Other than the error conditions listed
+ // here, implementations are free to report other errors that arise.
+ Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error)
+
+ // Reset resets the state and allows a Transformer to be reused.
+ Reset()
+}
+
+// SpanningTransformer extends the Transformer interface with a Span method
+// that determines how much of the input already conforms to the Transformer.
+type SpanningTransformer interface {
+ Transformer
+
+ // Span returns a position in src such that transforming src[:n] results in
+ // identical output src[:n] for these bytes. It does not necessarily return
+ // the largest such n. The atEOF argument tells whether src represents the
+ // last bytes of the input.
+ //
+ // Callers should always account for the n bytes consumed before
+ // considering the error err.
+ //
+ // A nil error means that all input bytes are known to be identical to the
+ // output produced by the Transformer. A nil error can be be returned
+ // regardless of whether atEOF is true. If err is nil, then then n must
+ // equal len(src); the converse is not necessarily true.
+ //
+ // ErrEndOfSpan means that the Transformer output may differ from the
+ // input after n bytes. Note that n may be len(src), meaning that the output
+ // would contain additional bytes after otherwise identical output.
+ // ErrShortSrc means that src had insufficient data to determine whether the
+ // remaining bytes would change. Other than the error conditions listed
+ // here, implementations are free to report other errors that arise.
+ //
+ // Calling Span can modify the Transformer state as a side effect. In
+ // effect, it does the transformation just as calling Transform would, only
+ // without copying to a destination buffer and only up to a point it can
+ // determine the input and output bytes are the same. This is obviously more
+ // limited than calling Transform, but can be more efficient in terms of
+ // copying and allocating buffers. Calls to Span and Transform may be
+ // interleaved.
+ Span(src []byte, atEOF bool) (n int, err error)
+}
+
+// NopResetter can be embedded by implementations of Transformer to add a nop
+// Reset method.
+type NopResetter struct{}
+
+// Reset implements the Reset method of the Transformer interface.
+func (NopResetter) Reset() {}
+
+// Reader wraps another io.Reader by transforming the bytes read.
+type Reader struct {
+ r io.Reader
+ t Transformer
+ err error
+
+ // dst[dst0:dst1] contains bytes that have been transformed by t but
+ // not yet copied out via Read.
+ dst []byte
+ dst0, dst1 int
+
+ // src[src0:src1] contains bytes that have been read from r but not
+ // yet transformed through t.
+ src []byte
+ src0, src1 int
+
+ // transformComplete is whether the transformation is complete,
+ // regardless of whether or not it was successful.
+ transformComplete bool
+}
+
+const defaultBufSize = 4096
+
+// NewReader returns a new Reader that wraps r by transforming the bytes read
+// via t. It calls Reset on t.
+func NewReader(r io.Reader, t Transformer) *Reader {
+ t.Reset()
+ return &Reader{
+ r: r,
+ t: t,
+ dst: make([]byte, defaultBufSize),
+ src: make([]byte, defaultBufSize),
+ }
+}
+
+// Read implements the io.Reader interface.
+func (r *Reader) Read(p []byte) (int, error) {
+ n, err := 0, error(nil)
+ for {
+ // Copy out any transformed bytes and return the final error if we are done.
+ if r.dst0 != r.dst1 {
+ n = copy(p, r.dst[r.dst0:r.dst1])
+ r.dst0 += n
+ if r.dst0 == r.dst1 && r.transformComplete {
+ return n, r.err
+ }
+ return n, nil
+ } else if r.transformComplete {
+ return 0, r.err
+ }
+
+ // Try to transform some source bytes, or to flush the transformer if we
+ // are out of source bytes. We do this even if r.r.Read returned an error.
+ // As the io.Reader documentation says, "process the n > 0 bytes returned
+ // before considering the error".
+ if r.src0 != r.src1 || r.err != nil {
+ r.dst0 = 0
+ r.dst1, n, err = r.t.Transform(r.dst, r.src[r.src0:r.src1], r.err == io.EOF)
+ r.src0 += n
+
+ switch {
+ case err == nil:
+ if r.src0 != r.src1 {
+ r.err = errInconsistentByteCount
+ }
+ // The Transform call was successful; we are complete if we
+ // cannot read more bytes into src.
+ r.transformComplete = r.err != nil
+ continue
+ case err == ErrShortDst && (r.dst1 != 0 || n != 0):
+ // Make room in dst by copying out, and try again.
+ continue
+ case err == ErrShortSrc && r.src1-r.src0 != len(r.src) && r.err == nil:
+ // Read more bytes into src via the code below, and try again.
+ default:
+ r.transformComplete = true
+ // The reader error (r.err) takes precedence over the
+ // transformer error (err) unless r.err is nil or io.EOF.
+ if r.err == nil || r.err == io.EOF {
+ r.err = err
+ }
+ continue
+ }
+ }
+
+ // Move any untransformed source bytes to the start of the buffer
+ // and read more bytes.
+ if r.src0 != 0 {
+ r.src0, r.src1 = 0, copy(r.src, r.src[r.src0:r.src1])
+ }
+ n, r.err = r.r.Read(r.src[r.src1:])
+ r.src1 += n
+ }
+}
+
+// TODO: implement ReadByte (and ReadRune??).
+
+// Writer wraps another io.Writer by transforming the bytes read.
+// The user needs to call Close to flush unwritten bytes that may
+// be buffered.
+type Writer struct {
+ w io.Writer
+ t Transformer
+ dst []byte
+
+ // src[:n] contains bytes that have not yet passed through t.
+ src []byte
+ n int
+}
+
+// NewWriter returns a new Writer that wraps w by transforming the bytes written
+// via t. It calls Reset on t.
+func NewWriter(w io.Writer, t Transformer) *Writer {
+ t.Reset()
+ return &Writer{
+ w: w,
+ t: t,
+ dst: make([]byte, defaultBufSize),
+ src: make([]byte, defaultBufSize),
+ }
+}
+
+// Write implements the io.Writer interface. If there are not enough
+// bytes available to complete a Transform, the bytes will be buffered
+// for the next write. Call Close to convert the remaining bytes.
+func (w *Writer) Write(data []byte) (n int, err error) {
+ src := data
+ if w.n > 0 {
+ // Append bytes from data to the last remainder.
+ // TODO: limit the amount copied on first try.
+ n = copy(w.src[w.n:], data)
+ w.n += n
+ src = w.src[:w.n]
+ }
+ for {
+ nDst, nSrc, err := w.t.Transform(w.dst, src, false)
+ if _, werr := w.w.Write(w.dst[:nDst]); werr != nil {
+ return n, werr
+ }
+ src = src[nSrc:]
+ if w.n == 0 {
+ n += nSrc
+ } else if len(src) <= n {
+ // Enough bytes from w.src have been consumed. We make src point
+ // to data instead to reduce the copying.
+ w.n = 0
+ n -= len(src)
+ src = data[n:]
+ if n < len(data) && (err == nil || err == ErrShortSrc) {
+ continue
+ }
+ }
+ switch err {
+ case ErrShortDst:
+ // This error is okay as long as we are making progress.
+ if nDst > 0 || nSrc > 0 {
+ continue
+ }
+ case ErrShortSrc:
+ if len(src) < len(w.src) {
+ m := copy(w.src, src)
+ // If w.n > 0, bytes from data were already copied to w.src and n
+ // was already set to the number of bytes consumed.
+ if w.n == 0 {
+ n += m
+ }
+ w.n = m
+ err = nil
+ } else if nDst > 0 || nSrc > 0 {
+ // Not enough buffer to store the remainder. Keep processing as
+ // long as there is progress. Without this case, transforms that
+ // require a lookahead larger than the buffer may result in an
+ // error. This is not something one may expect to be common in
+ // practice, but it may occur when buffers are set to small
+ // sizes during testing.
+ continue
+ }
+ case nil:
+ if w.n > 0 {
+ err = errInconsistentByteCount
+ }
+ }
+ return n, err
+ }
+}
+
+// Close implements the io.Closer interface.
+func (w *Writer) Close() error {
+ src := w.src[:w.n]
+ for {
+ nDst, nSrc, err := w.t.Transform(w.dst, src, true)
+ if _, werr := w.w.Write(w.dst[:nDst]); werr != nil {
+ return werr
+ }
+ if err != ErrShortDst {
+ return err
+ }
+ src = src[nSrc:]
+ }
+}
+
+type nop struct{ NopResetter }
+
+func (nop) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ n := copy(dst, src)
+ if n < len(src) {
+ err = ErrShortDst
+ }
+ return n, n, err
+}
+
+func (nop) Span(src []byte, atEOF bool) (n int, err error) {
+ return len(src), nil
+}
+
+type discard struct{ NopResetter }
+
+func (discard) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ return 0, len(src), nil
+}
+
+var (
+ // Discard is a Transformer for which all Transform calls succeed
+ // by consuming all bytes and writing nothing.
+ Discard Transformer = discard{}
+
+ // Nop is a SpanningTransformer that copies src to dst.
+ Nop SpanningTransformer = nop{}
+)
+
+// chain is a sequence of links. A chain with N Transformers has N+1 links and
+// N+1 buffers. Of those N+1 buffers, the first and last are the src and dst
+// buffers given to chain.Transform and the middle N-1 buffers are intermediate
+// buffers owned by the chain. The i'th link transforms bytes from the i'th
+// buffer chain.link[i].b at read offset chain.link[i].p to the i+1'th buffer
+// chain.link[i+1].b at write offset chain.link[i+1].n, for i in [0, N).
+type chain struct {
+ link []link
+ err error
+ // errStart is the index at which the error occurred plus 1. Processing
+ // errStart at this level at the next call to Transform. As long as
+ // errStart > 0, chain will not consume any more source bytes.
+ errStart int
+}
+
+func (c *chain) fatalError(errIndex int, err error) {
+ if i := errIndex + 1; i > c.errStart {
+ c.errStart = i
+ c.err = err
+ }
+}
+
+type link struct {
+ t Transformer
+ // b[p:n] holds the bytes to be transformed by t.
+ b []byte
+ p int
+ n int
+}
+
+func (l *link) src() []byte {
+ return l.b[l.p:l.n]
+}
+
+func (l *link) dst() []byte {
+ return l.b[l.n:]
+}
+
+// Chain returns a Transformer that applies t in sequence.
+func Chain(t ...Transformer) Transformer {
+ if len(t) == 0 {
+ return nop{}
+ }
+ c := &chain{link: make([]link, len(t)+1)}
+ for i, tt := range t {
+ c.link[i].t = tt
+ }
+ // Allocate intermediate buffers.
+ b := make([][defaultBufSize]byte, len(t)-1)
+ for i := range b {
+ c.link[i+1].b = b[i][:]
+ }
+ return c
+}
+
+// Reset resets the state of Chain. It calls Reset on all the Transformers.
+func (c *chain) Reset() {
+ for i, l := range c.link {
+ if l.t != nil {
+ l.t.Reset()
+ }
+ c.link[i].p, c.link[i].n = 0, 0
+ }
+}
+
+// TODO: make chain use Span (is going to be fun to implement!)
+
+// Transform applies the transformers of c in sequence.
+func (c *chain) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ // Set up src and dst in the chain.
+ srcL := &c.link[0]
+ dstL := &c.link[len(c.link)-1]
+ srcL.b, srcL.p, srcL.n = src, 0, len(src)
+ dstL.b, dstL.n = dst, 0
+ var lastFull, needProgress bool // for detecting progress
+
+ // i is the index of the next Transformer to apply, for i in [low, high].
+ // low is the lowest index for which c.link[low] may still produce bytes.
+ // high is the highest index for which c.link[high] has a Transformer.
+ // The error returned by Transform determines whether to increase or
+ // decrease i. We try to completely fill a buffer before converting it.
+ for low, i, high := c.errStart, c.errStart, len(c.link)-2; low <= i && i <= high; {
+ in, out := &c.link[i], &c.link[i+1]
+ nDst, nSrc, err0 := in.t.Transform(out.dst(), in.src(), atEOF && low == i)
+ out.n += nDst
+ in.p += nSrc
+ if i > 0 && in.p == in.n {
+ in.p, in.n = 0, 0
+ }
+ needProgress, lastFull = lastFull, false
+ switch err0 {
+ case ErrShortDst:
+ // Process the destination buffer next. Return if we are already
+ // at the high index.
+ if i == high {
+ return dstL.n, srcL.p, ErrShortDst
+ }
+ if out.n != 0 {
+ i++
+ // If the Transformer at the next index is not able to process any
+ // source bytes there is nothing that can be done to make progress
+ // and the bytes will remain unprocessed. lastFull is used to
+ // detect this and break out of the loop with a fatal error.
+ lastFull = true
+ continue
+ }
+ // The destination buffer was too small, but is completely empty.
+ // Return a fatal error as this transformation can never complete.
+ c.fatalError(i, errShortInternal)
+ case ErrShortSrc:
+ if i == 0 {
+ // Save ErrShortSrc in err. All other errors take precedence.
+ err = ErrShortSrc
+ break
+ }
+ // Source bytes were depleted before filling up the destination buffer.
+ // Verify we made some progress, move the remaining bytes to the errStart
+ // and try to get more source bytes.
+ if needProgress && nSrc == 0 || in.n-in.p == len(in.b) {
+ // There were not enough source bytes to proceed while the source
+ // buffer cannot hold any more bytes. Return a fatal error as this
+ // transformation can never complete.
+ c.fatalError(i, errShortInternal)
+ break
+ }
+ // in.b is an internal buffer and we can make progress.
+ in.p, in.n = 0, copy(in.b, in.src())
+ fallthrough
+ case nil:
+ // if i == low, we have depleted the bytes at index i or any lower levels.
+ // In that case we increase low and i. In all other cases we decrease i to
+ // fetch more bytes before proceeding to the next index.
+ if i > low {
+ i--
+ continue
+ }
+ default:
+ c.fatalError(i, err0)
+ }
+ // Exhausted level low or fatal error: increase low and continue
+ // to process the bytes accepted so far.
+ i++
+ low = i
+ }
+
+ // If c.errStart > 0, this means we found a fatal error. We will clear
+ // all upstream buffers. At this point, no more progress can be made
+ // downstream, as Transform would have bailed while handling ErrShortDst.
+ if c.errStart > 0 {
+ for i := 1; i < c.errStart; i++ {
+ c.link[i].p, c.link[i].n = 0, 0
+ }
+ err, c.errStart, c.err = c.err, 0, nil
+ }
+ return dstL.n, srcL.p, err
+}
+
+// Deprecated: use runes.Remove instead.
+func RemoveFunc(f func(r rune) bool) Transformer {
+ return removeF(f)
+}
+
+type removeF func(r rune) bool
+
+func (removeF) Reset() {}
+
+// Transform implements the Transformer interface.
+func (t removeF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ for r, sz := rune(0), 0; len(src) > 0; src = src[sz:] {
+
+ if r = rune(src[0]); r < utf8.RuneSelf {
+ sz = 1
+ } else {
+ r, sz = utf8.DecodeRune(src)
+
+ if sz == 1 {
+ // Invalid rune.
+ if !atEOF && !utf8.FullRune(src) {
+ err = ErrShortSrc
+ break
+ }
+ // We replace illegal bytes with RuneError. Not doing so might
+ // otherwise turn a sequence of invalid UTF-8 into valid UTF-8.
+ // The resulting byte sequence may subsequently contain runes
+ // for which t(r) is true that were passed unnoticed.
+ if !t(r) {
+ if nDst+3 > len(dst) {
+ err = ErrShortDst
+ break
+ }
+ nDst += copy(dst[nDst:], "\uFFFD")
+ }
+ nSrc++
+ continue
+ }
+ }
+
+ if !t(r) {
+ if nDst+sz > len(dst) {
+ err = ErrShortDst
+ break
+ }
+ nDst += copy(dst[nDst:], src[:sz])
+ }
+ nSrc += sz
+ }
+ return
+}
+
+// grow returns a new []byte that is longer than b, and copies the first n bytes
+// of b to the start of the new slice.
+func grow(b []byte, n int) []byte {
+ m := len(b)
+ if m <= 32 {
+ m = 64
+ } else if m <= 256 {
+ m *= 2
+ } else {
+ m += m >> 1
+ }
+ buf := make([]byte, m)
+ copy(buf, b[:n])
+ return buf
+}
+
+const initialBufSize = 128
+
+// String returns a string with the result of converting s[:n] using t, where
+// n <= len(s). If err == nil, n will be len(s). It calls Reset on t.
+func String(t Transformer, s string) (result string, n int, err error) {
+ t.Reset()
+ if s == "" {
+ // Fast path for the common case for empty input. Results in about a
+ // 86% reduction of running time for BenchmarkStringLowerEmpty.
+ if _, _, err := t.Transform(nil, nil, true); err == nil {
+ return "", 0, nil
+ }
+ }
+
+ // Allocate only once. Note that both dst and src escape when passed to
+ // Transform.
+ buf := [2 * initialBufSize]byte{}
+ dst := buf[:initialBufSize:initialBufSize]
+ src := buf[initialBufSize : 2*initialBufSize]
+
+ // The input string s is transformed in multiple chunks (starting with a
+ // chunk size of initialBufSize). nDst and nSrc are per-chunk (or
+ // per-Transform-call) indexes, pDst and pSrc are overall indexes.
+ nDst, nSrc := 0, 0
+ pDst, pSrc := 0, 0
+
+ // pPrefix is the length of a common prefix: the first pPrefix bytes of the
+ // result will equal the first pPrefix bytes of s. It is not guaranteed to
+ // be the largest such value, but if pPrefix, len(result) and len(s) are
+ // all equal after the final transform (i.e. calling Transform with atEOF
+ // being true returned nil error) then we don't need to allocate a new
+ // result string.
+ pPrefix := 0
+ for {
+ // Invariant: pDst == pPrefix && pSrc == pPrefix.
+
+ n := copy(src, s[pSrc:])
+ nDst, nSrc, err = t.Transform(dst, src[:n], pSrc+n == len(s))
+ pDst += nDst
+ pSrc += nSrc
+
+ // TODO: let transformers implement an optional Spanner interface, akin
+ // to norm's QuickSpan. This would even allow us to avoid any allocation.
+ if !bytes.Equal(dst[:nDst], src[:nSrc]) {
+ break
+ }
+ pPrefix = pSrc
+ if err == ErrShortDst {
+ // A buffer can only be short if a transformer modifies its input.
+ break
+ } else if err == ErrShortSrc {
+ if nSrc == 0 {
+ // No progress was made.
+ break
+ }
+ // Equal so far and !atEOF, so continue checking.
+ } else if err != nil || pPrefix == len(s) {
+ return string(s[:pPrefix]), pPrefix, err
+ }
+ }
+ // Post-condition: pDst == pPrefix + nDst && pSrc == pPrefix + nSrc.
+
+ // We have transformed the first pSrc bytes of the input s to become pDst
+ // transformed bytes. Those transformed bytes are discontiguous: the first
+ // pPrefix of them equal s[:pPrefix] and the last nDst of them equal
+ // dst[:nDst]. We copy them around, into a new dst buffer if necessary, so
+ // that they become one contiguous slice: dst[:pDst].
+ if pPrefix != 0 {
+ newDst := dst
+ if pDst > len(newDst) {
+ newDst = make([]byte, len(s)+nDst-nSrc)
+ }
+ copy(newDst[pPrefix:pDst], dst[:nDst])
+ copy(newDst[:pPrefix], s[:pPrefix])
+ dst = newDst
+ }
+
+ // Prevent duplicate Transform calls with atEOF being true at the end of
+ // the input. Also return if we have an unrecoverable error.
+ if (err == nil && pSrc == len(s)) ||
+ (err != nil && err != ErrShortDst && err != ErrShortSrc) {
+ return string(dst[:pDst]), pSrc, err
+ }
+
+ // Transform the remaining input, growing dst and src buffers as necessary.
+ for {
+ n := copy(src, s[pSrc:])
+ nDst, nSrc, err := t.Transform(dst[pDst:], src[:n], pSrc+n == len(s))
+ pDst += nDst
+ pSrc += nSrc
+
+ // If we got ErrShortDst or ErrShortSrc, do not grow as long as we can
+ // make progress. This may avoid excessive allocations.
+ if err == ErrShortDst {
+ if nDst == 0 {
+ dst = grow(dst, pDst)
+ }
+ } else if err == ErrShortSrc {
+ if nSrc == 0 {
+ src = grow(src, 0)
+ }
+ } else if err != nil || pSrc == len(s) {
+ return string(dst[:pDst]), pSrc, err
+ }
+ }
+}
+
+// Bytes returns a new byte slice with the result of converting b[:n] using t,
+// where n <= len(b). If err == nil, n will be len(b). It calls Reset on t.
+func Bytes(t Transformer, b []byte) (result []byte, n int, err error) {
+ return doAppend(t, 0, make([]byte, len(b)), b)
+}
+
+// Append appends the result of converting src[:n] using t to dst, where
+// n <= len(src), If err == nil, n will be len(src). It calls Reset on t.
+func Append(t Transformer, dst, src []byte) (result []byte, n int, err error) {
+ if len(dst) == cap(dst) {
+ n := len(src) + len(dst) // It is okay for this to be 0.
+ b := make([]byte, n)
+ dst = b[:copy(b, dst)]
+ }
+ return doAppend(t, len(dst), dst[:cap(dst)], src)
+}
+
+func doAppend(t Transformer, pDst int, dst, src []byte) (result []byte, n int, err error) {
+ t.Reset()
+ pSrc := 0
+ for {
+ nDst, nSrc, err := t.Transform(dst[pDst:], src[pSrc:], true)
+ pDst += nDst
+ pSrc += nSrc
+ if err != ErrShortDst {
+ return dst[:pDst], pSrc, err
+ }
+
+ // Grow the destination buffer, but do not grow as long as we can make
+ // progress. This may avoid excessive allocations.
+ if nDst == 0 {
+ dst = grow(dst, pDst)
+ }
+ }
+}
diff --git a/unum/vendor/golang.org/x/text/transform/transform_test.go b/unum/vendor/golang.org/x/text/transform/transform_test.go
new file mode 100644
index 0000000..771633d
--- /dev/null
+++ b/unum/vendor/golang.org/x/text/transform/transform_test.go
@@ -0,0 +1,1317 @@
+// Copyright 2013 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 transform
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+ "unicode/utf8"
+
+ "golang.org/x/text/internal/testtext"
+)
+
+type lowerCaseASCII struct{ NopResetter }
+
+func (lowerCaseASCII) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ n := len(src)
+ if n > len(dst) {
+ n, err = len(dst), ErrShortDst
+ }
+ for i, c := range src[:n] {
+ if 'A' <= c && c <= 'Z' {
+ c += 'a' - 'A'
+ }
+ dst[i] = c
+ }
+ return n, n, err
+}
+
+// lowerCaseASCIILookahead lowercases the string and reports ErrShortSrc as long
+// as the input is not atEOF.
+type lowerCaseASCIILookahead struct{ NopResetter }
+
+func (lowerCaseASCIILookahead) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ n := len(src)
+ if n > len(dst) {
+ n, err = len(dst), ErrShortDst
+ }
+ for i, c := range src[:n] {
+ if 'A' <= c && c <= 'Z' {
+ c += 'a' - 'A'
+ }
+ dst[i] = c
+ }
+ if !atEOF {
+ err = ErrShortSrc
+ }
+ return n, n, err
+}
+
+var errYouMentionedX = errors.New("you mentioned X")
+
+type dontMentionX struct{ NopResetter }
+
+func (dontMentionX) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ n := len(src)
+ if n > len(dst) {
+ n, err = len(dst), ErrShortDst
+ }
+ for i, c := range src[:n] {
+ if c == 'X' {
+ return i, i, errYouMentionedX
+ }
+ dst[i] = c
+ }
+ return n, n, err
+}
+
+var errAtEnd = errors.New("error after all text")
+
+type errorAtEnd struct{ NopResetter }
+
+func (errorAtEnd) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ n := copy(dst, src)
+ if n < len(src) {
+ return n, n, ErrShortDst
+ }
+ if atEOF {
+ return n, n, errAtEnd
+ }
+ return n, n, nil
+}
+
+type replaceWithConstant struct {
+ replacement string
+ written int
+}
+
+func (t *replaceWithConstant) Reset() {
+ t.written = 0
+}
+
+func (t *replaceWithConstant) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ if atEOF {
+ nDst = copy(dst, t.replacement[t.written:])
+ t.written += nDst
+ if t.written < len(t.replacement) {
+ err = ErrShortDst
+ }
+ }
+ return nDst, len(src), err
+}
+
+type addAnXAtTheEnd struct{ NopResetter }
+
+func (addAnXAtTheEnd) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ n := copy(dst, src)
+ if n < len(src) {
+ return n, n, ErrShortDst
+ }
+ if !atEOF {
+ return n, n, nil
+ }
+ if len(dst) == n {
+ return n, n, ErrShortDst
+ }
+ dst[n] = 'X'
+ return n + 1, n, nil
+}
+
+// doublerAtEOF is a strange Transformer that transforms "this" to "tthhiiss",
+// but only if atEOF is true.
+type doublerAtEOF struct{ NopResetter }
+
+func (doublerAtEOF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ if !atEOF {
+ return 0, 0, ErrShortSrc
+ }
+ for i, c := range src {
+ if 2*i+2 >= len(dst) {
+ return 2 * i, i, ErrShortDst
+ }
+ dst[2*i+0] = c
+ dst[2*i+1] = c
+ }
+ return 2 * len(src), len(src), nil
+}
+
+// rleDecode and rleEncode implement a toy run-length encoding: "aabbbbbbbbbb"
+// is encoded as "2a10b". The decoding is assumed to not contain any numbers.
+
+type rleDecode struct{ NopResetter }
+
+func (rleDecode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+loop:
+ for len(src) > 0 {
+ n := 0
+ for i, c := range src {
+ if '0' <= c && c <= '9' {
+ n = 10*n + int(c-'0')
+ continue
+ }
+ if i == 0 {
+ return nDst, nSrc, errors.New("rleDecode: bad input")
+ }
+ if n > len(dst) {
+ return nDst, nSrc, ErrShortDst
+ }
+ for j := 0; j < n; j++ {
+ dst[j] = c
+ }
+ dst, src = dst[n:], src[i+1:]
+ nDst, nSrc = nDst+n, nSrc+i+1
+ continue loop
+ }
+ if atEOF {
+ return nDst, nSrc, errors.New("rleDecode: bad input")
+ }
+ return nDst, nSrc, ErrShortSrc
+ }
+ return nDst, nSrc, nil
+}
+
+type rleEncode struct {
+ NopResetter
+
+ // allowStutter means that "xxxxxxxx" can be encoded as "5x3x"
+ // instead of always as "8x".
+ allowStutter bool
+}
+
+func (e rleEncode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ for len(src) > 0 {
+ n, c0 := len(src), src[0]
+ for i, c := range src[1:] {
+ if c != c0 {
+ n = i + 1
+ break
+ }
+ }
+ if n == len(src) && !atEOF && !e.allowStutter {
+ return nDst, nSrc, ErrShortSrc
+ }
+ s := strconv.Itoa(n)
+ if len(s) >= len(dst) {
+ return nDst, nSrc, ErrShortDst
+ }
+ copy(dst, s)
+ dst[len(s)] = c0
+ dst, src = dst[len(s)+1:], src[n:]
+ nDst, nSrc = nDst+len(s)+1, nSrc+n
+ }
+ return nDst, nSrc, nil
+}
+
+// trickler consumes all input bytes, but writes a single byte at a time to dst.
+type trickler []byte
+
+func (t *trickler) Reset() {
+ *t = nil
+}
+
+func (t *trickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ *t = append(*t, src...)
+ if len(*t) == 0 {
+ return 0, 0, nil
+ }
+ if len(dst) == 0 {
+ return 0, len(src), ErrShortDst
+ }
+ dst[0] = (*t)[0]
+ *t = (*t)[1:]
+ if len(*t) > 0 {
+ err = ErrShortDst
+ }
+ return 1, len(src), err
+}
+
+// delayedTrickler is like trickler, but delays writing output to dst. This is
+// highly unlikely to be relevant in practice, but it seems like a good idea
+// to have some tolerance as long as progress can be detected.
+type delayedTrickler []byte
+
+func (t *delayedTrickler) Reset() {
+ *t = nil
+}
+
+func (t *delayedTrickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ if len(*t) > 0 && len(dst) > 0 {
+ dst[0] = (*t)[0]
+ *t = (*t)[1:]
+ nDst = 1
+ }
+ *t = append(*t, src...)
+ if len(*t) > 0 {
+ err = ErrShortDst
+ }
+ return nDst, len(src), err
+}
+
+type testCase struct {
+ desc string
+ t Transformer
+ src string
+ dstSize int
+ srcSize int
+ ioSize int
+ wantStr string
+ wantErr error
+ wantIter int // number of iterations taken; 0 means we don't care.
+}
+
+func (t testCase) String() string {
+ return tstr(t.t) + "; " + t.desc
+}
+
+func tstr(t Transformer) string {
+ if stringer, ok := t.(fmt.Stringer); ok {
+ return stringer.String()
+ }
+ s := fmt.Sprintf("%T", t)
+ return s[1+strings.Index(s, "."):]
+}
+
+func (c chain) String() string {
+ buf := &bytes.Buffer{}
+ buf.WriteString("Chain(")
+ for i, l := range c.link[:len(c.link)-1] {
+ if i != 0 {
+ fmt.Fprint(buf, ", ")
+ }
+ buf.WriteString(tstr(l.t))
+ }
+ buf.WriteString(")")
+ return buf.String()
+}
+
+var testCases = []testCase{
+ {
+ desc: "empty",
+ t: lowerCaseASCII{},
+ src: "",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "",
+ },
+
+ {
+ desc: "basic",
+ t: lowerCaseASCII{},
+ src: "Hello WORLD.",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "hello world.",
+ },
+
+ {
+ desc: "small dst",
+ t: lowerCaseASCII{},
+ src: "Hello WORLD.",
+ dstSize: 3,
+ srcSize: 100,
+ wantStr: "hello world.",
+ },
+
+ {
+ desc: "small src",
+ t: lowerCaseASCII{},
+ src: "Hello WORLD.",
+ dstSize: 100,
+ srcSize: 4,
+ wantStr: "hello world.",
+ },
+
+ {
+ desc: "small buffers",
+ t: lowerCaseASCII{},
+ src: "Hello WORLD.",
+ dstSize: 3,
+ srcSize: 4,
+ wantStr: "hello world.",
+ },
+
+ {
+ desc: "very small buffers",
+ t: lowerCaseASCII{},
+ src: "Hello WORLD.",
+ dstSize: 1,
+ srcSize: 1,
+ wantStr: "hello world.",
+ },
+
+ {
+ desc: "small dst with lookahead",
+ t: lowerCaseASCIILookahead{},
+ src: "Hello WORLD.",
+ dstSize: 3,
+ srcSize: 100,
+ wantStr: "hello world.",
+ },
+
+ {
+ desc: "small src with lookahead",
+ t: lowerCaseASCIILookahead{},
+ src: "Hello WORLD.",
+ dstSize: 100,
+ srcSize: 4,
+ wantStr: "hello world.",
+ },
+
+ {
+ desc: "small buffers with lookahead",
+ t: lowerCaseASCIILookahead{},
+ src: "Hello WORLD.",
+ dstSize: 3,
+ srcSize: 4,
+ wantStr: "hello world.",
+ },
+
+ {
+ desc: "very small buffers with lookahead",
+ t: lowerCaseASCIILookahead{},
+ src: "Hello WORLD.",
+ dstSize: 1,
+ srcSize: 2,
+ wantStr: "hello world.",
+ },
+
+ {
+ desc: "user error",
+ t: dontMentionX{},
+ src: "The First Rule of Transform Club: don't mention Mister X, ever.",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "The First Rule of Transform Club: don't mention Mister ",
+ wantErr: errYouMentionedX,
+ },
+
+ {
+ desc: "user error at end",
+ t: errorAtEnd{},
+ src: "All goes well until it doesn't.",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "All goes well until it doesn't.",
+ wantErr: errAtEnd,
+ },
+
+ {
+ desc: "user error at end, incremental",
+ t: errorAtEnd{},
+ src: "All goes well until it doesn't.",
+ dstSize: 10,
+ srcSize: 10,
+ wantStr: "All goes well until it doesn't.",
+ wantErr: errAtEnd,
+ },
+
+ {
+ desc: "replace entire non-empty string with one byte",
+ t: &replaceWithConstant{replacement: "X"},
+ src: "none of this will be copied",
+ dstSize: 1,
+ srcSize: 10,
+ wantStr: "X",
+ },
+
+ {
+ desc: "replace entire empty string with one byte",
+ t: &replaceWithConstant{replacement: "X"},
+ src: "",
+ dstSize: 1,
+ srcSize: 10,
+ wantStr: "X",
+ },
+
+ {
+ desc: "replace entire empty string with seven bytes",
+ t: &replaceWithConstant{replacement: "ABCDEFG"},
+ src: "",
+ dstSize: 3,
+ srcSize: 10,
+ wantStr: "ABCDEFG",
+ },
+
+ {
+ desc: "add an X (initialBufSize-1)",
+ t: addAnXAtTheEnd{},
+ src: aaa[:initialBufSize-1],
+ dstSize: 10,
+ srcSize: 10,
+ wantStr: aaa[:initialBufSize-1] + "X",
+ },
+
+ {
+ desc: "add an X (initialBufSize+0)",
+ t: addAnXAtTheEnd{},
+ src: aaa[:initialBufSize+0],
+ dstSize: 10,
+ srcSize: 10,
+ wantStr: aaa[:initialBufSize+0] + "X",
+ },
+
+ {
+ desc: "add an X (initialBufSize+1)",
+ t: addAnXAtTheEnd{},
+ src: aaa[:initialBufSize+1],
+ dstSize: 10,
+ srcSize: 10,
+ wantStr: aaa[:initialBufSize+1] + "X",
+ },
+
+ {
+ desc: "small buffers",
+ t: dontMentionX{},
+ src: "The First Rule of Transform Club: don't mention Mister X, ever.",
+ dstSize: 10,
+ srcSize: 10,
+ wantStr: "The First Rule of Transform Club: don't mention Mister ",
+ wantErr: errYouMentionedX,
+ },
+
+ {
+ desc: "very small buffers",
+ t: dontMentionX{},
+ src: "The First Rule of Transform Club: don't mention Mister X, ever.",
+ dstSize: 1,
+ srcSize: 1,
+ wantStr: "The First Rule of Transform Club: don't mention Mister ",
+ wantErr: errYouMentionedX,
+ },
+
+ {
+ desc: "only transform at EOF",
+ t: doublerAtEOF{},
+ src: "this",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "tthhiiss",
+ },
+
+ {
+ desc: "basic",
+ t: rleDecode{},
+ src: "1a2b3c10d11e0f1g",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "abbcccddddddddddeeeeeeeeeeeg",
+ },
+
+ {
+ desc: "long",
+ t: rleDecode{},
+ src: "12a23b34c45d56e99z",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: strings.Repeat("a", 12) +
+ strings.Repeat("b", 23) +
+ strings.Repeat("c", 34) +
+ strings.Repeat("d", 45) +
+ strings.Repeat("e", 56) +
+ strings.Repeat("z", 99),
+ },
+
+ {
+ desc: "tight buffers",
+ t: rleDecode{},
+ src: "1a2b3c10d11e0f1g",
+ dstSize: 11,
+ srcSize: 3,
+ wantStr: "abbcccddddddddddeeeeeeeeeeeg",
+ },
+
+ {
+ desc: "short dst",
+ t: rleDecode{},
+ src: "1a2b3c10d11e0f1g",
+ dstSize: 10,
+ srcSize: 3,
+ wantStr: "abbcccdddddddddd",
+ wantErr: ErrShortDst,
+ },
+
+ {
+ desc: "short src",
+ t: rleDecode{},
+ src: "1a2b3c10d11e0f1g",
+ dstSize: 11,
+ srcSize: 2,
+ ioSize: 2,
+ wantStr: "abbccc",
+ wantErr: ErrShortSrc,
+ },
+
+ {
+ desc: "basic",
+ t: rleEncode{},
+ src: "abbcccddddddddddeeeeeeeeeeeg",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "1a2b3c10d11e1g",
+ },
+
+ {
+ desc: "long",
+ t: rleEncode{},
+ src: strings.Repeat("a", 12) +
+ strings.Repeat("b", 23) +
+ strings.Repeat("c", 34) +
+ strings.Repeat("d", 45) +
+ strings.Repeat("e", 56) +
+ strings.Repeat("z", 99),
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "12a23b34c45d56e99z",
+ },
+
+ {
+ desc: "tight buffers",
+ t: rleEncode{},
+ src: "abbcccddddddddddeeeeeeeeeeeg",
+ dstSize: 3,
+ srcSize: 12,
+ wantStr: "1a2b3c10d11e1g",
+ },
+
+ {
+ desc: "short dst",
+ t: rleEncode{},
+ src: "abbcccddddddddddeeeeeeeeeeeg",
+ dstSize: 2,
+ srcSize: 12,
+ wantStr: "1a2b3c",
+ wantErr: ErrShortDst,
+ },
+
+ {
+ desc: "short src",
+ t: rleEncode{},
+ src: "abbcccddddddddddeeeeeeeeeeeg",
+ dstSize: 3,
+ srcSize: 11,
+ ioSize: 11,
+ wantStr: "1a2b3c10d",
+ wantErr: ErrShortSrc,
+ },
+
+ {
+ desc: "allowStutter = false",
+ t: rleEncode{allowStutter: false},
+ src: "aaaabbbbbbbbccccddddd",
+ dstSize: 10,
+ srcSize: 10,
+ wantStr: "4a8b4c5d",
+ },
+
+ {
+ desc: "allowStutter = true",
+ t: rleEncode{allowStutter: true},
+ src: "aaaabbbbbbbbccccddddd",
+ dstSize: 10,
+ srcSize: 10,
+ ioSize: 10,
+ wantStr: "4a6b2b4c4d1d",
+ },
+
+ {
+ desc: "trickler",
+ t: &trickler{},
+ src: "abcdefghijklm",
+ dstSize: 3,
+ srcSize: 15,
+ wantStr: "abcdefghijklm",
+ },
+
+ {
+ desc: "delayedTrickler",
+ t: &delayedTrickler{},
+ src: "abcdefghijklm",
+ dstSize: 3,
+ srcSize: 15,
+ wantStr: "abcdefghijklm",
+ },
+}
+
+func TestReader(t *testing.T) {
+ for _, tc := range testCases {
+ testtext.Run(t, tc.desc, func(t *testing.T) {
+ r := NewReader(strings.NewReader(tc.src), tc.t)
+ // Differently sized dst and src buffers are not part of the
+ // exported API. We override them manually.
+ r.dst = make([]byte, tc.dstSize)
+ r.src = make([]byte, tc.srcSize)
+ got, err := ioutil.ReadAll(r)
+ str := string(got)
+ if str != tc.wantStr || err != tc.wantErr {
+ t.Errorf("\ngot %q, %v\nwant %q, %v", str, err, tc.wantStr, tc.wantErr)
+ }
+ })
+ }
+}
+
+func TestWriter(t *testing.T) {
+ tests := append(testCases, chainTests()...)
+ for _, tc := range tests {
+ sizes := []int{1, 2, 3, 4, 5, 10, 100, 1000}
+ if tc.ioSize > 0 {
+ sizes = []int{tc.ioSize}
+ }
+ for _, sz := range sizes {
+ testtext.Run(t, fmt.Sprintf("%s/%d", tc.desc, sz), func(t *testing.T) {
+ bb := &bytes.Buffer{}
+ w := NewWriter(bb, tc.t)
+ // Differently sized dst and src buffers are not part of the
+ // exported API. We override them manually.
+ w.dst = make([]byte, tc.dstSize)
+ w.src = make([]byte, tc.srcSize)
+ src := make([]byte, sz)
+ var err error
+ for b := tc.src; len(b) > 0 && err == nil; {
+ n := copy(src, b)
+ b = b[n:]
+ m := 0
+ m, err = w.Write(src[:n])
+ if m != n && err == nil {
+ t.Errorf("did not consume all bytes %d < %d", m, n)
+ }
+ }
+ if err == nil {
+ err = w.Close()
+ }
+ str := bb.String()
+ if str != tc.wantStr || err != tc.wantErr {
+ t.Errorf("\ngot %q, %v\nwant %q, %v", str, err, tc.wantStr, tc.wantErr)
+ }
+ })
+ }
+ }
+}
+
+func TestNop(t *testing.T) {
+ testCases := []struct {
+ str string
+ dstSize int
+ err error
+ }{
+ {"", 0, nil},
+ {"", 10, nil},
+ {"a", 0, ErrShortDst},
+ {"a", 1, nil},
+ {"a", 10, nil},
+ }
+ for i, tc := range testCases {
+ dst := make([]byte, tc.dstSize)
+ nDst, nSrc, err := Nop.Transform(dst, []byte(tc.str), true)
+ want := tc.str
+ if tc.dstSize < len(want) {
+ want = want[:tc.dstSize]
+ }
+ if got := string(dst[:nDst]); got != want || err != tc.err || nSrc != nDst {
+ t.Errorf("%d:\ngot %q, %d, %v\nwant %q, %d, %v", i, got, nSrc, err, want, nDst, tc.err)
+ }
+ }
+}
+
+func TestDiscard(t *testing.T) {
+ testCases := []struct {
+ str string
+ dstSize int
+ }{
+ {"", 0},
+ {"", 10},
+ {"a", 0},
+ {"ab", 10},
+ }
+ for i, tc := range testCases {
+ nDst, nSrc, err := Discard.Transform(make([]byte, tc.dstSize), []byte(tc.str), true)
+ if nDst != 0 || nSrc != len(tc.str) || err != nil {
+ t.Errorf("%d:\ngot %q, %d, %v\nwant 0, %d, nil", i, nDst, nSrc, err, len(tc.str))
+ }
+ }
+}
+
+// mkChain creates a Chain transformer. x must be alternating between transformer
+// and bufSize, like T, (sz, T)*
+func mkChain(x ...interface{}) *chain {
+ t := []Transformer{}
+ for i := 0; i < len(x); i += 2 {
+ t = append(t, x[i].(Transformer))
+ }
+ c := Chain(t...).(*chain)
+ for i, j := 1, 1; i < len(x); i, j = i+2, j+1 {
+ c.link[j].b = make([]byte, x[i].(int))
+ }
+ return c
+}
+
+func chainTests() []testCase {
+ return []testCase{
+ {
+ desc: "nil error",
+ t: mkChain(rleEncode{}, 100, lowerCaseASCII{}),
+ src: "ABB",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "1a2b",
+ wantErr: nil,
+ wantIter: 1,
+ },
+
+ {
+ desc: "short dst buffer",
+ t: mkChain(lowerCaseASCII{}, 3, rleDecode{}),
+ src: "1a2b3c10d11e0f1g",
+ dstSize: 10,
+ srcSize: 3,
+ wantStr: "abbcccdddddddddd",
+ wantErr: ErrShortDst,
+ },
+
+ {
+ desc: "short internal dst buffer",
+ t: mkChain(lowerCaseASCII{}, 3, rleDecode{}, 10, Nop),
+ src: "1a2b3c10d11e0f1g",
+ dstSize: 100,
+ srcSize: 3,
+ wantStr: "abbcccdddddddddd",
+ wantErr: errShortInternal,
+ },
+
+ {
+ desc: "short internal dst buffer from input",
+ t: mkChain(rleDecode{}, 10, Nop),
+ src: "1a2b3c10d11e0f1g",
+ dstSize: 100,
+ srcSize: 3,
+ wantStr: "abbcccdddddddddd",
+ wantErr: errShortInternal,
+ },
+
+ {
+ desc: "empty short internal dst buffer",
+ t: mkChain(lowerCaseASCII{}, 3, rleDecode{}, 10, Nop),
+ src: "4a7b11e0f1g",
+ dstSize: 100,
+ srcSize: 3,
+ wantStr: "aaaabbbbbbb",
+ wantErr: errShortInternal,
+ },
+
+ {
+ desc: "empty short internal dst buffer from input",
+ t: mkChain(rleDecode{}, 10, Nop),
+ src: "4a7b11e0f1g",
+ dstSize: 100,
+ srcSize: 3,
+ wantStr: "aaaabbbbbbb",
+ wantErr: errShortInternal,
+ },
+
+ {
+ desc: "short internal src buffer after full dst buffer",
+ t: mkChain(Nop, 5, rleEncode{}, 10, Nop),
+ src: "cccccddddd",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "",
+ wantErr: errShortInternal,
+ wantIter: 1,
+ },
+
+ {
+ desc: "short internal src buffer after short dst buffer; test lastFull",
+ t: mkChain(rleDecode{}, 5, rleEncode{}, 4, Nop),
+ src: "2a1b4c6d",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "2a1b",
+ wantErr: errShortInternal,
+ },
+
+ {
+ desc: "short internal src buffer after successful complete fill",
+ t: mkChain(Nop, 3, rleDecode{}),
+ src: "123a4b",
+ dstSize: 4,
+ srcSize: 3,
+ wantStr: "",
+ wantErr: errShortInternal,
+ wantIter: 1,
+ },
+
+ {
+ desc: "short internal src buffer after short dst buffer; test lastFull",
+ t: mkChain(rleDecode{}, 5, rleEncode{}),
+ src: "2a1b4c6d",
+ dstSize: 4,
+ srcSize: 100,
+ wantStr: "2a1b",
+ wantErr: errShortInternal,
+ },
+
+ {
+ desc: "short src buffer",
+ t: mkChain(rleEncode{}, 5, Nop),
+ src: "abbcccddddeeeee",
+ dstSize: 4,
+ srcSize: 4,
+ ioSize: 4,
+ wantStr: "1a2b3c",
+ wantErr: ErrShortSrc,
+ },
+
+ {
+ desc: "process all in one go",
+ t: mkChain(rleEncode{}, 5, Nop),
+ src: "abbcccddddeeeeeffffff",
+ dstSize: 100,
+ srcSize: 100,
+ wantStr: "1a2b3c4d5e6f",
+ wantErr: nil,
+ wantIter: 1,
+ },
+
+ {
+ desc: "complete processing downstream after error",
+ t: mkChain(dontMentionX{}, 2, rleDecode{}, 5, Nop),
+ src: "3a4b5eX",
+ dstSize: 100,
+ srcSize: 100,
+ ioSize: 100,
+ wantStr: "aaabbbbeeeee",
+ wantErr: errYouMentionedX,
+ },
+
+ {
+ desc: "return downstream fatal errors first (followed by short dst)",
+ t: mkChain(dontMentionX{}, 8, rleDecode{}, 4, Nop),
+ src: "3a4b5eX",
+ dstSize: 100,
+ srcSize: 100,
+ ioSize: 100,
+ wantStr: "aaabbbb",
+ wantErr: errShortInternal,
+ },
+
+ {
+ desc: "return downstream fatal errors first (followed by short src)",
+ t: mkChain(dontMentionX{}, 5, Nop, 1, rleDecode{}),
+ src: "1a5bX",
+ dstSize: 100,
+ srcSize: 100,
+ ioSize: 100,
+ wantStr: "",
+ wantErr: errShortInternal,
+ },
+
+ {
+ desc: "short internal",
+ t: mkChain(Nop, 11, rleEncode{}, 3, Nop),
+ src: "abbcccddddddddddeeeeeeeeeeeg",
+ dstSize: 3,
+ srcSize: 100,
+ wantStr: "1a2b3c10d",
+ wantErr: errShortInternal,
+ },
+ }
+}
+
+func doTransform(tc testCase) (res string, iter int, err error) {
+ tc.t.Reset()
+ dst := make([]byte, tc.dstSize)
+ out, in := make([]byte, 0, 2*len(tc.src)), []byte(tc.src)
+ for {
+ iter++
+ src, atEOF := in, true
+ if len(src) > tc.srcSize {
+ src, atEOF = src[:tc.srcSize], false
+ }
+ nDst, nSrc, err := tc.t.Transform(dst, src, atEOF)
+ out = append(out, dst[:nDst]...)
+ in = in[nSrc:]
+ switch {
+ case err == nil && len(in) != 0:
+ case err == ErrShortSrc && nSrc > 0:
+ case err == ErrShortDst && (nDst > 0 || nSrc > 0):
+ default:
+ return string(out), iter, err
+ }
+ }
+}
+
+func TestChain(t *testing.T) {
+ if c, ok := Chain().(nop); !ok {
+ t.Errorf("empty chain: %v; want Nop", c)
+ }
+
+ // Test Chain for a single Transformer.
+ for _, tc := range testCases {
+ tc.t = Chain(tc.t)
+ str, _, err := doTransform(tc)
+ if str != tc.wantStr || err != tc.wantErr {
+ t.Errorf("%s:\ngot %q, %v\nwant %q, %v", tc, str, err, tc.wantStr, tc.wantErr)
+ }
+ }
+
+ tests := chainTests()
+ sizes := []int{1, 2, 3, 4, 5, 7, 10, 100, 1000}
+ addTest := func(tc testCase, t *chain) {
+ if t.link[0].t != tc.t && tc.wantErr == ErrShortSrc {
+ tc.wantErr = errShortInternal
+ }
+ if t.link[len(t.link)-2].t != tc.t && tc.wantErr == ErrShortDst {
+ tc.wantErr = errShortInternal
+ }
+ tc.t = t
+ tests = append(tests, tc)
+ }
+ for _, tc := range testCases {
+ for _, sz := range sizes {
+ tt := tc
+ tt.dstSize = sz
+ addTest(tt, mkChain(tc.t, tc.dstSize, Nop))
+ addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 2, Nop))
+ addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop))
+ if sz >= tc.dstSize && (tc.wantErr != ErrShortDst || sz == tc.dstSize) {
+ addTest(tt, mkChain(Nop, tc.srcSize, tc.t))
+ addTest(tt, mkChain(Nop, 100, Nop, tc.srcSize, tc.t))
+ }
+ }
+ }
+ for _, tc := range testCases {
+ tt := tc
+ tt.dstSize = 1
+ tt.wantStr = ""
+ addTest(tt, mkChain(tc.t, tc.dstSize, Discard))
+ addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Discard))
+ addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, tc.dstSize, Discard))
+ }
+ for _, tc := range testCases {
+ tt := tc
+ tt.dstSize = 100
+ tt.wantStr = strings.Replace(tc.src, "0f", "", -1)
+ // Chain encoders and decoders.
+ if _, ok := tc.t.(rleEncode); ok && tc.wantErr == nil {
+ addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 1000, rleDecode{}))
+ addTest(tt, mkChain(tc.t, tc.dstSize, Nop, tc.dstSize, rleDecode{}))
+ addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleDecode{}))
+ // decoding needs larger destinations
+ addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, rleDecode{}, 100, Nop))
+ addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleDecode{}, 100, Nop))
+ } else if _, ok := tc.t.(rleDecode); ok && tc.wantErr == nil {
+ // The internal buffer size may need to be the sum of the maximum segment
+ // size of the two encoders!
+ addTest(tt, mkChain(tc.t, 2*tc.dstSize, rleEncode{}))
+ addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 101, rleEncode{}))
+ addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleEncode{}))
+ addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 200, rleEncode{}, 100, Nop))
+ }
+ }
+ for _, tc := range tests {
+ str, iter, err := doTransform(tc)
+ mi := tc.wantIter != 0 && tc.wantIter != iter
+ if str != tc.wantStr || err != tc.wantErr || mi {
+ t.Errorf("%s:\ngot iter:%d, %q, %v\nwant iter:%d, %q, %v", tc, iter, str, err, tc.wantIter, tc.wantStr, tc.wantErr)
+ }
+ break
+ }
+}
+
+func TestRemoveFunc(t *testing.T) {
+ filter := RemoveFunc(func(r rune) bool {
+ return strings.IndexRune("ab\u0300\u1234,", r) != -1
+ })
+ tests := []testCase{
+ {
+ src: ",",
+ wantStr: "",
+ },
+
+ {
+ src: "c",
+ wantStr: "c",
+ },
+
+ {
+ src: "\u2345",
+ wantStr: "\u2345",
+ },
+
+ {
+ src: "tschüß",
+ wantStr: "tschüß",
+ },
+
+ {
+ src: ",до,свидания,",
+ wantStr: "досвидания",
+ },
+
+ {
+ src: "a\xbd\xb2=\xbc ⌘",
+ wantStr: "\uFFFD\uFFFD=\uFFFD ⌘",
+ },
+
+ {
+ // If we didn't replace illegal bytes with RuneError, the result
+ // would be \u0300 or the code would need to be more complex.
+ src: "\xcc\u0300\x80",
+ wantStr: "\uFFFD\uFFFD",
+ },
+
+ {
+ src: "\xcc\u0300\x80",
+ dstSize: 3,
+ wantStr: "\uFFFD\uFFFD",
+ wantIter: 2,
+ },
+
+ {
+ // Test a long buffer greater than the internal buffer size
+ src: "hello\xcc\xcc\xccworld",
+ srcSize: 13,
+ wantStr: "hello\uFFFD\uFFFD\uFFFDworld",
+ wantIter: 1,
+ },
+
+ {
+ src: "\u2345",
+ dstSize: 2,
+ wantStr: "",
+ wantErr: ErrShortDst,
+ },
+
+ {
+ src: "\xcc",
+ dstSize: 2,
+ wantStr: "",
+ wantErr: ErrShortDst,
+ },
+
+ {
+ src: "\u0300",
+ dstSize: 2,
+ srcSize: 1,
+ wantStr: "",
+ wantErr: ErrShortSrc,
+ },
+
+ {
+ t: RemoveFunc(func(r rune) bool {
+ return r == utf8.RuneError
+ }),
+ src: "\xcc\u0300\x80",
+ wantStr: "\u0300",
+ },
+ }
+
+ for _, tc := range tests {
+ tc.desc = tc.src
+ if tc.t == nil {
+ tc.t = filter
+ }
+ if tc.dstSize == 0 {
+ tc.dstSize = 100
+ }
+ if tc.srcSize == 0 {
+ tc.srcSize = 100
+ }
+ str, iter, err := doTransform(tc)
+ mi := tc.wantIter != 0 && tc.wantIter != iter
+ if str != tc.wantStr || err != tc.wantErr || mi {
+ t.Errorf("%+q:\ngot iter:%d, %+q, %v\nwant iter:%d, %+q, %v", tc.src, iter, str, err, tc.wantIter, tc.wantStr, tc.wantErr)
+ }
+
+ tc.src = str
+ idem, _, _ := doTransform(tc)
+ if str != idem {
+ t.Errorf("%+q: found %+q; want %+q", tc.src, idem, str)
+ }
+ }
+}
+
+func testString(t *testing.T, f func(Transformer, string) (string, int, error)) {
+ for _, tt := range append(testCases, chainTests()...) {
+ if tt.desc == "allowStutter = true" {
+ // We don't have control over the buffer size, so we eliminate tests
+ // that depend on a specific buffer size being set.
+ continue
+ }
+ if tt.wantErr == ErrShortDst || tt.wantErr == ErrShortSrc {
+ // The result string will be different.
+ continue
+ }
+ testtext.Run(t, tt.desc, func(t *testing.T) {
+ got, n, err := f(tt.t, tt.src)
+ if tt.wantErr != err {
+ t.Errorf("error: got %v; want %v", err, tt.wantErr)
+ }
+ // Check that err == nil implies that n == len(tt.src). Note that vice
+ // versa isn't necessarily true.
+ if err == nil && n != len(tt.src) {
+ t.Errorf("err == nil: got %d bytes, want %d", n, err)
+ }
+ if got != tt.wantStr {
+ t.Errorf("string: got %q; want %q", got, tt.wantStr)
+ }
+ })
+ }
+}
+
+func TestBytes(t *testing.T) {
+ testString(t, func(z Transformer, s string) (string, int, error) {
+ b, n, err := Bytes(z, []byte(s))
+ return string(b), n, err
+ })
+}
+
+func TestAppend(t *testing.T) {
+ // Create a bunch of subtests for different buffer sizes.
+ testCases := [][]byte{
+ nil,
+ make([]byte, 0, 0),
+ make([]byte, 0, 1),
+ make([]byte, 1, 1),
+ make([]byte, 1, 5),
+ make([]byte, 100, 100),
+ make([]byte, 100, 200),
+ }
+ for _, tc := range testCases {
+ testString(t, func(z Transformer, s string) (string, int, error) {
+ b, n, err := Append(z, tc, []byte(s))
+ return string(b[len(tc):]), n, err
+ })
+ }
+}
+
+func TestString(t *testing.T) {
+ testtext.Run(t, "transform", func(t *testing.T) { testString(t, String) })
+
+ // Overrun the internal destination buffer.
+ for i, s := range []string{
+ aaa[:1*initialBufSize-1],
+ aaa[:1*initialBufSize+0],
+ aaa[:1*initialBufSize+1],
+ AAA[:1*initialBufSize-1],
+ AAA[:1*initialBufSize+0],
+ AAA[:1*initialBufSize+1],
+ AAA[:2*initialBufSize-1],
+ AAA[:2*initialBufSize+0],
+ AAA[:2*initialBufSize+1],
+ aaa[:1*initialBufSize-2] + "A",
+ aaa[:1*initialBufSize-1] + "A",
+ aaa[:1*initialBufSize+0] + "A",
+ aaa[:1*initialBufSize+1] + "A",
+ } {
+ testtext.Run(t, fmt.Sprint("dst buffer test using lower/", i), func(t *testing.T) {
+ got, _, _ := String(lowerCaseASCII{}, s)
+ if want := strings.ToLower(s); got != want {
+ t.Errorf("got %s (%d); want %s (%d)", got, len(got), want, len(want))
+ }
+ })
+ }
+
+ // Overrun the internal source buffer.
+ for i, s := range []string{
+ aaa[:1*initialBufSize-1],
+ aaa[:1*initialBufSize+0],
+ aaa[:1*initialBufSize+1],
+ aaa[:2*initialBufSize+1],
+ aaa[:2*initialBufSize+0],
+ aaa[:2*initialBufSize+1],
+ } {
+ testtext.Run(t, fmt.Sprint("src buffer test using rleEncode/", i), func(t *testing.T) {
+ got, _, _ := String(rleEncode{}, s)
+ if want := fmt.Sprintf("%da", len(s)); got != want {
+ t.Errorf("got %s (%d); want %s (%d)", got, len(got), want, len(want))
+ }
+ })
+ }
+
+ // Test allocations for non-changing strings.
+ // Note we still need to allocate a single buffer.
+ for i, s := range []string{
+ "",
+ "123456789",
+ aaa[:initialBufSize-1],
+ aaa[:initialBufSize+0],
+ aaa[:initialBufSize+1],
+ aaa[:10*initialBufSize],
+ } {
+ testtext.Run(t, fmt.Sprint("alloc/", i), func(t *testing.T) {
+ if n := testtext.AllocsPerRun(5, func() { String(&lowerCaseASCIILookahead{}, s) }); n > 1 {
+ t.Errorf("#allocs was %f; want 1", n)
+ }
+ })
+ }
+}
+
+// TestBytesAllocation tests that buffer growth stays limited with the trickler
+// transformer, which behaves oddly but within spec. In case buffer growth is
+// not correctly handled, the test will either panic with a failed allocation or
+// thrash. To ensure the tests terminate under the last condition, we time out
+// after some sufficiently long period of time.
+func TestBytesAllocation(t *testing.T) {
+ done := make(chan bool)
+ go func() {
+ in := bytes.Repeat([]byte{'a'}, 1000)
+ tr := trickler(make([]byte, 1))
+ Bytes(&tr, in)
+ done <- true
+ }()
+ select {
+ case <-done:
+ case <-time.After(3 * time.Second):
+ t.Error("time out, likely due to excessive allocation")
+ }
+}
+
+// TestStringAllocation tests that buffer growth stays limited with the trickler
+// transformer, which behaves oddly but within spec. In case buffer growth is
+// not correctly handled, the test will either panic with a failed allocation or
+// thrash. To ensure the tests terminate under the last condition, we time out
+// after some sufficiently long period of time.
+func TestStringAllocation(t *testing.T) {
+ done := make(chan bool)
+ go func() {
+ tr := trickler(make([]byte, 1))
+ String(&tr, aaa[:1000])
+ done <- true
+ }()
+ select {
+ case <-done:
+ case <-time.After(3 * time.Second):
+ t.Error("time out, likely due to excessive allocation")
+ }
+}
+
+func BenchmarkStringLowerEmpty(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ String(&lowerCaseASCIILookahead{}, "")
+ }
+}
+
+func BenchmarkStringLowerIdentical(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ String(&lowerCaseASCIILookahead{}, aaa[:4096])
+ }
+}
+
+func BenchmarkStringLowerChanged(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ String(&lowerCaseASCIILookahead{}, AAA[:4096])
+ }
+}
+
+var (
+ aaa = strings.Repeat("a", 4096)
+ AAA = strings.Repeat("A", 4096)
+)