1// Copyright 2013 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 norm
 6
 7import (
 8	"unicode/utf8"
 9
10	"golang.org/x/text/transform"
11)
12
13// Reset implements the Reset method of the transform.Transformer interface.
14func (Form) Reset() {}
15
16// Transform implements the Transform method of the transform.Transformer
17// interface. It may need to write segments of up to MaxSegmentSize at once.
18// Users should either catch ErrShortDst and allow dst to grow or have dst be at
19// least of size MaxTransformChunkSize to be guaranteed of progress.
20func (f Form) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
21	// Cap the maximum number of src bytes to check.
22	b := src
23	eof := atEOF
24	if ns := len(dst); ns < len(b) {
25		err = transform.ErrShortDst
26		eof = false
27		b = b[:ns]
28	}
29	i, ok := formTable[f].quickSpan(inputBytes(b), 0, len(b), eof)
30	n := copy(dst, b[:i])
31	if !ok {
32		nDst, nSrc, err = f.transform(dst[n:], src[n:], atEOF)
33		return nDst + n, nSrc + n, err
34	}
35
36	if err == nil && n < len(src) && !atEOF {
37		err = transform.ErrShortSrc
38	}
39	return n, n, err
40}
41
42func flushTransform(rb *reorderBuffer) bool {
43	// Write out (must fully fit in dst, or else it is an ErrShortDst).
44	if len(rb.out) < rb.nrune*utf8.UTFMax {
45		return false
46	}
47	rb.out = rb.out[rb.flushCopy(rb.out):]
48	return true
49}
50
51var errs = []error{nil, transform.ErrShortDst, transform.ErrShortSrc}
52
53// transform implements the transform.Transformer interface. It is only called
54// when quickSpan does not pass for a given string.
55func (f Form) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
56	// TODO: get rid of reorderBuffer. See CL 23460044.
57	rb := reorderBuffer{}
58	rb.init(f, src)
59	for {
60		// Load segment into reorder buffer.
61		rb.setFlusher(dst[nDst:], flushTransform)
62		end := decomposeSegment(&rb, nSrc, atEOF)
63		if end < 0 {
64			return nDst, nSrc, errs[-end]
65		}
66		nDst = len(dst) - len(rb.out)
67		nSrc = end
68
69		// Next quickSpan.
70		end = rb.nsrc
71		eof := atEOF
72		if n := nSrc + len(dst) - nDst; n < end {
73			err = transform.ErrShortDst
74			end = n
75			eof = false
76		}
77		end, ok := rb.f.quickSpan(rb.src, nSrc, end, eof)
78		n := copy(dst[nDst:], rb.src.bytes[nSrc:end])
79		nSrc += n
80		nDst += n
81		if ok {
82			if err == nil && n < rb.nsrc && !atEOF {
83				err = transform.ErrShortSrc
84			}
85			return nDst, nSrc, err
86		}
87	}
88}