| // 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 width | |
| import ( | |
| "unicode/utf8" | |
| "golang.org/x/text/transform" | |
| ) | |
| type foldTransform struct { | |
| transform.NopResetter | |
| } | |
| func (foldTransform) Span(src []byte, atEOF bool) (n int, err error) { | |
| for n < len(src) { | |
| if src[n] < utf8.RuneSelf { | |
| // ASCII fast path. | |
| for n++; n < len(src) && src[n] < utf8.RuneSelf; n++ { | |
| } | |
| continue | |
| } | |
| v, size := trie.lookup(src[n:]) | |
| if size == 0 { // incomplete UTF-8 encoding | |
| if !atEOF { | |
| err = transform.ErrShortSrc | |
| } else { | |
| n = len(src) | |
| } | |
| break | |
| } | |
| if elem(v)&tagNeedsFold != 0 { | |
| err = transform.ErrEndOfSpan | |
| break | |
| } | |
| n += size | |
| } | |
| return n, err | |
| } | |
| func (foldTransform) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { | |
| for nSrc < len(src) { | |
| if src[nSrc] < utf8.RuneSelf { | |
| // ASCII fast path. | |
| start, end := nSrc, len(src) | |
| if d := len(dst) - nDst; d < end-start { | |
| end = nSrc + d | |
| } | |
| for nSrc++; nSrc < end && src[nSrc] < utf8.RuneSelf; nSrc++ { | |
| } | |
| n := copy(dst[nDst:], src[start:nSrc]) | |
| if nDst += n; nDst == len(dst) { | |
| nSrc = start + n | |
| if nSrc == len(src) { | |
| return nDst, nSrc, nil | |
| } | |
| if src[nSrc] < utf8.RuneSelf { | |
| return nDst, nSrc, transform.ErrShortDst | |
| } | |
| } | |
| continue | |
| } | |
| v, size := trie.lookup(src[nSrc:]) | |
| if size == 0 { // incomplete UTF-8 encoding | |
| if !atEOF { | |
| return nDst, nSrc, transform.ErrShortSrc | |
| } | |
| size = 1 // gobble 1 byte | |
| } | |
| if elem(v)&tagNeedsFold == 0 { | |
| if size != copy(dst[nDst:], src[nSrc:nSrc+size]) { | |
| return nDst, nSrc, transform.ErrShortDst | |
| } | |
| nDst += size | |
| } else { | |
| data := inverseData[byte(v)] | |
| if len(dst)-nDst < int(data[0]) { | |
| return nDst, nSrc, transform.ErrShortDst | |
| } | |
| i := 1 | |
| for end := int(data[0]); i < end; i++ { | |
| dst[nDst] = data[i] | |
| nDst++ | |
| } | |
| dst[nDst] = data[i] ^ src[nSrc+size-1] | |
| nDst++ | |
| } | |
| nSrc += size | |
| } | |
| return nDst, nSrc, nil | |
| } | |
| type narrowTransform struct { | |
| transform.NopResetter | |
| } | |
| func (narrowTransform) Span(src []byte, atEOF bool) (n int, err error) { | |
| for n < len(src) { | |
| if src[n] < utf8.RuneSelf { | |
| // ASCII fast path. | |
| for n++; n < len(src) && src[n] < utf8.RuneSelf; n++ { | |
| } | |
| continue | |
| } | |
| v, size := trie.lookup(src[n:]) | |
| if size == 0 { // incomplete UTF-8 encoding | |
| if !atEOF { | |
| err = transform.ErrShortSrc | |
| } else { | |
| n = len(src) | |
| } | |
| break | |
| } | |
| if k := elem(v).kind(); byte(v) == 0 || k != EastAsianFullwidth && k != EastAsianWide && k != EastAsianAmbiguous { | |
| } else { | |
| err = transform.ErrEndOfSpan | |
| break | |
| } | |
| n += size | |
| } | |
| return n, err | |
| } | |
| func (narrowTransform) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { | |
| for nSrc < len(src) { | |
| if src[nSrc] < utf8.RuneSelf { | |
| // ASCII fast path. | |
| start, end := nSrc, len(src) | |
| if d := len(dst) - nDst; d < end-start { | |
| end = nSrc + d | |
| } | |
| for nSrc++; nSrc < end && src[nSrc] < utf8.RuneSelf; nSrc++ { | |
| } | |
| n := copy(dst[nDst:], src[start:nSrc]) | |
| if nDst += n; nDst == len(dst) { | |
| nSrc = start + n | |
| if nSrc == len(src) { | |
| return nDst, nSrc, nil | |
| } | |
| if src[nSrc] < utf8.RuneSelf { | |
| return nDst, nSrc, transform.ErrShortDst | |
| } | |
| } | |
| continue | |
| } | |
| v, size := trie.lookup(src[nSrc:]) | |
| if size == 0 { // incomplete UTF-8 encoding | |
| if !atEOF { | |
| return nDst, nSrc, transform.ErrShortSrc | |
| } | |
| size = 1 // gobble 1 byte | |
| } | |
| if k := elem(v).kind(); byte(v) == 0 || k != EastAsianFullwidth && k != EastAsianWide && k != EastAsianAmbiguous { | |
| if size != copy(dst[nDst:], src[nSrc:nSrc+size]) { | |
| return nDst, nSrc, transform.ErrShortDst | |
| } | |
| nDst += size | |
| } else { | |
| data := inverseData[byte(v)] | |
| if len(dst)-nDst < int(data[0]) { | |
| return nDst, nSrc, transform.ErrShortDst | |
| } | |
| i := 1 | |
| for end := int(data[0]); i < end; i++ { | |
| dst[nDst] = data[i] | |
| nDst++ | |
| } | |
| dst[nDst] = data[i] ^ src[nSrc+size-1] | |
| nDst++ | |
| } | |
| nSrc += size | |
| } | |
| return nDst, nSrc, nil | |
| } | |
| type wideTransform struct { | |
| transform.NopResetter | |
| } | |
| func (wideTransform) Span(src []byte, atEOF bool) (n int, err error) { | |
| for n < len(src) { | |
| // TODO: Consider ASCII fast path. Special-casing ASCII handling can | |
| // reduce the ns/op of BenchmarkWideASCII by about 30%. This is probably | |
| // not enough to warrant the extra code and complexity. | |
| v, size := trie.lookup(src[n:]) | |
| if size == 0 { // incomplete UTF-8 encoding | |
| if !atEOF { | |
| err = transform.ErrShortSrc | |
| } else { | |
| n = len(src) | |
| } | |
| break | |
| } | |
| if k := elem(v).kind(); byte(v) == 0 || k != EastAsianHalfwidth && k != EastAsianNarrow { | |
| } else { | |
| err = transform.ErrEndOfSpan | |
| break | |
| } | |
| n += size | |
| } | |
| return n, err | |
| } | |
| func (wideTransform) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { | |
| for nSrc < len(src) { | |
| // TODO: Consider ASCII fast path. Special-casing ASCII handling can | |
| // reduce the ns/op of BenchmarkWideASCII by about 30%. This is probably | |
| // not enough to warrant the extra code and complexity. | |
| v, size := trie.lookup(src[nSrc:]) | |
| if size == 0 { // incomplete UTF-8 encoding | |
| if !atEOF { | |
| return nDst, nSrc, transform.ErrShortSrc | |
| } | |
| size = 1 // gobble 1 byte | |
| } | |
| if k := elem(v).kind(); byte(v) == 0 || k != EastAsianHalfwidth && k != EastAsianNarrow { | |
| if size != copy(dst[nDst:], src[nSrc:nSrc+size]) { | |
| return nDst, nSrc, transform.ErrShortDst | |
| } | |
| nDst += size | |
| } else { | |
| data := inverseData[byte(v)] | |
| if len(dst)-nDst < int(data[0]) { | |
| return nDst, nSrc, transform.ErrShortDst | |
| } | |
| i := 1 | |
| for end := int(data[0]); i < end; i++ { | |
| dst[nDst] = data[i] | |
| nDst++ | |
| } | |
| dst[nDst] = data[i] ^ src[nSrc+size-1] | |
| nDst++ | |
| } | |
| nSrc += size | |
| } | |
| return nDst, nSrc, nil | |
| } |