mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-07 10:17:51 +00:00
Add vendor folder to git
This commit is contained in:
parent
66cf5eaafb
commit
183585f56f
6916 changed files with 2629581 additions and 1 deletions
10
vendor/golang.org/x/text/.gitattributes
generated
vendored
Normal file
10
vendor/golang.org/x/text/.gitattributes
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Treat all files in this repo as binary, with no git magic updating
|
||||
# line endings. Windows users contributing to Go will need to use a
|
||||
# modern version of git and editors capable of LF line endings.
|
||||
#
|
||||
# We'll prevent accidental CRLF line endings from entering the repo
|
||||
# via the git-review gofmt checks.
|
||||
#
|
||||
# See golang.org/issue/9281
|
||||
|
||||
* -text
|
||||
2
vendor/golang.org/x/text/.gitignore
generated
vendored
Normal file
2
vendor/golang.org/x/text/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Add no patterns to .gitignore except for files generated by the build.
|
||||
last-change
|
||||
3
vendor/golang.org/x/text/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/text/AUTHORS
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
||||
31
vendor/golang.org/x/text/CONTRIBUTING.md
generated
vendored
Normal file
31
vendor/golang.org/x/text/CONTRIBUTING.md
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Contributing to Go
|
||||
|
||||
Go is an open source project.
|
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
|
||||
## Filing issues
|
||||
|
||||
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
|
||||
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
|
||||
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
||||
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||
|
||||
## Contributing code
|
||||
|
||||
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
||||
before sending patches.
|
||||
|
||||
**We do not accept GitHub pull requests**
|
||||
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
|
||||
|
||||
Unless otherwise noted, the Go source files are distributed under
|
||||
the BSD-style license found in the LICENSE file.
|
||||
|
||||
3
vendor/golang.org/x/text/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/text/CONTRIBUTORS
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
||||
27
vendor/golang.org/x/text/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/text/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
22
vendor/golang.org/x/text/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/text/PATENTS
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
3
vendor/golang.org/x/text/README
generated
vendored
Normal file
3
vendor/golang.org/x/text/README
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
This repository holds supplementary Go libraries for text processing, many involving Unicode.
|
||||
|
||||
To submit changes to this repository, see http://golang.org/doc/contribute.html.
|
||||
129
vendor/golang.org/x/text/cases/cases.go
generated
vendored
Normal file
129
vendor/golang.org/x/text/cases/cases.go
generated
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
//go:generate go run gen.go gen_trieval.go
|
||||
|
||||
// Package cases provides general and language-specific case mappers.
|
||||
package cases // import "golang.org/x/text/cases"
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// References:
|
||||
// - Unicode Reference Manual Chapter 3.13, 4.2, and 5.18.
|
||||
// - http://www.unicode.org/reports/tr29/
|
||||
// - http://www.unicode.org/Public/6.3.0/ucd/CaseFolding.txt
|
||||
// - http://www.unicode.org/Public/6.3.0/ucd/SpecialCasing.txt
|
||||
// - http://www.unicode.org/Public/6.3.0/ucd/DerivedCoreProperties.txt
|
||||
// - http://www.unicode.org/Public/6.3.0/ucd/auxiliary/WordBreakProperty.txt
|
||||
// - http://www.unicode.org/Public/6.3.0/ucd/auxiliary/WordBreakTest.txt
|
||||
// - http://userguide.icu-project.org/transforms/casemappings
|
||||
|
||||
// TODO:
|
||||
// - Case folding
|
||||
// - Wide and Narrow?
|
||||
// - Segmenter option for title casing.
|
||||
// - ASCII fast paths
|
||||
// - Encode Soft-Dotted property within trie somehow.
|
||||
|
||||
// A Caser transforms given input to a certain case. It implements
|
||||
// transform.Transformer.
|
||||
//
|
||||
// A Caser may be stateful and should therefore not be shared between
|
||||
// goroutines.
|
||||
type Caser struct {
|
||||
t transform.Transformer
|
||||
}
|
||||
|
||||
// Bytes returns a new byte slice with the result of converting b to the case
|
||||
// form implemented by c.
|
||||
func (c Caser) Bytes(b []byte) []byte {
|
||||
b, _, _ = transform.Bytes(c.t, b)
|
||||
return b
|
||||
}
|
||||
|
||||
// String returns a string with the result of transforming s to the case form
|
||||
// implemented by c.
|
||||
func (c Caser) String(s string) string {
|
||||
s, _, _ = transform.String(c.t, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Reset resets the Caser to be reused for new input after a previous call to
|
||||
// Transform.
|
||||
func (c Caser) Reset() { c.t.Reset() }
|
||||
|
||||
// Transform implements the Transformer interface and transforms the given input
|
||||
// to the case form implemented by c.
|
||||
func (c Caser) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
return c.t.Transform(dst, src, atEOF)
|
||||
}
|
||||
|
||||
// Upper returns a Caser for language-specific uppercasing.
|
||||
func Upper(t language.Tag, opts ...Option) Caser {
|
||||
return Caser{makeUpper(t, getOpts(opts...))}
|
||||
}
|
||||
|
||||
// Lower returns a Caser for language-specific lowercasing.
|
||||
func Lower(t language.Tag, opts ...Option) Caser {
|
||||
return Caser{makeLower(t, getOpts(opts...))}
|
||||
}
|
||||
|
||||
// Title returns a Caser for language-specific title casing. It uses an
|
||||
// approximation of the default Unicode Word Break algorithm.
|
||||
func Title(t language.Tag, opts ...Option) Caser {
|
||||
return Caser{makeTitle(t, getOpts(opts...))}
|
||||
}
|
||||
|
||||
// Fold returns a Caser that implements Unicode case folding. The returned Caser
|
||||
// is stateless and safe to use concurrently by multiple goroutines.
|
||||
//
|
||||
// Case folding does not normalize the input and may not preserve a normal form.
|
||||
// Use the collate or search package for more convenient and linguistically
|
||||
// sound comparisons. Use unicode/precis for string comparisons where security
|
||||
// aspects are a concern.
|
||||
func Fold(opts ...Option) Caser {
|
||||
return Caser{makeFold(getOpts(opts...))}
|
||||
}
|
||||
|
||||
// An Option is used to modify the behavior of a Caser.
|
||||
type Option func(o *options)
|
||||
|
||||
var (
|
||||
// NoLower disables the lowercasing of non-leading letters for a title
|
||||
// caser.
|
||||
NoLower Option = noLower
|
||||
|
||||
// Compact omits mappings in case folding for characters that would grow the
|
||||
// input. (Unimplemented.)
|
||||
Compact Option = compact
|
||||
)
|
||||
|
||||
// TODO: option to preserve a normal form, if applicable?
|
||||
|
||||
type options struct {
|
||||
noLower bool
|
||||
simple bool
|
||||
|
||||
// TODO: segmenter, max ignorable, alternative versions, etc.
|
||||
|
||||
noFinalSigma bool // Only used for testing.
|
||||
}
|
||||
|
||||
func getOpts(o ...Option) (res options) {
|
||||
for _, f := range o {
|
||||
f(&res)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func noLower(o *options) {
|
||||
o.noLower = true
|
||||
}
|
||||
|
||||
func compact(o *options) {
|
||||
o.simple = true
|
||||
}
|
||||
281
vendor/golang.org/x/text/cases/context.go
generated
vendored
Normal file
281
vendor/golang.org/x/text/cases/context.go
generated
vendored
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
// Copyright 2014 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 cases
|
||||
|
||||
import (
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// A context is used for iterating over source bytes, fetching case info and
|
||||
// writing to a destination buffer.
|
||||
//
|
||||
// Casing operations may need more than one rune of context to decide how a rune
|
||||
// should be cased. Casing implementations should call checkpoint on context
|
||||
// whenever it is known to be safe to return the runes processed so far.
|
||||
//
|
||||
// It is recommended for implementations to not allow for more than 30 case
|
||||
// ignorables as lookahead (analogous to the limit in norm) and to use state if
|
||||
// unbounded lookahead is needed for cased runes.
|
||||
type context struct {
|
||||
dst, src []byte
|
||||
atEOF bool
|
||||
|
||||
pDst int // pDst points past the last written rune in dst.
|
||||
pSrc int // pSrc points to the start of the currently scanned rune.
|
||||
|
||||
// checkpoints safe to return in Transform, where nDst <= pDst and nSrc <= pSrc.
|
||||
nDst, nSrc int
|
||||
err error
|
||||
|
||||
sz int // size of current rune
|
||||
info info // case information of currently scanned rune
|
||||
|
||||
// State preserved across calls to Transform.
|
||||
isMidWord bool // false if next cased letter needs to be title-cased.
|
||||
}
|
||||
|
||||
func (c *context) Reset() {
|
||||
c.isMidWord = false
|
||||
}
|
||||
|
||||
// ret returns the return values for the Transform method. It checks whether
|
||||
// there were insufficient bytes in src to complete and introduces an error
|
||||
// accordingly, if necessary.
|
||||
func (c *context) ret() (nDst, nSrc int, err error) {
|
||||
if c.err != nil || c.nSrc == len(c.src) {
|
||||
return c.nDst, c.nSrc, c.err
|
||||
}
|
||||
// This point is only reached by mappers if there was no short destination
|
||||
// buffer. This means that the source buffer was exhausted and that c.sz was
|
||||
// set to 0 by next.
|
||||
if c.atEOF && c.pSrc == len(c.src) {
|
||||
return c.pDst, c.pSrc, nil
|
||||
}
|
||||
return c.nDst, c.nSrc, transform.ErrShortSrc
|
||||
}
|
||||
|
||||
// checkpoint sets the return value buffer points for Transform to the current
|
||||
// positions.
|
||||
func (c *context) checkpoint() {
|
||||
if c.err == nil {
|
||||
c.nDst, c.nSrc = c.pDst, c.pSrc+c.sz
|
||||
}
|
||||
}
|
||||
|
||||
// unreadRune causes the last rune read by next to be reread on the next
|
||||
// invocation of next. Only one unreadRune may be called after a call to next.
|
||||
func (c *context) unreadRune() {
|
||||
c.sz = 0
|
||||
}
|
||||
|
||||
func (c *context) next() bool {
|
||||
c.pSrc += c.sz
|
||||
if c.pSrc == len(c.src) || c.err != nil {
|
||||
c.info, c.sz = 0, 0
|
||||
return false
|
||||
}
|
||||
v, sz := trie.lookup(c.src[c.pSrc:])
|
||||
c.info, c.sz = info(v), sz
|
||||
if c.sz == 0 {
|
||||
if c.atEOF {
|
||||
// A zero size means we have an incomplete rune. If we are atEOF,
|
||||
// this means it is an illegal rune, which we will consume one
|
||||
// byte at a time.
|
||||
c.sz = 1
|
||||
} else {
|
||||
c.err = transform.ErrShortSrc
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// writeBytes adds bytes to dst.
|
||||
func (c *context) writeBytes(b []byte) bool {
|
||||
if len(c.dst)-c.pDst < len(b) {
|
||||
c.err = transform.ErrShortDst
|
||||
return false
|
||||
}
|
||||
// This loop is faster than using copy.
|
||||
for _, ch := range b {
|
||||
c.dst[c.pDst] = ch
|
||||
c.pDst++
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// writeString writes the given string to dst.
|
||||
func (c *context) writeString(s string) bool {
|
||||
if len(c.dst)-c.pDst < len(s) {
|
||||
c.err = transform.ErrShortDst
|
||||
return false
|
||||
}
|
||||
// This loop is faster than using copy.
|
||||
for i := 0; i < len(s); i++ {
|
||||
c.dst[c.pDst] = s[i]
|
||||
c.pDst++
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// copy writes the current rune to dst.
|
||||
func (c *context) copy() bool {
|
||||
return c.writeBytes(c.src[c.pSrc : c.pSrc+c.sz])
|
||||
}
|
||||
|
||||
// copyXOR copies the current rune to dst and modifies it by applying the XOR
|
||||
// pattern of the case info. It is the responsibility of the caller to ensure
|
||||
// that this is a rune with a XOR pattern defined.
|
||||
func (c *context) copyXOR() bool {
|
||||
if !c.copy() {
|
||||
return false
|
||||
}
|
||||
if c.info&xorIndexBit == 0 {
|
||||
// Fast path for 6-bit XOR pattern, which covers most cases.
|
||||
c.dst[c.pDst-1] ^= byte(c.info >> xorShift)
|
||||
} else {
|
||||
// Interpret XOR bits as an index.
|
||||
// TODO: test performance for unrolling this loop. Verify that we have
|
||||
// at least two bytes and at most three.
|
||||
idx := c.info >> xorShift
|
||||
for p := c.pDst - 1; ; p-- {
|
||||
c.dst[p] ^= xorData[idx]
|
||||
idx--
|
||||
if xorData[idx] == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// hasPrefix returns true if src[pSrc:] starts with the given string.
|
||||
func (c *context) hasPrefix(s string) bool {
|
||||
b := c.src[c.pSrc:]
|
||||
if len(b) < len(s) {
|
||||
return false
|
||||
}
|
||||
for i, c := range b[:len(s)] {
|
||||
if c != s[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// caseType returns an info with only the case bits, normalized to either
|
||||
// cLower, cUpper, cTitle or cUncased.
|
||||
func (c *context) caseType() info {
|
||||
cm := c.info & 0x7
|
||||
if cm < 4 {
|
||||
return cm
|
||||
}
|
||||
if cm >= cXORCase {
|
||||
// xor the last bit of the rune with the case type bits.
|
||||
b := c.src[c.pSrc+c.sz-1]
|
||||
return info(b&1) ^ cm&0x3
|
||||
}
|
||||
if cm == cIgnorableCased {
|
||||
return cLower
|
||||
}
|
||||
return cUncased
|
||||
}
|
||||
|
||||
// lower writes the lowercase version of the current rune to dst.
|
||||
func lower(c *context) bool {
|
||||
ct := c.caseType()
|
||||
if c.info&hasMappingMask == 0 || ct == cLower {
|
||||
return c.copy()
|
||||
}
|
||||
if c.info&exceptionBit == 0 {
|
||||
return c.copyXOR()
|
||||
}
|
||||
e := exceptions[c.info>>exceptionShift:]
|
||||
offset := 2 + e[0]&lengthMask // size of header + fold string
|
||||
if nLower := (e[1] >> lengthBits) & lengthMask; nLower != noChange {
|
||||
return c.writeString(e[offset : offset+nLower])
|
||||
}
|
||||
return c.copy()
|
||||
}
|
||||
|
||||
// upper writes the uppercase version of the current rune to dst.
|
||||
func upper(c *context) bool {
|
||||
ct := c.caseType()
|
||||
if c.info&hasMappingMask == 0 || ct == cUpper {
|
||||
return c.copy()
|
||||
}
|
||||
if c.info&exceptionBit == 0 {
|
||||
return c.copyXOR()
|
||||
}
|
||||
e := exceptions[c.info>>exceptionShift:]
|
||||
offset := 2 + e[0]&lengthMask // size of header + fold string
|
||||
// Get length of first special case mapping.
|
||||
n := (e[1] >> lengthBits) & lengthMask
|
||||
if ct == cTitle {
|
||||
// The first special case mapping is for lower. Set n to the second.
|
||||
if n == noChange {
|
||||
n = 0
|
||||
}
|
||||
n, e = e[1]&lengthMask, e[n:]
|
||||
}
|
||||
if n != noChange {
|
||||
return c.writeString(e[offset : offset+n])
|
||||
}
|
||||
return c.copy()
|
||||
}
|
||||
|
||||
// title writes the title case version of the current rune to dst.
|
||||
func title(c *context) bool {
|
||||
ct := c.caseType()
|
||||
if c.info&hasMappingMask == 0 || ct == cTitle {
|
||||
return c.copy()
|
||||
}
|
||||
if c.info&exceptionBit == 0 {
|
||||
if ct == cLower {
|
||||
return c.copyXOR()
|
||||
}
|
||||
return c.copy()
|
||||
}
|
||||
// Get the exception data.
|
||||
e := exceptions[c.info>>exceptionShift:]
|
||||
offset := 2 + e[0]&lengthMask // size of header + fold string
|
||||
|
||||
nFirst := (e[1] >> lengthBits) & lengthMask
|
||||
if nTitle := e[1] & lengthMask; nTitle != noChange {
|
||||
if nFirst != noChange {
|
||||
e = e[nFirst:]
|
||||
}
|
||||
return c.writeString(e[offset : offset+nTitle])
|
||||
}
|
||||
if ct == cLower && nFirst != noChange {
|
||||
// Use the uppercase version instead.
|
||||
return c.writeString(e[offset : offset+nFirst])
|
||||
}
|
||||
// Already in correct case.
|
||||
return c.copy()
|
||||
}
|
||||
|
||||
// foldFull writes the foldFull version of the current rune to dst.
|
||||
func foldFull(c *context) bool {
|
||||
if c.info&hasMappingMask == 0 {
|
||||
return c.copy()
|
||||
}
|
||||
ct := c.caseType()
|
||||
if c.info&exceptionBit == 0 {
|
||||
if ct != cLower || c.info&inverseFoldBit != 0 {
|
||||
return c.copyXOR()
|
||||
}
|
||||
return c.copy()
|
||||
}
|
||||
e := exceptions[c.info>>exceptionShift:]
|
||||
n := e[0] & lengthMask
|
||||
if n == 0 {
|
||||
if ct == cLower {
|
||||
return c.copy()
|
||||
}
|
||||
n = (e[1] >> lengthBits) & lengthMask
|
||||
}
|
||||
return c.writeString(e[2 : 2+n])
|
||||
}
|
||||
423
vendor/golang.org/x/text/cases/context_test.go
generated
vendored
Normal file
423
vendor/golang.org/x/text/cases/context_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
// Copyright 2014 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 cases
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
"golang.org/x/text/unicode/rangetable"
|
||||
)
|
||||
|
||||
// The following definitions are taken directly from Chapter 3 of The Unicode
|
||||
// Standard.
|
||||
|
||||
func propCased(r rune) bool {
|
||||
return propLower(r) || propUpper(r) || unicode.IsTitle(r)
|
||||
}
|
||||
|
||||
func propLower(r rune) bool {
|
||||
return unicode.IsLower(r) || unicode.Is(unicode.Other_Lowercase, r)
|
||||
}
|
||||
|
||||
func propUpper(r rune) bool {
|
||||
return unicode.IsUpper(r) || unicode.Is(unicode.Other_Uppercase, r)
|
||||
}
|
||||
|
||||
func propIgnore(r rune) bool {
|
||||
if unicode.In(r, unicode.Mn, unicode.Me, unicode.Cf, unicode.Lm, unicode.Sk) {
|
||||
return true
|
||||
}
|
||||
return caseIgnorable[r]
|
||||
}
|
||||
|
||||
func hasBreakProp(r rune) bool {
|
||||
// binary search over ranges
|
||||
lo := 0
|
||||
hi := len(breakProp)
|
||||
for lo < hi {
|
||||
m := lo + (hi-lo)/2
|
||||
bp := &breakProp[m]
|
||||
if bp.lo <= r && r <= bp.hi {
|
||||
return true
|
||||
}
|
||||
if r < bp.lo {
|
||||
hi = m
|
||||
} else {
|
||||
lo = m + 1
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func contextFromRune(r rune) *context {
|
||||
c := context{dst: make([]byte, 128), src: []byte(string(r)), atEOF: true}
|
||||
c.next()
|
||||
return &c
|
||||
}
|
||||
|
||||
func TestCaseProperties(t *testing.T) {
|
||||
if unicode.Version != UnicodeVersion {
|
||||
// Properties of existing code points may change by Unicode version, so
|
||||
// we need to skip.
|
||||
t.Skipf("Skipping as core Unicode version %s different than %s", unicode.Version, UnicodeVersion)
|
||||
}
|
||||
assigned := rangetable.Assigned(UnicodeVersion)
|
||||
coreVersion := rangetable.Assigned(unicode.Version)
|
||||
for r := rune(0); r <= lastRuneForTesting; r++ {
|
||||
if !unicode.In(r, assigned) || !unicode.In(r, coreVersion) {
|
||||
continue
|
||||
}
|
||||
c := contextFromRune(r)
|
||||
if got, want := c.info.isCaseIgnorable(), propIgnore(r); got != want {
|
||||
t.Errorf("caseIgnorable(%U): got %v; want %v (%x)", r, got, want, c.info)
|
||||
}
|
||||
// New letters may change case types, but existing case pairings should
|
||||
// not change. See Case Pair Stability in
|
||||
// http://unicode.org/policies/stability_policy.html.
|
||||
if rf := unicode.SimpleFold(r); rf != r && unicode.In(rf, assigned) {
|
||||
if got, want := c.info.isCased(), propCased(r); got != want {
|
||||
t.Errorf("cased(%U): got %v; want %v (%x)", r, got, want, c.info)
|
||||
}
|
||||
if got, want := c.caseType() == cUpper, propUpper(r); got != want {
|
||||
t.Errorf("upper(%U): got %v; want %v (%x)", r, got, want, c.info)
|
||||
}
|
||||
if got, want := c.caseType() == cLower, propLower(r); got != want {
|
||||
t.Errorf("lower(%U): got %v; want %v (%x)", r, got, want, c.info)
|
||||
}
|
||||
}
|
||||
if got, want := c.info.isBreak(), hasBreakProp(r); got != want {
|
||||
t.Errorf("isBreak(%U): got %v; want %v (%x)", r, got, want, c.info)
|
||||
}
|
||||
}
|
||||
// TODO: get title case from unicode file.
|
||||
}
|
||||
|
||||
func TestMapping(t *testing.T) {
|
||||
assigned := rangetable.Assigned(UnicodeVersion)
|
||||
coreVersion := rangetable.Assigned(unicode.Version)
|
||||
if coreVersion == nil {
|
||||
coreVersion = assigned
|
||||
}
|
||||
apply := func(r rune, f func(c *context) bool) string {
|
||||
c := contextFromRune(r)
|
||||
f(c)
|
||||
return string(c.dst[:c.pDst])
|
||||
}
|
||||
|
||||
for r, tt := range special {
|
||||
if got, want := apply(r, lower), tt.toLower; got != want {
|
||||
t.Errorf("lowerSpecial:(%U): got %+q; want %+q", r, got, want)
|
||||
}
|
||||
if got, want := apply(r, title), tt.toTitle; got != want {
|
||||
t.Errorf("titleSpecial:(%U): got %+q; want %+q", r, got, want)
|
||||
}
|
||||
if got, want := apply(r, upper), tt.toUpper; got != want {
|
||||
t.Errorf("upperSpecial:(%U): got %+q; want %+q", r, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
for r := rune(0); r <= lastRuneForTesting; r++ {
|
||||
if !unicode.In(r, assigned) || !unicode.In(r, coreVersion) {
|
||||
continue
|
||||
}
|
||||
if rf := unicode.SimpleFold(r); rf == r || !unicode.In(rf, assigned) {
|
||||
continue
|
||||
}
|
||||
if _, ok := special[r]; ok {
|
||||
continue
|
||||
}
|
||||
want := string(unicode.ToLower(r))
|
||||
if got := apply(r, lower); got != want {
|
||||
t.Errorf("lower:%q (%U): got %q %U; want %q %U", r, r, got, []rune(got), want, []rune(want))
|
||||
}
|
||||
|
||||
want = string(unicode.ToUpper(r))
|
||||
if got := apply(r, upper); got != want {
|
||||
t.Errorf("upper:%q (%U): got %q %U; want %q %U", r, r, got, []rune(got), want, []rune(want))
|
||||
}
|
||||
|
||||
want = string(unicode.ToTitle(r))
|
||||
if got := apply(r, title); got != want {
|
||||
t.Errorf("title:%q (%U): got %q %U; want %q %U", r, r, got, []rune(got), want, []rune(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runeFoldData(r rune) (x struct{ simple, full, special string }) {
|
||||
x = foldMap[r]
|
||||
if x.simple == "" {
|
||||
x.simple = string(unicode.ToLower(r))
|
||||
}
|
||||
if x.full == "" {
|
||||
x.full = string(unicode.ToLower(r))
|
||||
}
|
||||
if x.special == "" {
|
||||
x.special = x.full
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestFoldData(t *testing.T) {
|
||||
assigned := rangetable.Assigned(UnicodeVersion)
|
||||
coreVersion := rangetable.Assigned(unicode.Version)
|
||||
if coreVersion == nil {
|
||||
coreVersion = assigned
|
||||
}
|
||||
apply := func(r rune, f func(c *context) bool) (string, info) {
|
||||
c := contextFromRune(r)
|
||||
f(c)
|
||||
return string(c.dst[:c.pDst]), c.info.cccType()
|
||||
}
|
||||
for r := rune(0); r <= lastRuneForTesting; r++ {
|
||||
if !unicode.In(r, assigned) || !unicode.In(r, coreVersion) {
|
||||
continue
|
||||
}
|
||||
x := runeFoldData(r)
|
||||
if got, info := apply(r, foldFull); got != x.full {
|
||||
t.Errorf("full:%q (%U): got %q %U; want %q %U (ccc=%x)", r, r, got, []rune(got), x.full, []rune(x.full), info)
|
||||
}
|
||||
// TODO: special and simple.
|
||||
}
|
||||
}
|
||||
|
||||
func TestCCC(t *testing.T) {
|
||||
assigned := rangetable.Assigned(UnicodeVersion)
|
||||
normVersion := rangetable.Assigned(norm.Version)
|
||||
for r := rune(0); r <= lastRuneForTesting; r++ {
|
||||
if !unicode.In(r, assigned) || !unicode.In(r, normVersion) {
|
||||
continue
|
||||
}
|
||||
c := contextFromRune(r)
|
||||
|
||||
p := norm.NFC.PropertiesString(string(r))
|
||||
want := cccOther
|
||||
switch p.CCC() {
|
||||
case 0:
|
||||
want = cccZero
|
||||
case above:
|
||||
want = cccAbove
|
||||
}
|
||||
if got := c.info.cccType(); got != want {
|
||||
t.Errorf("%U: got %x; want %x", r, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWordBreaks(t *testing.T) {
|
||||
for i, tt := range breakTest {
|
||||
parts := strings.Split(tt, "|")
|
||||
want := ""
|
||||
for _, s := range parts {
|
||||
want += Title(language.Und).String(s)
|
||||
}
|
||||
src := strings.Join(parts, "")
|
||||
got := Title(language.Und).String(src)
|
||||
if got != want {
|
||||
t.Errorf("%d: title(%q) = %q; want %q", i, src, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
dstSize int
|
||||
atEOF bool
|
||||
src string
|
||||
out string
|
||||
nSrc int
|
||||
err error
|
||||
ops string
|
||||
prefixArg string
|
||||
prefixWant bool
|
||||
}{{
|
||||
desc: "next: past end, atEOF, no checkpoint",
|
||||
dstSize: 10,
|
||||
atEOF: true,
|
||||
src: "12",
|
||||
out: "",
|
||||
nSrc: 2,
|
||||
ops: "next;next;next",
|
||||
// Test that calling prefix with a non-empty argument when the buffer
|
||||
// is depleted returns false.
|
||||
prefixArg: "x",
|
||||
prefixWant: false,
|
||||
}, {
|
||||
desc: "next: not at end, atEOF, no checkpoint",
|
||||
dstSize: 10,
|
||||
atEOF: false,
|
||||
src: "12",
|
||||
out: "",
|
||||
nSrc: 0,
|
||||
err: transform.ErrShortSrc,
|
||||
ops: "next;next",
|
||||
prefixArg: "",
|
||||
prefixWant: true,
|
||||
}, {
|
||||
desc: "next: past end, !atEOF, no checkpoint",
|
||||
dstSize: 10,
|
||||
atEOF: false,
|
||||
src: "12",
|
||||
out: "",
|
||||
nSrc: 0,
|
||||
err: transform.ErrShortSrc,
|
||||
ops: "next;next;next",
|
||||
prefixArg: "",
|
||||
prefixWant: true,
|
||||
}, {
|
||||
desc: "next: past end, !atEOF, checkpoint",
|
||||
dstSize: 10,
|
||||
atEOF: false,
|
||||
src: "12",
|
||||
out: "",
|
||||
nSrc: 2,
|
||||
ops: "next;next;checkpoint;next",
|
||||
prefixArg: "",
|
||||
prefixWant: true,
|
||||
}, {
|
||||
desc: "copy: exact count, atEOF, no checkpoint",
|
||||
dstSize: 2,
|
||||
atEOF: true,
|
||||
src: "12",
|
||||
out: "12",
|
||||
nSrc: 2,
|
||||
ops: "next;copy;next;copy;next",
|
||||
prefixArg: "",
|
||||
prefixWant: true,
|
||||
}, {
|
||||
desc: "copy: past end, !atEOF, no checkpoint",
|
||||
dstSize: 2,
|
||||
atEOF: false,
|
||||
src: "12",
|
||||
out: "",
|
||||
nSrc: 0,
|
||||
err: transform.ErrShortSrc,
|
||||
ops: "next;copy;next;copy;next",
|
||||
prefixArg: "",
|
||||
prefixWant: true,
|
||||
}, {
|
||||
desc: "copy: past end, !atEOF, checkpoint",
|
||||
dstSize: 2,
|
||||
atEOF: false,
|
||||
src: "12",
|
||||
out: "12",
|
||||
nSrc: 2,
|
||||
ops: "next;copy;next;copy;checkpoint;next",
|
||||
prefixArg: "",
|
||||
prefixWant: true,
|
||||
}, {
|
||||
desc: "copy: short dst",
|
||||
dstSize: 1,
|
||||
atEOF: false,
|
||||
src: "12",
|
||||
out: "",
|
||||
nSrc: 0,
|
||||
err: transform.ErrShortDst,
|
||||
ops: "next;copy;next;copy;checkpoint;next",
|
||||
prefixArg: "12",
|
||||
prefixWant: false,
|
||||
}, {
|
||||
desc: "copy: short dst, checkpointed",
|
||||
dstSize: 1,
|
||||
atEOF: false,
|
||||
src: "12",
|
||||
out: "1",
|
||||
nSrc: 1,
|
||||
err: transform.ErrShortDst,
|
||||
ops: "next;copy;checkpoint;next;copy;next",
|
||||
prefixArg: "",
|
||||
prefixWant: true,
|
||||
}, {
|
||||
desc: "writeString: simple",
|
||||
dstSize: 3,
|
||||
atEOF: true,
|
||||
src: "1",
|
||||
out: "1ab",
|
||||
nSrc: 1,
|
||||
ops: "next;copy;writeab;next",
|
||||
prefixArg: "",
|
||||
prefixWant: true,
|
||||
}, {
|
||||
desc: "writeString: short dst",
|
||||
dstSize: 2,
|
||||
atEOF: true,
|
||||
src: "12",
|
||||
out: "",
|
||||
nSrc: 0,
|
||||
err: transform.ErrShortDst,
|
||||
ops: "next;copy;writeab;next",
|
||||
prefixArg: "2",
|
||||
prefixWant: true,
|
||||
}, {
|
||||
desc: "writeString: simple",
|
||||
dstSize: 3,
|
||||
atEOF: true,
|
||||
src: "12",
|
||||
out: "1ab",
|
||||
nSrc: 2,
|
||||
ops: "next;copy;next;writeab;next",
|
||||
prefixArg: "",
|
||||
prefixWant: true,
|
||||
}, {
|
||||
desc: "writeString: short dst",
|
||||
dstSize: 2,
|
||||
atEOF: true,
|
||||
src: "12",
|
||||
out: "",
|
||||
nSrc: 0,
|
||||
err: transform.ErrShortDst,
|
||||
ops: "next;copy;next;writeab;next",
|
||||
prefixArg: "1",
|
||||
prefixWant: false,
|
||||
}, {
|
||||
desc: "prefix",
|
||||
dstSize: 2,
|
||||
atEOF: true,
|
||||
src: "12",
|
||||
out: "",
|
||||
nSrc: 0,
|
||||
// Context will assign an ErrShortSrc if the input wasn't exhausted.
|
||||
err: transform.ErrShortSrc,
|
||||
prefixArg: "12",
|
||||
prefixWant: true,
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
c := context{dst: make([]byte, tt.dstSize), src: []byte(tt.src), atEOF: tt.atEOF}
|
||||
|
||||
for _, op := range strings.Split(tt.ops, ";") {
|
||||
switch op {
|
||||
case "next":
|
||||
c.next()
|
||||
case "checkpoint":
|
||||
c.checkpoint()
|
||||
case "writeab":
|
||||
c.writeString("ab")
|
||||
case "copy":
|
||||
c.copy()
|
||||
case "":
|
||||
default:
|
||||
t.Fatalf("unknown op %q", op)
|
||||
}
|
||||
}
|
||||
if got := c.hasPrefix(tt.prefixArg); got != tt.prefixWant {
|
||||
t.Errorf("%s:\nprefix was %v; want %v", tt.desc, got, tt.prefixWant)
|
||||
}
|
||||
nDst, nSrc, err := c.ret()
|
||||
if err != tt.err {
|
||||
t.Errorf("%s:\nerror was %v; want %v", tt.desc, err, tt.err)
|
||||
}
|
||||
if out := string(c.dst[:nDst]); out != tt.out {
|
||||
t.Errorf("%s:\nout was %q; want %q", tt.desc, out, tt.out)
|
||||
}
|
||||
if nSrc != tt.nSrc {
|
||||
t.Errorf("%s:\nnSrc was %d; want %d", tt.desc, nSrc, tt.nSrc)
|
||||
}
|
||||
}
|
||||
}
|
||||
53
vendor/golang.org/x/text/cases/example_test.go
generated
vendored
Normal file
53
vendor/golang.org/x/text/cases/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2014 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 cases_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
src := []string{
|
||||
"hello world!",
|
||||
"i with dot",
|
||||
"'n ijsberg",
|
||||
"here comes O'Brian",
|
||||
}
|
||||
for _, c := range []cases.Caser{
|
||||
cases.Lower(language.Und),
|
||||
cases.Upper(language.Turkish),
|
||||
cases.Title(language.Dutch),
|
||||
cases.Title(language.Und, cases.NoLower),
|
||||
} {
|
||||
fmt.Println()
|
||||
for _, s := range src {
|
||||
fmt.Println(c.String(s))
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world!
|
||||
// i with dot
|
||||
// 'n ijsberg
|
||||
// here comes o'brian
|
||||
//
|
||||
// HELLO WORLD!
|
||||
// İ WİTH DOT
|
||||
// 'N İJSBERG
|
||||
// HERE COMES O'BRİAN
|
||||
//
|
||||
// Hello World!
|
||||
// I With Dot
|
||||
// 'n IJsberg
|
||||
// Here Comes O'brian
|
||||
//
|
||||
// Hello World!
|
||||
// I With Dot
|
||||
// 'N Ijsberg
|
||||
// Here Comes O'Brian
|
||||
}
|
||||
26
vendor/golang.org/x/text/cases/fold.go
generated
vendored
Normal file
26
vendor/golang.org/x/text/cases/fold.go
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2016 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 cases
|
||||
|
||||
import "golang.org/x/text/transform"
|
||||
|
||||
type caseFolder struct{ transform.NopResetter }
|
||||
|
||||
// caseFolder implements the Transformer interface for doing case folding.
|
||||
func (t *caseFolder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
c := context{dst: dst, src: src, atEOF: atEOF}
|
||||
for c.next() {
|
||||
foldFull(&c)
|
||||
c.checkpoint()
|
||||
}
|
||||
return c.ret()
|
||||
}
|
||||
|
||||
func makeFold(o options) transform.Transformer {
|
||||
// TODO: Special case folding, through option Language, Special/Turkic, or
|
||||
// both.
|
||||
// TODO: Implement Compact options.
|
||||
return &caseFolder{}
|
||||
}
|
||||
53
vendor/golang.org/x/text/cases/fold_test.go
generated
vendored
Normal file
53
vendor/golang.org/x/text/cases/fold_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2016 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 cases
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/internal/testtext"
|
||||
)
|
||||
|
||||
func TestFold(t *testing.T) {
|
||||
testCases := []string{
|
||||
"βß\u13f8", // "βssᏰ"
|
||||
"ab\u13fc\uab7aꭰ", // abᏴᎪᎠ
|
||||
"affifflast", // affifflast
|
||||
"Iİiı\u0345", // ii̇iıι
|
||||
"µµΜΜςσΣΣ", // μμμμσσσσ
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
testEntry := func(name string, c Caser, m func(r rune) string) {
|
||||
want := ""
|
||||
for _, r := range tc {
|
||||
want += m(r)
|
||||
}
|
||||
if got := c.String(tc); got != want {
|
||||
t.Errorf("%s(%s) = %+q; want %+q", name, tc, got, want)
|
||||
}
|
||||
dst := make([]byte, 256) // big enough to hold any result
|
||||
src := []byte(tc)
|
||||
v := testtext.AllocsPerRun(20, func() {
|
||||
c.Transform(dst, src, true)
|
||||
})
|
||||
if v > 0 {
|
||||
t.Errorf("%s(%s): number of allocs was %f; want 0", name, tc, v)
|
||||
}
|
||||
}
|
||||
testEntry("FullFold", Fold(), func(r rune) string {
|
||||
return runeFoldData(r).full
|
||||
})
|
||||
// TODO:
|
||||
// testEntry("SimpleFold", Fold(Compact), func(r rune) string {
|
||||
// return runeFoldData(r).simple
|
||||
// })
|
||||
// testEntry("SpecialFold", Fold(Turkic), func(r rune) string {
|
||||
// return runeFoldData(r).special
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFullFold(b *testing.B) { benchTransformer(b, Fold(), txtNonASCII) }
|
||||
func BenchmarkFullFoldASCII(b *testing.B) { benchTransformer(b, Fold(), txtASCII) }
|
||||
831
vendor/golang.org/x/text/cases/gen.go
generated
vendored
Normal file
831
vendor/golang.org/x/text/cases/gen.go
generated
vendored
Normal file
|
|
@ -0,0 +1,831 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// This program generates the trie for casing operations. The Unicode casing
|
||||
// algorithm requires the lookup of various properties and mappings for each
|
||||
// rune. The table generated by this generator combines several of the most
|
||||
// frequently used of these into a single trie so that they can be accessed
|
||||
// with a single lookup.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/internal/gen"
|
||||
"golang.org/x/text/internal/triegen"
|
||||
"golang.org/x/text/internal/ucd"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gen.Init()
|
||||
genTables()
|
||||
genTablesTest()
|
||||
gen.Repackage("gen_trieval.go", "trieval.go", "cases")
|
||||
}
|
||||
|
||||
// runeInfo contains all information for a rune that we care about for casing
|
||||
// operations.
|
||||
type runeInfo struct {
|
||||
Rune rune
|
||||
|
||||
entry info // trie value for this rune.
|
||||
|
||||
CaseMode info
|
||||
|
||||
// Simple case mappings.
|
||||
Simple [1 + maxCaseMode][]rune
|
||||
|
||||
// Special casing
|
||||
HasSpecial bool
|
||||
Conditional bool
|
||||
Special [1 + maxCaseMode][]rune
|
||||
|
||||
// Folding
|
||||
FoldSimple rune
|
||||
FoldSpecial rune
|
||||
FoldFull []rune
|
||||
|
||||
// TODO: FC_NFKC, or equivalent data.
|
||||
|
||||
// Properties
|
||||
SoftDotted bool
|
||||
CaseIgnorable bool
|
||||
Cased bool
|
||||
DecomposeGreek bool
|
||||
BreakType string
|
||||
BreakCat breakCategory
|
||||
|
||||
// We care mostly about 0, Above, and IotaSubscript.
|
||||
CCC byte
|
||||
}
|
||||
|
||||
type breakCategory int
|
||||
|
||||
const (
|
||||
breakBreak breakCategory = iota
|
||||
breakLetter
|
||||
breakIgnored
|
||||
)
|
||||
|
||||
// mapping returns the case mapping for the given case type.
|
||||
func (r *runeInfo) mapping(c info) string {
|
||||
if r.HasSpecial {
|
||||
return string(r.Special[c])
|
||||
}
|
||||
if len(r.Simple[c]) != 0 {
|
||||
return string(r.Simple[c])
|
||||
}
|
||||
return string(r.Rune)
|
||||
}
|
||||
|
||||
func parse(file string, f func(p *ucd.Parser)) {
|
||||
ucd.Parse(gen.OpenUCDFile(file), f)
|
||||
}
|
||||
|
||||
func parseUCD() []runeInfo {
|
||||
chars := make([]runeInfo, unicode.MaxRune)
|
||||
|
||||
get := func(r rune) *runeInfo {
|
||||
c := &chars[r]
|
||||
c.Rune = r
|
||||
return c
|
||||
}
|
||||
|
||||
parse("UnicodeData.txt", func(p *ucd.Parser) {
|
||||
ri := get(p.Rune(0))
|
||||
ri.CCC = byte(p.Int(ucd.CanonicalCombiningClass))
|
||||
ri.Simple[cLower] = p.Runes(ucd.SimpleLowercaseMapping)
|
||||
ri.Simple[cUpper] = p.Runes(ucd.SimpleUppercaseMapping)
|
||||
ri.Simple[cTitle] = p.Runes(ucd.SimpleTitlecaseMapping)
|
||||
if p.String(ucd.GeneralCategory) == "Lt" {
|
||||
ri.CaseMode = cTitle
|
||||
}
|
||||
})
|
||||
|
||||
// <code>; <property>
|
||||
parse("PropList.txt", func(p *ucd.Parser) {
|
||||
if p.String(1) == "Soft_Dotted" {
|
||||
chars[p.Rune(0)].SoftDotted = true
|
||||
}
|
||||
})
|
||||
|
||||
// <code>; <word break type>
|
||||
parse("DerivedCoreProperties.txt", func(p *ucd.Parser) {
|
||||
ri := get(p.Rune(0))
|
||||
switch p.String(1) {
|
||||
case "Case_Ignorable":
|
||||
ri.CaseIgnorable = true
|
||||
case "Cased":
|
||||
ri.Cased = true
|
||||
case "Lowercase":
|
||||
ri.CaseMode = cLower
|
||||
case "Uppercase":
|
||||
ri.CaseMode = cUpper
|
||||
}
|
||||
})
|
||||
|
||||
// <code>; <lower> ; <title> ; <upper> ; (<condition_list> ;)?
|
||||
parse("SpecialCasing.txt", func(p *ucd.Parser) {
|
||||
// We drop all conditional special casing and deal with them manually in
|
||||
// the language-specific case mappers. Rune 0x03A3 is the only one with
|
||||
// a conditional formatting that is not language-specific. However,
|
||||
// dealing with this letter is tricky, especially in a streaming
|
||||
// context, so we deal with it in the Caser for Greek specifically.
|
||||
ri := get(p.Rune(0))
|
||||
if p.String(4) == "" {
|
||||
ri.HasSpecial = true
|
||||
ri.Special[cLower] = p.Runes(1)
|
||||
ri.Special[cTitle] = p.Runes(2)
|
||||
ri.Special[cUpper] = p.Runes(3)
|
||||
} else {
|
||||
ri.Conditional = true
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: Use text breaking according to UAX #29.
|
||||
// <code>; <word break type>
|
||||
parse("auxiliary/WordBreakProperty.txt", func(p *ucd.Parser) {
|
||||
ri := get(p.Rune(0))
|
||||
ri.BreakType = p.String(1)
|
||||
|
||||
// We collapse the word breaking properties onto the categories we need.
|
||||
switch p.String(1) { // TODO: officially we need to canonicalize.
|
||||
case "Format", "MidLetter", "MidNumLet", "Single_Quote":
|
||||
ri.BreakCat = breakIgnored
|
||||
case "ALetter", "Hebrew_Letter", "Numeric", "Extend", "ExtendNumLet":
|
||||
ri.BreakCat = breakLetter
|
||||
}
|
||||
})
|
||||
|
||||
// <code>; <type>; <mapping>
|
||||
parse("CaseFolding.txt", func(p *ucd.Parser) {
|
||||
ri := get(p.Rune(0))
|
||||
switch p.String(1) {
|
||||
case "C":
|
||||
ri.FoldSimple = p.Rune(2)
|
||||
ri.FoldFull = p.Runes(2)
|
||||
case "S":
|
||||
ri.FoldSimple = p.Rune(2)
|
||||
case "T":
|
||||
ri.FoldSpecial = p.Rune(2)
|
||||
case "F":
|
||||
ri.FoldFull = p.Runes(2)
|
||||
default:
|
||||
log.Fatalf("%U: unknown type: %s", p.Rune(0), p.String(1))
|
||||
}
|
||||
})
|
||||
|
||||
return chars
|
||||
}
|
||||
|
||||
func genTables() {
|
||||
chars := parseUCD()
|
||||
verifyProperties(chars)
|
||||
|
||||
t := triegen.NewTrie("case")
|
||||
for i := range chars {
|
||||
c := &chars[i]
|
||||
makeEntry(c)
|
||||
t.Insert(rune(i), uint64(c.entry))
|
||||
}
|
||||
|
||||
w := gen.NewCodeWriter()
|
||||
defer w.WriteGoFile("tables.go", "cases")
|
||||
|
||||
gen.WriteUnicodeVersion(w)
|
||||
|
||||
// TODO: write CLDR version after adding a mechanism to detect that the
|
||||
// tables on which the manually created locale-sensitive casing code is
|
||||
// based hasn't changed.
|
||||
|
||||
w.WriteVar("xorData", string(xorData))
|
||||
w.WriteVar("exceptions", string(exceptionData))
|
||||
|
||||
sz, err := t.Gen(w, triegen.Compact(&sparseCompacter{}))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
w.Size += sz
|
||||
}
|
||||
|
||||
func makeEntry(ri *runeInfo) {
|
||||
if ri.CaseIgnorable {
|
||||
if ri.Cased {
|
||||
ri.entry = cIgnorableCased
|
||||
} else {
|
||||
ri.entry = cIgnorableUncased
|
||||
}
|
||||
} else {
|
||||
ri.entry = ri.CaseMode
|
||||
}
|
||||
|
||||
// TODO: handle soft-dotted.
|
||||
|
||||
ccc := cccOther
|
||||
switch ri.CCC {
|
||||
case 0: // Not_Reordered
|
||||
ccc = cccZero
|
||||
case above: // Above
|
||||
ccc = cccAbove
|
||||
}
|
||||
if ri.BreakCat == breakBreak {
|
||||
ccc = cccBreak
|
||||
}
|
||||
|
||||
ri.entry |= ccc
|
||||
|
||||
if ri.CaseMode == cUncased {
|
||||
return
|
||||
}
|
||||
|
||||
// Need to do something special.
|
||||
if ri.CaseMode == cTitle || ri.HasSpecial || ri.mapping(cTitle) != ri.mapping(cUpper) {
|
||||
makeException(ri)
|
||||
return
|
||||
}
|
||||
if f := string(ri.FoldFull); len(f) > 0 && f != ri.mapping(cUpper) && f != ri.mapping(cLower) {
|
||||
makeException(ri)
|
||||
return
|
||||
}
|
||||
|
||||
// Rune is either lowercase or uppercase.
|
||||
|
||||
orig := string(ri.Rune)
|
||||
mapped := ""
|
||||
if ri.CaseMode == cUpper {
|
||||
mapped = ri.mapping(cLower)
|
||||
} else {
|
||||
mapped = ri.mapping(cUpper)
|
||||
}
|
||||
|
||||
if len(orig) != len(mapped) {
|
||||
makeException(ri)
|
||||
return
|
||||
}
|
||||
|
||||
if string(ri.FoldFull) == ri.mapping(cUpper) {
|
||||
ri.entry |= inverseFoldBit
|
||||
}
|
||||
|
||||
n := len(orig)
|
||||
|
||||
// Create per-byte XOR mask.
|
||||
var b []byte
|
||||
for i := 0; i < n; i++ {
|
||||
b = append(b, orig[i]^mapped[i])
|
||||
}
|
||||
|
||||
// Remove leading 0 bytes, but keep at least one byte.
|
||||
for ; len(b) > 1 && b[0] == 0; b = b[1:] {
|
||||
}
|
||||
|
||||
if len(b) == 1 && b[0]&0xc0 == 0 {
|
||||
ri.entry |= info(b[0]) << xorShift
|
||||
return
|
||||
}
|
||||
|
||||
key := string(b)
|
||||
x, ok := xorCache[key]
|
||||
if !ok {
|
||||
xorData = append(xorData, 0) // for detecting start of sequence
|
||||
xorData = append(xorData, b...)
|
||||
|
||||
x = len(xorData) - 1
|
||||
xorCache[key] = x
|
||||
}
|
||||
ri.entry |= info(x<<xorShift) | xorIndexBit
|
||||
}
|
||||
|
||||
var xorCache = map[string]int{}
|
||||
|
||||
// xorData contains byte-wise XOR data for the least significant bytes of a
|
||||
// UTF-8 encoded rune. An index points to the last byte. The sequence starts
|
||||
// with a zero terminator.
|
||||
var xorData = []byte{}
|
||||
|
||||
// See the comments in gen_trieval.go re "the exceptions slice".
|
||||
var exceptionData = []byte{0}
|
||||
|
||||
// makeException encodes case mappings that cannot be expressed in a simple
|
||||
// XOR diff.
|
||||
func makeException(ri *runeInfo) {
|
||||
ccc := ri.entry & cccMask
|
||||
// Set exception bit and retain case type.
|
||||
ri.entry &= 0x0007
|
||||
ri.entry |= exceptionBit
|
||||
|
||||
if len(exceptionData) >= 1<<numExceptionBits {
|
||||
log.Fatalf("%U:exceptionData too large %x > %d bits", ri.Rune, len(exceptionData), numExceptionBits)
|
||||
}
|
||||
|
||||
// Set the offset in the exceptionData array.
|
||||
ri.entry |= info(len(exceptionData) << exceptionShift)
|
||||
|
||||
orig := string(ri.Rune)
|
||||
tc := ri.mapping(cTitle)
|
||||
uc := ri.mapping(cUpper)
|
||||
lc := ri.mapping(cLower)
|
||||
ff := string(ri.FoldFull)
|
||||
|
||||
// addString sets the length of a string and adds it to the expansions array.
|
||||
addString := func(s string, b *byte) {
|
||||
if len(s) == 0 {
|
||||
// Zero-length mappings exist, but only for conditional casing,
|
||||
// which we are representing outside of this table.
|
||||
log.Fatalf("%U: has zero-length mapping.", ri.Rune)
|
||||
}
|
||||
*b <<= 3
|
||||
if s != orig {
|
||||
n := len(s)
|
||||
if n > 7 {
|
||||
log.Fatalf("%U: mapping larger than 7 (%d)", ri.Rune, n)
|
||||
}
|
||||
*b |= byte(n)
|
||||
exceptionData = append(exceptionData, s...)
|
||||
}
|
||||
}
|
||||
|
||||
// byte 0:
|
||||
exceptionData = append(exceptionData, byte(ccc)|byte(len(ff)))
|
||||
|
||||
// byte 1:
|
||||
p := len(exceptionData)
|
||||
exceptionData = append(exceptionData, 0)
|
||||
|
||||
if len(ff) > 7 { // May be zero-length.
|
||||
log.Fatalf("%U: fold string larger than 7 (%d)", ri.Rune, len(ff))
|
||||
}
|
||||
exceptionData = append(exceptionData, ff...)
|
||||
ct := ri.CaseMode
|
||||
if ct != cLower {
|
||||
addString(lc, &exceptionData[p])
|
||||
}
|
||||
if ct != cUpper {
|
||||
addString(uc, &exceptionData[p])
|
||||
}
|
||||
if ct != cTitle {
|
||||
// If title is the same as upper, we set it to the original string so
|
||||
// that it will be marked as not present. This implies title case is
|
||||
// the same as upper case.
|
||||
if tc == uc {
|
||||
tc = orig
|
||||
}
|
||||
addString(tc, &exceptionData[p])
|
||||
}
|
||||
}
|
||||
|
||||
// sparseCompacter is a trie value block Compacter. There are many cases where
|
||||
// successive runes alternate between lower- and upper-case. This Compacter
|
||||
// exploits this by adding a special case type where the case value is obtained
|
||||
// from or-ing it with the least-significant bit of the rune, creating large
|
||||
// ranges of equal case values that compress well.
|
||||
type sparseCompacter struct {
|
||||
sparseBlocks [][]uint16
|
||||
sparseOffsets []uint16
|
||||
sparseCount int
|
||||
}
|
||||
|
||||
// makeSparse returns the number of elements that compact block would contain
|
||||
// as well as the modified values.
|
||||
func makeSparse(vals []uint64) ([]uint16, int) {
|
||||
// Copy the values.
|
||||
values := make([]uint16, len(vals))
|
||||
for i, v := range vals {
|
||||
values[i] = uint16(v)
|
||||
}
|
||||
|
||||
alt := func(i int, v uint16) uint16 {
|
||||
if cm := info(v & fullCasedMask); cm == cUpper || cm == cLower {
|
||||
// Convert cLower or cUpper to cXORCase value, which has the form 11x.
|
||||
xor := v
|
||||
xor &^= 1
|
||||
xor |= uint16(i&1) ^ (v & 1)
|
||||
xor |= 0x4
|
||||
return xor
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
var count int
|
||||
var previous uint16
|
||||
for i, v := range values {
|
||||
if v != 0 {
|
||||
// Try if the unmodified value is equal to the previous.
|
||||
if v == previous {
|
||||
continue
|
||||
}
|
||||
|
||||
// Try if the xor-ed value is equal to the previous value.
|
||||
a := alt(i, v)
|
||||
if a == previous {
|
||||
values[i] = a
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a new value.
|
||||
count++
|
||||
|
||||
// Use the xor-ed value if it will be identical to the next value.
|
||||
if p := i + 1; p < len(values) && alt(p, values[p]) == a {
|
||||
values[i] = a
|
||||
v = a
|
||||
}
|
||||
}
|
||||
previous = v
|
||||
}
|
||||
return values, count
|
||||
}
|
||||
|
||||
func (s *sparseCompacter) Size(v []uint64) (int, bool) {
|
||||
_, n := makeSparse(v)
|
||||
|
||||
// We limit using this method to having 16 entries.
|
||||
if n > 16 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return 2 + int(reflect.TypeOf(valueRange{}).Size())*n, true
|
||||
}
|
||||
|
||||
func (s *sparseCompacter) Store(v []uint64) uint32 {
|
||||
h := uint32(len(s.sparseOffsets))
|
||||
values, sz := makeSparse(v)
|
||||
s.sparseBlocks = append(s.sparseBlocks, values)
|
||||
s.sparseOffsets = append(s.sparseOffsets, uint16(s.sparseCount))
|
||||
s.sparseCount += sz
|
||||
return h
|
||||
}
|
||||
|
||||
func (s *sparseCompacter) Handler() string {
|
||||
// The sparse global variable and its lookup method is defined in gen_trieval.go.
|
||||
return "sparse.lookup"
|
||||
}
|
||||
|
||||
func (s *sparseCompacter) Print(w io.Writer) (retErr error) {
|
||||
p := func(format string, args ...interface{}) {
|
||||
_, err := fmt.Fprintf(w, format, args...)
|
||||
if retErr == nil && err != nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
|
||||
ls := len(s.sparseBlocks)
|
||||
if ls == len(s.sparseOffsets) {
|
||||
s.sparseOffsets = append(s.sparseOffsets, uint16(s.sparseCount))
|
||||
}
|
||||
p("// sparseOffsets: %d entries, %d bytes\n", ls+1, (ls+1)*2)
|
||||
p("var sparseOffsets = %#v\n\n", s.sparseOffsets)
|
||||
|
||||
ns := s.sparseCount
|
||||
p("// sparseValues: %d entries, %d bytes\n", ns, ns*4)
|
||||
p("var sparseValues = [%d]valueRange {", ns)
|
||||
for i, values := range s.sparseBlocks {
|
||||
p("\n// Block %#x, offset %#x", i, s.sparseOffsets[i])
|
||||
var v uint16
|
||||
for i, nv := range values {
|
||||
if nv != v {
|
||||
if v != 0 {
|
||||
p(",hi:%#02x},", 0x80+i-1)
|
||||
}
|
||||
if nv != 0 {
|
||||
p("\n{value:%#04x,lo:%#02x", nv, 0x80+i)
|
||||
}
|
||||
}
|
||||
v = nv
|
||||
}
|
||||
if v != 0 {
|
||||
p(",hi:%#02x},", 0x80+len(values)-1)
|
||||
}
|
||||
}
|
||||
p("\n}\n\n")
|
||||
return
|
||||
}
|
||||
|
||||
// verifyProperties that properties of the runes that are relied upon in the
|
||||
// implementation. Each property is marked with an identifier that is referred
|
||||
// to in the places where it is used.
|
||||
func verifyProperties(chars []runeInfo) {
|
||||
for i, c := range chars {
|
||||
r := rune(i)
|
||||
|
||||
// Rune properties.
|
||||
|
||||
// A.1: modifier never changes on lowercase. [ltLower]
|
||||
if c.CCC > 0 && unicode.ToLower(r) != r {
|
||||
log.Fatalf("%U: non-starter changes when lowercased", r)
|
||||
}
|
||||
|
||||
// A.2: properties of decompositions starting with I or J. [ltLower]
|
||||
d := norm.NFD.PropertiesString(string(r)).Decomposition()
|
||||
if len(d) > 0 {
|
||||
if d[0] == 'I' || d[0] == 'J' {
|
||||
// A.2.1: we expect at least an ASCII character and a modifier.
|
||||
if len(d) < 3 {
|
||||
log.Fatalf("%U: length of decomposition was %d; want >= 3", r, len(d))
|
||||
}
|
||||
|
||||
// All subsequent runes are modifiers and all have the same CCC.
|
||||
runes := []rune(string(d[1:]))
|
||||
ccc := chars[runes[0]].CCC
|
||||
|
||||
for _, mr := range runes[1:] {
|
||||
mc := chars[mr]
|
||||
|
||||
// A.2.2: all modifiers have a CCC of Above or less.
|
||||
if ccc == 0 || ccc > above {
|
||||
log.Fatalf("%U: CCC of successive rune (%U) was %d; want (0,230]", r, mr, ccc)
|
||||
}
|
||||
|
||||
// A.2.3: a sequence of modifiers all have the same CCC.
|
||||
if mc.CCC != ccc {
|
||||
log.Fatalf("%U: CCC of follow-up modifier (%U) was %d; want %d", r, mr, mc.CCC, ccc)
|
||||
}
|
||||
|
||||
// A.2.4: for each trailing r, r in [0x300, 0x311] <=> CCC == Above.
|
||||
if (ccc == above) != (0x300 <= mr && mr <= 0x311) {
|
||||
log.Fatalf("%U: modifier %U in [U+0300, U+0311] != ccc(%U) == 230", r, mr, mr)
|
||||
}
|
||||
|
||||
if i += len(string(mr)); i >= len(d) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A.3: no U+0307 in decomposition of Soft-Dotted rune. [ltUpper]
|
||||
if unicode.Is(unicode.Soft_Dotted, r) && strings.Contains(string(d), "\u0307") {
|
||||
log.Fatalf("%U: decomposition of soft-dotted rune may not contain U+0307", r)
|
||||
}
|
||||
|
||||
// A.4: only rune U+0345 may be of CCC Iota_Subscript. [elUpper]
|
||||
if c.CCC == iotaSubscript && r != 0x0345 {
|
||||
log.Fatalf("%U: only rune U+0345 may have CCC Iota_Subscript", r)
|
||||
}
|
||||
|
||||
// A.5: soft-dotted runes do not have exceptions.
|
||||
if c.SoftDotted && c.entry&exceptionBit != 0 {
|
||||
log.Fatalf("%U: soft-dotted has exception", r)
|
||||
}
|
||||
|
||||
// A.6: Greek decomposition. [elUpper]
|
||||
if unicode.Is(unicode.Greek, r) {
|
||||
if b := norm.NFD.PropertiesString(string(r)).Decomposition(); b != nil {
|
||||
runes := []rune(string(b))
|
||||
// A.6.1: If a Greek rune decomposes and the first rune of the
|
||||
// decomposition is greater than U+00FF, the rune is always
|
||||
// great and not a modifier.
|
||||
if f := runes[0]; unicode.IsMark(f) || f > 0xFF && !unicode.Is(unicode.Greek, f) {
|
||||
log.Fatalf("%U: expeced first rune of Greek decomposition to be letter, found %U", r, f)
|
||||
}
|
||||
// A.6.2: Any follow-up rune in a Greek decomposition is a
|
||||
// modifier of which the first should be gobbled in
|
||||
// decomposition.
|
||||
for _, m := range runes[1:] {
|
||||
switch m {
|
||||
case 0x0313, 0x0314, 0x0301, 0x0300, 0x0306, 0x0342, 0x0308, 0x0304, 0x345:
|
||||
default:
|
||||
log.Fatalf("%U: modifier %U is outside of expeced Greek modifier set", r, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Breaking properties.
|
||||
|
||||
// B.1: all runes with CCC > 0 are of break type Extend.
|
||||
if c.CCC > 0 && c.BreakType != "Extend" {
|
||||
log.Fatalf("%U: CCC == %d, but got break type %s; want Extend", r, c.CCC, c.BreakType)
|
||||
}
|
||||
|
||||
// B.2: all cased runes with c.CCC == 0 are of break type ALetter.
|
||||
if c.CCC == 0 && c.Cased && c.BreakType != "ALetter" {
|
||||
log.Fatalf("%U: cased, but got break type %s; want ALetter", r, c.BreakType)
|
||||
}
|
||||
|
||||
// B.3: letter category.
|
||||
if c.CCC == 0 && c.BreakCat != breakBreak && !c.CaseIgnorable {
|
||||
if c.BreakCat != breakLetter {
|
||||
log.Fatalf("%U: check for letter break type gave %d; want %d", r, c.BreakCat, breakLetter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func genTablesTest() {
|
||||
w := &bytes.Buffer{}
|
||||
|
||||
fmt.Fprintln(w, "var (")
|
||||
printProperties(w, "DerivedCoreProperties.txt", "Case_Ignorable", verifyIgnore)
|
||||
|
||||
// We discard the output as we know we have perfect functions. We run them
|
||||
// just to verify the properties are correct.
|
||||
n := printProperties(ioutil.Discard, "DerivedCoreProperties.txt", "Cased", verifyCased)
|
||||
n += printProperties(ioutil.Discard, "DerivedCoreProperties.txt", "Lowercase", verifyLower)
|
||||
n += printProperties(ioutil.Discard, "DerivedCoreProperties.txt", "Uppercase", verifyUpper)
|
||||
if n > 0 {
|
||||
log.Fatalf("One of the discarded properties does not have a perfect filter.")
|
||||
}
|
||||
|
||||
// <code>; <lower> ; <title> ; <upper> ; (<condition_list> ;)?
|
||||
fmt.Fprintln(w, "\tspecial = map[rune]struct{ toLower, toTitle, toUpper string }{")
|
||||
parse("SpecialCasing.txt", func(p *ucd.Parser) {
|
||||
// Skip conditional entries.
|
||||
if p.String(4) != "" {
|
||||
return
|
||||
}
|
||||
r := p.Rune(0)
|
||||
fmt.Fprintf(w, "\t\t0x%04x: {%q, %q, %q},\n",
|
||||
r, string(p.Runes(1)), string(p.Runes(2)), string(p.Runes(3)))
|
||||
})
|
||||
fmt.Fprint(w, "\t}\n\n")
|
||||
|
||||
// <code>; <type>; <runes>
|
||||
table := map[rune]struct{ simple, full, special string }{}
|
||||
parse("CaseFolding.txt", func(p *ucd.Parser) {
|
||||
r := p.Rune(0)
|
||||
t := p.String(1)
|
||||
v := string(p.Runes(2))
|
||||
if t != "T" && v == string(unicode.ToLower(r)) {
|
||||
return
|
||||
}
|
||||
x := table[r]
|
||||
switch t {
|
||||
case "C":
|
||||
x.full = v
|
||||
x.simple = v
|
||||
case "S":
|
||||
x.simple = v
|
||||
case "F":
|
||||
x.full = v
|
||||
case "T":
|
||||
x.special = v
|
||||
}
|
||||
table[r] = x
|
||||
})
|
||||
fmt.Fprintln(w, "\tfoldMap = map[rune]struct{ simple, full, special string }{")
|
||||
for r := rune(0); r < 0x10FFFF; r++ {
|
||||
x, ok := table[r]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "\t\t0x%04x: {%q, %q, %q},\n", r, x.simple, x.full, x.special)
|
||||
}
|
||||
fmt.Fprint(w, "\t}\n\n")
|
||||
|
||||
// Break property
|
||||
notBreak := map[rune]bool{}
|
||||
parse("auxiliary/WordBreakProperty.txt", func(p *ucd.Parser) {
|
||||
switch p.String(1) {
|
||||
case "Extend", "Format", "MidLetter", "MidNumLet", "Single_Quote",
|
||||
"ALetter", "Hebrew_Letter", "Numeric", "ExtendNumLet":
|
||||
notBreak[p.Rune(0)] = true
|
||||
}
|
||||
})
|
||||
|
||||
fmt.Fprintln(w, "\tbreakProp = []struct{ lo, hi rune }{")
|
||||
inBreak := false
|
||||
for r := rune(0); r <= lastRuneForTesting; r++ {
|
||||
if isBreak := !notBreak[r]; isBreak != inBreak {
|
||||
if isBreak {
|
||||
fmt.Fprintf(w, "\t\t{0x%x, ", r)
|
||||
} else {
|
||||
fmt.Fprintf(w, "0x%x},\n", r-1)
|
||||
}
|
||||
inBreak = isBreak
|
||||
}
|
||||
}
|
||||
if inBreak {
|
||||
fmt.Fprintf(w, "0x%x},\n", lastRuneForTesting)
|
||||
}
|
||||
fmt.Fprint(w, "\t}\n\n")
|
||||
|
||||
// Word break test
|
||||
// Filter out all samples that do not contain cased characters.
|
||||
cased := map[rune]bool{}
|
||||
parse("DerivedCoreProperties.txt", func(p *ucd.Parser) {
|
||||
if p.String(1) == "Cased" {
|
||||
cased[p.Rune(0)] = true
|
||||
}
|
||||
})
|
||||
|
||||
fmt.Fprintln(w, "\tbreakTest = []string{")
|
||||
parse("auxiliary/WordBreakTest.txt", func(p *ucd.Parser) {
|
||||
c := strings.Split(p.String(0), " ")
|
||||
|
||||
const sep = '|'
|
||||
numCased := 0
|
||||
test := ""
|
||||
for ; len(c) >= 2; c = c[2:] {
|
||||
if c[0] == "÷" && test != "" {
|
||||
test += string(sep)
|
||||
}
|
||||
i, err := strconv.ParseUint(c[1], 16, 32)
|
||||
r := rune(i)
|
||||
if err != nil {
|
||||
log.Fatalf("Invalid rune %q.", c[1])
|
||||
}
|
||||
if r == sep {
|
||||
log.Fatalf("Separator %q not allowed in test data. Pick another one.", sep)
|
||||
}
|
||||
if cased[r] {
|
||||
numCased++
|
||||
}
|
||||
test += string(r)
|
||||
}
|
||||
if numCased > 1 {
|
||||
fmt.Fprintf(w, "\t\t%q,\n", test)
|
||||
}
|
||||
})
|
||||
fmt.Fprintln(w, "\t}")
|
||||
|
||||
fmt.Fprintln(w, ")")
|
||||
|
||||
gen.WriteGoFile("tables_test.go", "cases", w.Bytes())
|
||||
}
|
||||
|
||||
// These functions are just used for verification that their definition have not
|
||||
// changed in the Unicode Standard.
|
||||
|
||||
func verifyCased(r rune) bool {
|
||||
return verifyLower(r) || verifyUpper(r) || unicode.IsTitle(r)
|
||||
}
|
||||
|
||||
func verifyLower(r rune) bool {
|
||||
return unicode.IsLower(r) || unicode.Is(unicode.Other_Lowercase, r)
|
||||
}
|
||||
|
||||
func verifyUpper(r rune) bool {
|
||||
return unicode.IsUpper(r) || unicode.Is(unicode.Other_Uppercase, r)
|
||||
}
|
||||
|
||||
// verifyIgnore is an approximation of the Case_Ignorable property using the
|
||||
// core unicode package. It is used to reduce the size of the test data.
|
||||
func verifyIgnore(r rune) bool {
|
||||
props := []*unicode.RangeTable{
|
||||
unicode.Mn,
|
||||
unicode.Me,
|
||||
unicode.Cf,
|
||||
unicode.Lm,
|
||||
unicode.Sk,
|
||||
}
|
||||
for _, p := range props {
|
||||
if unicode.Is(p, r) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// printProperties prints tables of rune properties from the given UCD file.
|
||||
// A filter func f can be given to exclude certain values. A rune r will have
|
||||
// the indicated property if it is in the generated table or if f(r).
|
||||
func printProperties(w io.Writer, file, property string, f func(r rune) bool) int {
|
||||
verify := map[rune]bool{}
|
||||
n := 0
|
||||
varNameParts := strings.Split(property, "_")
|
||||
varNameParts[0] = strings.ToLower(varNameParts[0])
|
||||
fmt.Fprintf(w, "\t%s = map[rune]bool{\n", strings.Join(varNameParts, ""))
|
||||
parse(file, func(p *ucd.Parser) {
|
||||
if p.String(1) == property {
|
||||
r := p.Rune(0)
|
||||
verify[r] = true
|
||||
if !f(r) {
|
||||
n++
|
||||
fmt.Fprintf(w, "\t\t0x%.4x: true,\n", r)
|
||||
}
|
||||
}
|
||||
})
|
||||
fmt.Fprint(w, "\t}\n\n")
|
||||
|
||||
// Verify that f is correct, that is, it represents a subset of the property.
|
||||
for r := rune(0); r <= lastRuneForTesting; r++ {
|
||||
if !verify[r] && f(r) {
|
||||
log.Fatalf("Incorrect filter func for property %q.", property)
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// The newCaseTrie, sparseValues and sparseOffsets definitions below are
|
||||
// placeholders referred to by gen_trieval.go. The real definitions are
|
||||
// generated by this program and written to tables.go.
|
||||
|
||||
func newCaseTrie(int) int { return 0 }
|
||||
|
||||
var (
|
||||
sparseValues [0]valueRange
|
||||
sparseOffsets [0]uint16
|
||||
)
|
||||
217
vendor/golang.org/x/text/cases/gen_trieval.go
generated
vendored
Normal file
217
vendor/golang.org/x/text/cases/gen_trieval.go
generated
vendored
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
// This file contains definitions for interpreting the trie value of the case
|
||||
// trie generated by "go run gen*.go". It is shared by both the generator
|
||||
// program and the resultant package. Sharing is achieved by the generator
|
||||
// copying gen_trieval.go to trieval.go and changing what's above this comment.
|
||||
|
||||
// info holds case information for a single rune. It is the value returned
|
||||
// by a trie lookup. Most mapping information can be stored in a single 16-bit
|
||||
// value. If not, for example when a rune is mapped to multiple runes, the value
|
||||
// stores some basic case data and an index into an array with additional data.
|
||||
//
|
||||
// The per-rune values have the following format:
|
||||
//
|
||||
// if (exception) {
|
||||
// 15..5 unsigned exception index
|
||||
// 4 unused
|
||||
// } else {
|
||||
// 15..8 XOR pattern or index to XOR pattern for case mapping
|
||||
// Only 13..8 are used for XOR patterns.
|
||||
// 7 inverseFold (fold to upper, not to lower)
|
||||
// 6 index: interpret the XOR pattern as an index
|
||||
// 5..4 CCC: zero (normal or break), above or other
|
||||
// }
|
||||
// 3 exception: interpret this value as an exception index
|
||||
// (TODO: is this bit necessary? Probably implied from case mode.)
|
||||
// 2..0 case mode
|
||||
//
|
||||
// For the non-exceptional cases, a rune must be either uncased, lowercase or
|
||||
// uppercase. If the rune is cased, the XOR pattern maps either a lowercase
|
||||
// rune to uppercase or an uppercase rune to lowercase (applied to the 10
|
||||
// least-significant bits of the rune).
|
||||
//
|
||||
// See the definitions below for a more detailed description of the various
|
||||
// bits.
|
||||
type info uint16
|
||||
|
||||
const (
|
||||
casedMask = 0x0003
|
||||
fullCasedMask = 0x0007
|
||||
ignorableMask = 0x0006
|
||||
ignorableValue = 0x0004
|
||||
|
||||
inverseFoldBit = 1 << 7
|
||||
|
||||
exceptionBit = 1 << 3
|
||||
exceptionShift = 5
|
||||
numExceptionBits = 11
|
||||
|
||||
xorIndexBit = 1 << 6
|
||||
xorShift = 8
|
||||
|
||||
// There is no mapping if all xor bits and the exception bit are zero.
|
||||
hasMappingMask = 0xffc0 | exceptionBit
|
||||
)
|
||||
|
||||
// The case mode bits encodes the case type of a rune. This includes uncased,
|
||||
// title, upper and lower case and case ignorable. (For a definition of these
|
||||
// terms see Chapter 3 of The Unicode Standard Core Specification.) In some rare
|
||||
// cases, a rune can be both cased and case-ignorable. This is encoded by
|
||||
// cIgnorableCased. A rune of this type is always lower case. Some runes are
|
||||
// cased while not having a mapping.
|
||||
//
|
||||
// A common pattern for scripts in the Unicode standard is for upper and lower
|
||||
// case runes to alternate for increasing rune values (e.g. the accented Latin
|
||||
// ranges starting from U+0100 and U+1E00 among others and some Cyrillic
|
||||
// characters). We use this property by defining a cXORCase mode, where the case
|
||||
// mode (always upper or lower case) is derived from the rune value. As the XOR
|
||||
// pattern for case mappings is often identical for successive runes, using
|
||||
// cXORCase can result in large series of identical trie values. This, in turn,
|
||||
// allows us to better compress the trie blocks.
|
||||
const (
|
||||
cUncased info = iota // 000
|
||||
cTitle // 001
|
||||
cLower // 010
|
||||
cUpper // 011
|
||||
cIgnorableUncased // 100
|
||||
cIgnorableCased // 101 // lower case if mappings exist
|
||||
cXORCase // 11x // case is cLower | ((rune&1) ^ x)
|
||||
|
||||
maxCaseMode = cUpper
|
||||
)
|
||||
|
||||
func (c info) isCased() bool {
|
||||
return c&casedMask != 0
|
||||
}
|
||||
|
||||
func (c info) isCaseIgnorable() bool {
|
||||
return c&ignorableMask == ignorableValue
|
||||
}
|
||||
|
||||
func (c info) isCaseIgnorableAndNonBreakStarter() bool {
|
||||
return c&(fullCasedMask|cccMask) == (ignorableValue | cccZero)
|
||||
}
|
||||
|
||||
func (c info) isNotCasedAndNotCaseIgnorable() bool {
|
||||
return c&fullCasedMask == 0
|
||||
}
|
||||
|
||||
func (c info) isCaseIgnorableAndNotCased() bool {
|
||||
return c&fullCasedMask == cIgnorableUncased
|
||||
}
|
||||
|
||||
// The case mapping implementation will need to know about various Canonical
|
||||
// Combining Class (CCC) values. We encode two of these in the trie value:
|
||||
// cccZero (0) and cccAbove (230). If the value is cccOther, it means that
|
||||
// CCC(r) > 0, but not 230. A value of cccBreak means that CCC(r) == 0 and that
|
||||
// the rune also has the break category Break (see below).
|
||||
const (
|
||||
cccBreak info = iota << 4
|
||||
cccZero
|
||||
cccAbove
|
||||
cccOther
|
||||
|
||||
cccMask = cccBreak | cccZero | cccAbove | cccOther
|
||||
)
|
||||
|
||||
const (
|
||||
starter = 0
|
||||
above = 230
|
||||
iotaSubscript = 240
|
||||
)
|
||||
|
||||
// The exceptions slice holds data that does not fit in a normal info entry.
|
||||
// The entry is pointed to by the exception index in an entry. It has the
|
||||
// following format:
|
||||
//
|
||||
// Header
|
||||
// byte 0:
|
||||
// 7..6 unused
|
||||
// 5..4 CCC type (same bits as entry)
|
||||
// 3 unused
|
||||
// 2..0 length of fold
|
||||
//
|
||||
// byte 1:
|
||||
// 7..6 unused
|
||||
// 5..3 length of 1st mapping of case type
|
||||
// 2..0 length of 2nd mapping of case type
|
||||
//
|
||||
// case 1st 2nd
|
||||
// lower -> upper, title
|
||||
// upper -> lower, title
|
||||
// title -> lower, upper
|
||||
//
|
||||
// Lengths with the value 0x7 indicate no value and implies no change.
|
||||
// A length of 0 indicates a mapping to zero-length string.
|
||||
//
|
||||
// Body bytes:
|
||||
// case folding bytes
|
||||
// lowercase mapping bytes
|
||||
// uppercase mapping bytes
|
||||
// titlecase mapping bytes
|
||||
// closure mapping bytes (for NFKC_Casefold). (TODO)
|
||||
//
|
||||
// Fallbacks:
|
||||
// missing fold -> lower
|
||||
// missing title -> upper
|
||||
// all missing -> original rune
|
||||
//
|
||||
// exceptions starts with a dummy byte to enforce that there is no zero index
|
||||
// value.
|
||||
const (
|
||||
lengthMask = 0x07
|
||||
lengthBits = 3
|
||||
noChange = 0
|
||||
)
|
||||
|
||||
// References to generated trie.
|
||||
|
||||
var trie = newCaseTrie(0)
|
||||
|
||||
var sparse = sparseBlocks{
|
||||
values: sparseValues[:],
|
||||
offsets: sparseOffsets[:],
|
||||
}
|
||||
|
||||
// Sparse block lookup code.
|
||||
|
||||
// valueRange is an entry in a sparse block.
|
||||
type valueRange struct {
|
||||
value uint16
|
||||
lo, hi byte
|
||||
}
|
||||
|
||||
type sparseBlocks struct {
|
||||
values []valueRange
|
||||
offsets []uint16
|
||||
}
|
||||
|
||||
// lookup returns the value from values block n for byte b using binary search.
|
||||
func (s *sparseBlocks) lookup(n uint32, b byte) uint16 {
|
||||
lo := s.offsets[n]
|
||||
hi := s.offsets[n+1]
|
||||
for lo < hi {
|
||||
m := lo + (hi-lo)/2
|
||||
r := s.values[m]
|
||||
if r.lo <= b && b <= r.hi {
|
||||
return r.value
|
||||
}
|
||||
if b < r.lo {
|
||||
hi = m
|
||||
} else {
|
||||
lo = m + 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// lastRuneForTesting is the last rune used for testing. Everything after this
|
||||
// is boring.
|
||||
const lastRuneForTesting = rune(0x1FFFF)
|
||||
83
vendor/golang.org/x/text/cases/info.go
generated
vendored
Normal file
83
vendor/golang.org/x/text/cases/info.go
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// 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 cases
|
||||
|
||||
func (c info) cccVal() info {
|
||||
if c&exceptionBit != 0 {
|
||||
return info(exceptions[c>>exceptionShift]) & cccMask
|
||||
}
|
||||
return c & cccMask
|
||||
}
|
||||
|
||||
func (c info) cccType() info {
|
||||
ccc := c.cccVal()
|
||||
if ccc <= cccZero {
|
||||
return cccZero
|
||||
}
|
||||
return ccc
|
||||
}
|
||||
|
||||
// TODO: Implement full Unicode breaking algorithm:
|
||||
// 1) Implement breaking in separate package.
|
||||
// 2) Use the breaker here.
|
||||
// 3) Compare table size and performance of using the more generic breaker.
|
||||
//
|
||||
// Note that we can extend the current algorithm to be much more accurate. This
|
||||
// only makes sense, though, if the performance and/or space penalty of using
|
||||
// the generic breaker is big. Extra data will only be needed for non-cased
|
||||
// runes, which means there are sufficient bits left in the caseType.
|
||||
// Also note that the standard breaking algorithm doesn't always make sense
|
||||
// for title casing. For example, a4a -> A4a, but a"4a -> A"4A (where " stands
|
||||
// for modifier \u0308).
|
||||
// ICU prohibits breaking in such cases as well.
|
||||
|
||||
// For the purpose of title casing we use an approximation of the Unicode Word
|
||||
// Breaking algorithm defined in Annex #29:
|
||||
// http://www.unicode.org/reports/tr29/#Default_Grapheme_Cluster_Table.
|
||||
//
|
||||
// For our approximation, we group the Word Break types into the following
|
||||
// categories, with associated rules:
|
||||
//
|
||||
// 1) Letter:
|
||||
// ALetter, Hebrew_Letter, Numeric, ExtendNumLet, Extend.
|
||||
// Rule: Never break between consecutive runes of this category.
|
||||
//
|
||||
// 2) Mid:
|
||||
// Format, MidLetter, MidNumLet, Single_Quote.
|
||||
// (Cf. case-ignorable: MidLetter, MidNumLet or cat is Mn, Me, Cf, Lm or Sk).
|
||||
// Rule: Don't break between Letter and Mid, but break between two Mids.
|
||||
//
|
||||
// 3) Break:
|
||||
// Any other category, including NewLine, CR, LF and Double_Quote. These
|
||||
// categories should always result in a break between two cased letters.
|
||||
// Rule: Always break.
|
||||
//
|
||||
// Note 1: the Katakana and MidNum categories can, in esoteric cases, result in
|
||||
// preventing a break between two cased letters. For now we will ignore this
|
||||
// (e.g. [ALetter] [ExtendNumLet] [Katakana] [ExtendNumLet] [ALetter] and
|
||||
// [ALetter] [Numeric] [MidNum] [Numeric] [ALetter].)
|
||||
//
|
||||
// Note 2: the rule for Mid is very approximate, but works in most cases. To
|
||||
// improve, we could store the categories in the trie value and use a FA to
|
||||
// manage breaks. See TODO comment above.
|
||||
//
|
||||
// Note 3: according to the spec, it is possible for the Extend category to
|
||||
// introduce breaks between other categories grouped in Letter. However, this
|
||||
// is undesirable for our purposes. ICU prevents breaks in such cases as well.
|
||||
|
||||
// isBreak returns whether this rune should introduce a break.
|
||||
func (c info) isBreak() bool {
|
||||
return c.cccVal() == cccBreak
|
||||
}
|
||||
|
||||
// isLetter returns whether the rune is of break type ALetter, Hebrew_Letter,
|
||||
// Numeric, ExtendNumLet, or Extend.
|
||||
func (c info) isLetter() bool {
|
||||
ccc := c.cccVal()
|
||||
if ccc == cccZero {
|
||||
return !c.isCaseIgnorable()
|
||||
}
|
||||
return ccc != cccBreak
|
||||
}
|
||||
599
vendor/golang.org/x/text/cases/map.go
generated
vendored
Normal file
599
vendor/golang.org/x/text/cases/map.go
generated
vendored
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
// Copyright 2014 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 cases
|
||||
|
||||
// This file contains the definitions of case mappings for all supported
|
||||
// languages. The rules for the language-specific tailorings were taken and
|
||||
// modified from the CLDR transform definitions in common/transforms.
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// A mapFunc takes a context set to the current rune and writes the mapped
|
||||
// version to the same context. It may advance the context to the next rune. It
|
||||
// returns whether a checkpoint is possible: whether the pDst bytes written to
|
||||
// dst so far won't need changing as we see more source bytes.
|
||||
type mapFunc func(*context) bool
|
||||
|
||||
// maxIgnorable defines the maximum number of ignorables to consider for
|
||||
// lookahead operations.
|
||||
const maxIgnorable = 30
|
||||
|
||||
// supported lists the language tags for which we have tailorings.
|
||||
const supported = "und af az el lt nl tr"
|
||||
|
||||
func init() {
|
||||
tags := []language.Tag{}
|
||||
for _, s := range strings.Split(supported, " ") {
|
||||
tags = append(tags, language.MustParse(s))
|
||||
}
|
||||
matcher = language.NewMatcher(tags)
|
||||
Supported = language.NewCoverage(tags)
|
||||
}
|
||||
|
||||
var (
|
||||
matcher language.Matcher
|
||||
|
||||
Supported language.Coverage
|
||||
|
||||
// We keep the following lists separate, instead of having a single per-
|
||||
// language struct, to give the compiler a chance to remove unused code.
|
||||
|
||||
// Some uppercase mappers are stateless, so we can precompute the
|
||||
// Transformers and save a bit on runtime allocations.
|
||||
upperFunc = []mapFunc{
|
||||
nil, // und
|
||||
nil, // af
|
||||
aztrUpper(upper), // az
|
||||
elUpper, // el
|
||||
ltUpper(upper), // lt
|
||||
nil, // nl
|
||||
aztrUpper(upper), // tr
|
||||
}
|
||||
|
||||
undUpper transform.Transformer = &undUpperCaser{}
|
||||
|
||||
lowerFunc = []mapFunc{
|
||||
lower, // und
|
||||
lower, // af
|
||||
aztrLower, // az
|
||||
lower, // el
|
||||
ltLower, // lt
|
||||
lower, // nl
|
||||
aztrLower, // tr
|
||||
}
|
||||
|
||||
titleInfos = []struct {
|
||||
title, lower mapFunc
|
||||
rewrite func(*context)
|
||||
}{
|
||||
{title, lower, nil}, // und
|
||||
{title, lower, afnlRewrite}, // af
|
||||
{aztrUpper(title), aztrLower, nil}, // az
|
||||
{title, lower, nil}, // el
|
||||
{ltUpper(title), ltLower, nil}, // lt
|
||||
{nlTitle, lower, afnlRewrite}, // nl
|
||||
{aztrUpper(title), aztrLower, nil}, // tr
|
||||
}
|
||||
)
|
||||
|
||||
func makeUpper(t language.Tag, o options) transform.Transformer {
|
||||
_, i, _ := matcher.Match(t)
|
||||
f := upperFunc[i]
|
||||
if f == nil {
|
||||
return undUpper
|
||||
}
|
||||
return &simpleCaser{f: f}
|
||||
}
|
||||
|
||||
func makeLower(t language.Tag, o options) transform.Transformer {
|
||||
_, i, _ := matcher.Match(t)
|
||||
f := lowerFunc[i]
|
||||
if o.noFinalSigma {
|
||||
return &simpleCaser{f: f}
|
||||
}
|
||||
return &lowerCaser{
|
||||
first: f,
|
||||
midWord: finalSigma(f),
|
||||
}
|
||||
}
|
||||
|
||||
func makeTitle(t language.Tag, o options) transform.Transformer {
|
||||
_, i, _ := matcher.Match(t)
|
||||
x := &titleInfos[i]
|
||||
lower := x.lower
|
||||
if o.noLower {
|
||||
lower = (*context).copy
|
||||
} else if !o.noFinalSigma {
|
||||
lower = finalSigma(lower)
|
||||
}
|
||||
return &titleCaser{
|
||||
title: x.title,
|
||||
lower: lower,
|
||||
rewrite: x.rewrite,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: consider a similar special case for the fast majority lower case. This
|
||||
// is a bit more involved so will require some more precise benchmarking to
|
||||
// justify it.
|
||||
|
||||
type undUpperCaser struct{ transform.NopResetter }
|
||||
|
||||
// undUpperCaser implements the Transformer interface for doing an upper case
|
||||
// mapping for the root locale (und). It eliminates the need for an allocation
|
||||
// as it prevents escaping by not using function pointers.
|
||||
func (t *undUpperCaser) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
c := context{dst: dst, src: src, atEOF: atEOF}
|
||||
for c.next() {
|
||||
upper(&c)
|
||||
c.checkpoint()
|
||||
}
|
||||
return c.ret()
|
||||
}
|
||||
|
||||
type simpleCaser struct {
|
||||
context
|
||||
f mapFunc
|
||||
}
|
||||
|
||||
// simpleCaser implements the Transformer interface for doing a case operation
|
||||
// on a rune-by-rune basis.
|
||||
func (t *simpleCaser) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
t.context = context{dst: dst, src: src, atEOF: atEOF}
|
||||
c := &t.context
|
||||
for c.next() && t.f(c) {
|
||||
c.checkpoint()
|
||||
}
|
||||
return c.ret()
|
||||
}
|
||||
|
||||
// lowerCaser implements the Transformer interface. The default Unicode lower
|
||||
// casing requires different treatment for the first and subsequent characters
|
||||
// of a word, most notably to handle the Greek final Sigma.
|
||||
type lowerCaser struct {
|
||||
context
|
||||
|
||||
first, midWord mapFunc
|
||||
}
|
||||
|
||||
func (t *lowerCaser) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
t.context = context{dst: dst, src: src, atEOF: atEOF}
|
||||
c := &t.context
|
||||
|
||||
for isInterWord := true; c.next(); {
|
||||
if isInterWord {
|
||||
if c.info.isCased() {
|
||||
if !t.first(c) {
|
||||
break
|
||||
}
|
||||
isInterWord = false
|
||||
} else if !c.copy() {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if c.info.isNotCasedAndNotCaseIgnorable() {
|
||||
if !c.copy() {
|
||||
break
|
||||
}
|
||||
isInterWord = true
|
||||
} else if !t.midWord(c) {
|
||||
break
|
||||
}
|
||||
}
|
||||
c.checkpoint()
|
||||
}
|
||||
return c.ret()
|
||||
}
|
||||
|
||||
// titleCaser implements the Transformer interface. Title casing algorithms
|
||||
// distinguish between the first letter of a word and subsequent letters of the
|
||||
// same word. It uses state to avoid requiring a potentially infinite lookahead.
|
||||
type titleCaser struct {
|
||||
context
|
||||
|
||||
// rune mappings used by the actual casing algorithms.
|
||||
title, lower mapFunc
|
||||
|
||||
rewrite func(*context)
|
||||
}
|
||||
|
||||
// Transform implements the standard Unicode title case algorithm as defined in
|
||||
// Chapter 3 of The Unicode Standard:
|
||||
// toTitlecase(X): Find the word boundaries in X according to Unicode Standard
|
||||
// Annex #29, "Unicode Text Segmentation." For each word boundary, find the
|
||||
// first cased character F following the word boundary. If F exists, map F to
|
||||
// Titlecase_Mapping(F); then map all characters C between F and the following
|
||||
// word boundary to Lowercase_Mapping(C).
|
||||
func (t *titleCaser) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
t.context = context{dst: dst, src: src, atEOF: atEOF, isMidWord: t.isMidWord}
|
||||
c := &t.context
|
||||
|
||||
if !c.next() {
|
||||
return c.ret()
|
||||
}
|
||||
|
||||
for {
|
||||
p := c.info
|
||||
if t.rewrite != nil {
|
||||
t.rewrite(c)
|
||||
}
|
||||
|
||||
wasMid := p.isCaseIgnorableAndNonBreakStarter()
|
||||
// Break out of this loop on failure to ensure we do not modify the
|
||||
// state incorrectly.
|
||||
if p.isCased() && !p.isCaseIgnorableAndNotCased() {
|
||||
if !c.isMidWord {
|
||||
if !t.title(c) {
|
||||
break
|
||||
}
|
||||
c.isMidWord = true
|
||||
} else if !t.lower(c) {
|
||||
break
|
||||
}
|
||||
} else if !c.copy() {
|
||||
break
|
||||
}
|
||||
|
||||
// TODO: make this an "else if" if we can prove that no rune that does
|
||||
// not match the first condition of the if statement can be a break.
|
||||
if p.isBreak() {
|
||||
c.isMidWord = false
|
||||
}
|
||||
|
||||
// As we save the state of the transformer, it is safe to call
|
||||
// checkpoint after any successful write.
|
||||
c.checkpoint()
|
||||
|
||||
if !c.next() {
|
||||
break
|
||||
}
|
||||
if wasMid && c.info.isCaseIgnorableAndNonBreakStarter() {
|
||||
c.isMidWord = false
|
||||
}
|
||||
}
|
||||
return c.ret()
|
||||
}
|
||||
|
||||
// finalSigma adds Greek final Sigma handing to another casing function. It
|
||||
// determines whether a lowercased sigma should be σ or ς, by looking ahead for
|
||||
// case-ignorables and a cased letters.
|
||||
func finalSigma(f mapFunc) mapFunc {
|
||||
return func(c *context) bool {
|
||||
// ::NFD();
|
||||
// # 03A3; 03C2; 03A3; 03A3; Final_Sigma; # GREEK CAPITAL LETTER SIGMA
|
||||
// Σ } [:case-ignorable:]* [:cased:] → σ;
|
||||
// [:cased:] [:case-ignorable:]* { Σ → ς;
|
||||
// ::Any-Lower;
|
||||
// ::NFC();
|
||||
|
||||
if !c.hasPrefix("Σ") {
|
||||
return f(c)
|
||||
}
|
||||
|
||||
p := c.pDst
|
||||
c.writeString("ς")
|
||||
// We need to do one more iteration after maxIgnorable, as a cased
|
||||
// letter is not an ignorable and may modify the result.
|
||||
for i := 0; i < maxIgnorable+1; i++ {
|
||||
if !c.next() {
|
||||
return false
|
||||
}
|
||||
if !c.info.isCaseIgnorable() {
|
||||
if c.info.isCased() {
|
||||
// p+1 is guaranteed to be in bounds: if writing ς was
|
||||
// successful, p+1 will contain the second byte of ς. If not,
|
||||
// this function will have returned after c.next returned false.
|
||||
c.dst[p+1]++ // ς → σ
|
||||
}
|
||||
c.unreadRune()
|
||||
return true
|
||||
}
|
||||
// A case ignorable may also introduce a word break, so we may need
|
||||
// to continue searching even after detecting a break.
|
||||
c.isMidWord = c.isMidWord && !c.info.isBreak()
|
||||
c.copy()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// elUpper implements Greek upper casing, which entails removing a predefined
|
||||
// set of non-blocked modifiers. Note that these accents should not be removed
|
||||
// for title casing!
|
||||
// Example: "Οδός" -> "ΟΔΟΣ".
|
||||
func elUpper(c *context) bool {
|
||||
// From CLDR:
|
||||
// [:Greek:] [^[:ccc=Not_Reordered:][:ccc=Above:]]*? { [\u0313\u0314\u0301\u0300\u0306\u0342\u0308\u0304] → ;
|
||||
// [:Greek:] [^[:ccc=Not_Reordered:][:ccc=Iota_Subscript:]]*? { \u0345 → ;
|
||||
|
||||
r, _ := utf8.DecodeRune(c.src[c.pSrc:])
|
||||
oldPDst := c.pDst
|
||||
if !upper(c) {
|
||||
return false
|
||||
}
|
||||
if !unicode.Is(unicode.Greek, r) {
|
||||
return true
|
||||
}
|
||||
i := 0
|
||||
// Take the properties of the uppercased rune that is already written to the
|
||||
// destination. This saves us the trouble of having to uppercase the
|
||||
// decomposed rune again.
|
||||
if b := norm.NFD.Properties(c.dst[oldPDst:]).Decomposition(); b != nil {
|
||||
// Restore the destination position and process the decomposed rune.
|
||||
r, sz := utf8.DecodeRune(b)
|
||||
if r <= 0xFF { // See A.6.1
|
||||
return true
|
||||
}
|
||||
c.pDst = oldPDst
|
||||
// Insert the first rune and ignore the modifiers. See A.6.2.
|
||||
c.writeBytes(b[:sz])
|
||||
i = len(b[sz:]) / 2 // Greek modifiers are always of length 2.
|
||||
}
|
||||
|
||||
for ; i < maxIgnorable && c.next(); i++ {
|
||||
switch r, _ := utf8.DecodeRune(c.src[c.pSrc:]); r {
|
||||
// Above and Iota Subscript
|
||||
case 0x0300, // U+0300 COMBINING GRAVE ACCENT
|
||||
0x0301, // U+0301 COMBINING ACUTE ACCENT
|
||||
0x0304, // U+0304 COMBINING MACRON
|
||||
0x0306, // U+0306 COMBINING BREVE
|
||||
0x0308, // U+0308 COMBINING DIAERESIS
|
||||
0x0313, // U+0313 COMBINING COMMA ABOVE
|
||||
0x0314, // U+0314 COMBINING REVERSED COMMA ABOVE
|
||||
0x0342, // U+0342 COMBINING GREEK PERISPOMENI
|
||||
0x0345: // U+0345 COMBINING GREEK YPOGEGRAMMENI
|
||||
// No-op. Gobble the modifier.
|
||||
|
||||
default:
|
||||
switch v, _ := trie.lookup(c.src[c.pSrc:]); info(v).cccType() {
|
||||
case cccZero:
|
||||
c.unreadRune()
|
||||
return true
|
||||
|
||||
// We don't need to test for IotaSubscript as the only rune that
|
||||
// qualifies (U+0345) was already excluded in the switch statement
|
||||
// above. See A.4.
|
||||
|
||||
case cccAbove:
|
||||
return c.copy()
|
||||
default:
|
||||
// Some other modifier. We're still allowed to gobble Greek
|
||||
// modifiers after this.
|
||||
c.copy()
|
||||
}
|
||||
}
|
||||
}
|
||||
return i == maxIgnorable
|
||||
}
|
||||
|
||||
func ltLower(c *context) bool {
|
||||
// From CLDR:
|
||||
// # Introduce an explicit dot above when lowercasing capital I's and J's
|
||||
// # whenever there are more accents above.
|
||||
// # (of the accents used in Lithuanian: grave, acute, tilde above, and ogonek)
|
||||
// # 0049; 0069 0307; 0049; 0049; lt More_Above; # LATIN CAPITAL LETTER I
|
||||
// # 004A; 006A 0307; 004A; 004A; lt More_Above; # LATIN CAPITAL LETTER J
|
||||
// # 012E; 012F 0307; 012E; 012E; lt More_Above; # LATIN CAPITAL LETTER I WITH OGONEK
|
||||
// # 00CC; 0069 0307 0300; 00CC; 00CC; lt; # LATIN CAPITAL LETTER I WITH GRAVE
|
||||
// # 00CD; 0069 0307 0301; 00CD; 00CD; lt; # LATIN CAPITAL LETTER I WITH ACUTE
|
||||
// # 0128; 0069 0307 0303; 0128; 0128; lt; # LATIN CAPITAL LETTER I WITH TILDE
|
||||
// ::NFD();
|
||||
// I } [^[:ccc=Not_Reordered:][:ccc=Above:]]* [:ccc=Above:] → i \u0307;
|
||||
// J } [^[:ccc=Not_Reordered:][:ccc=Above:]]* [:ccc=Above:] → j \u0307;
|
||||
// Į } [^[:ccc=Not_Reordered:][:ccc=Above:]]* [:ccc=Above:] → į \u0307;
|
||||
// Ì → i \u0307 \u0300;
|
||||
// Í → i \u0307 \u0301;
|
||||
// Ĩ → i \u0307 \u0303;
|
||||
// ::Any-Lower();
|
||||
// ::NFC();
|
||||
|
||||
i := 0
|
||||
if r := c.src[c.pSrc]; r < utf8.RuneSelf {
|
||||
lower(c)
|
||||
if r != 'I' && r != 'J' {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
p := norm.NFD.Properties(c.src[c.pSrc:])
|
||||
if d := p.Decomposition(); len(d) >= 3 && (d[0] == 'I' || d[0] == 'J') {
|
||||
// UTF-8 optimization: the decomposition will only have an above
|
||||
// modifier if the last rune of the decomposition is in [U+300-U+311].
|
||||
// In all other cases, a decomposition starting with I is always
|
||||
// an I followed by modifiers that are not cased themselves. See A.2.
|
||||
if d[1] == 0xCC && d[2] <= 0x91 { // A.2.4.
|
||||
if !c.writeBytes(d[:1]) {
|
||||
return false
|
||||
}
|
||||
c.dst[c.pDst-1] += 'a' - 'A' // lower
|
||||
|
||||
// Assumption: modifier never changes on lowercase. See A.1.
|
||||
// Assumption: all modifiers added have CCC = Above. See A.2.3.
|
||||
return c.writeString("\u0307") && c.writeBytes(d[1:])
|
||||
}
|
||||
// In all other cases the additional modifiers will have a CCC
|
||||
// that is less than 230 (Above). We will insert the U+0307, if
|
||||
// needed, after these modifiers so that a string in FCD form
|
||||
// will remain so. See A.2.2.
|
||||
lower(c)
|
||||
i = 1
|
||||
} else {
|
||||
return lower(c)
|
||||
}
|
||||
}
|
||||
|
||||
for ; i < maxIgnorable && c.next(); i++ {
|
||||
switch c.info.cccType() {
|
||||
case cccZero:
|
||||
c.unreadRune()
|
||||
return true
|
||||
case cccAbove:
|
||||
return c.writeString("\u0307") && c.copy() // See A.1.
|
||||
default:
|
||||
c.copy() // See A.1.
|
||||
}
|
||||
}
|
||||
return i == maxIgnorable
|
||||
}
|
||||
|
||||
func ltUpper(f mapFunc) mapFunc {
|
||||
return func(c *context) bool {
|
||||
// From CLDR:
|
||||
// ::NFD();
|
||||
// [:Soft_Dotted:] [^[:ccc=Not_Reordered:][:ccc=Above:]]* { \u0307 → ;
|
||||
// ::Any-Upper();
|
||||
// ::NFC();
|
||||
|
||||
// TODO: See A.5. A soft-dotted rune never has an exception. This would
|
||||
// allow us to overload the exception bit and encode this property in
|
||||
// info. Need to measure performance impact of this.
|
||||
r, _ := utf8.DecodeRune(c.src[c.pSrc:])
|
||||
oldPDst := c.pDst
|
||||
if !f(c) {
|
||||
return false
|
||||
}
|
||||
if !unicode.Is(unicode.Soft_Dotted, r) {
|
||||
return true
|
||||
}
|
||||
|
||||
// We don't need to do an NFD normalization, as a soft-dotted rune never
|
||||
// contains U+0307. See A.3.
|
||||
|
||||
i := 0
|
||||
for ; i < maxIgnorable && c.next(); i++ {
|
||||
switch c.info.cccType() {
|
||||
case cccZero:
|
||||
c.unreadRune()
|
||||
return true
|
||||
case cccAbove:
|
||||
if c.hasPrefix("\u0307") {
|
||||
// We don't do a full NFC, but rather combine runes for
|
||||
// some of the common cases. (Returning NFC or
|
||||
// preserving normal form is neither a requirement nor
|
||||
// a possibility anyway).
|
||||
if !c.next() {
|
||||
return false
|
||||
}
|
||||
if c.dst[oldPDst] == 'I' && c.pDst == oldPDst+1 && c.src[c.pSrc] == 0xcc {
|
||||
s := ""
|
||||
switch c.src[c.pSrc+1] {
|
||||
case 0x80: // U+0300 COMBINING GRAVE ACCENT
|
||||
s = "\u00cc" // U+00CC LATIN CAPITAL LETTER I WITH GRAVE
|
||||
case 0x81: // U+0301 COMBINING ACUTE ACCENT
|
||||
s = "\u00cd" // U+00CD LATIN CAPITAL LETTER I WITH ACUTE
|
||||
case 0x83: // U+0303 COMBINING TILDE
|
||||
s = "\u0128" // U+0128 LATIN CAPITAL LETTER I WITH TILDE
|
||||
case 0x88: // U+0308 COMBINING DIAERESIS
|
||||
s = "\u00cf" // U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS
|
||||
default:
|
||||
}
|
||||
if s != "" {
|
||||
c.pDst = oldPDst
|
||||
return c.writeString(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
return c.copy()
|
||||
default:
|
||||
c.copy()
|
||||
}
|
||||
}
|
||||
return i == maxIgnorable
|
||||
}
|
||||
}
|
||||
|
||||
func aztrUpper(f mapFunc) mapFunc {
|
||||
return func(c *context) bool {
|
||||
// i→İ;
|
||||
if c.src[c.pSrc] == 'i' {
|
||||
return c.writeString("İ")
|
||||
}
|
||||
return f(c)
|
||||
}
|
||||
}
|
||||
|
||||
func aztrLower(c *context) (done bool) {
|
||||
// From CLDR:
|
||||
// # I and i-dotless; I-dot and i are case pairs in Turkish and Azeri
|
||||
// # 0130; 0069; 0130; 0130; tr; # LATIN CAPITAL LETTER I WITH DOT ABOVE
|
||||
// İ→i;
|
||||
// # When lowercasing, remove dot_above in the sequence I + dot_above, which will turn into i.
|
||||
// # This matches the behavior of the canonically equivalent I-dot_above
|
||||
// # 0307; ; 0307; 0307; tr After_I; # COMBINING DOT ABOVE
|
||||
// # When lowercasing, unless an I is before a dot_above, it turns into a dotless i.
|
||||
// # 0049; 0131; 0049; 0049; tr Not_Before_Dot; # LATIN CAPITAL LETTER I
|
||||
// I([^[:ccc=Not_Reordered:][:ccc=Above:]]*)\u0307 → i$1 ;
|
||||
// I→ı ;
|
||||
// ::Any-Lower();
|
||||
if c.hasPrefix("\u0130") { // İ
|
||||
return c.writeString("i")
|
||||
}
|
||||
if c.src[c.pSrc] != 'I' {
|
||||
return lower(c)
|
||||
}
|
||||
|
||||
// We ignore the lower-case I for now, but insert it later when we know
|
||||
// which form we need.
|
||||
start := c.pSrc + c.sz
|
||||
|
||||
i := 0
|
||||
Loop:
|
||||
// We check for up to n ignorables before \u0307. As \u0307 is an
|
||||
// ignorable as well, n is maxIgnorable-1.
|
||||
for ; i < maxIgnorable && c.next(); i++ {
|
||||
switch c.info.cccType() {
|
||||
case cccAbove:
|
||||
if c.hasPrefix("\u0307") {
|
||||
return c.writeString("i") && c.writeBytes(c.src[start:c.pSrc]) // ignore U+0307
|
||||
}
|
||||
done = true
|
||||
break Loop
|
||||
case cccZero:
|
||||
c.unreadRune()
|
||||
done = true
|
||||
break Loop
|
||||
default:
|
||||
// We'll write this rune after we know which starter to use.
|
||||
}
|
||||
}
|
||||
if i == maxIgnorable {
|
||||
done = true
|
||||
}
|
||||
return c.writeString("ı") && c.writeBytes(c.src[start:c.pSrc+c.sz]) && done
|
||||
}
|
||||
|
||||
func nlTitle(c *context) bool {
|
||||
// From CLDR:
|
||||
// # Special titlecasing for Dutch initial "ij".
|
||||
// ::Any-Title();
|
||||
// # Fix up Ij at the beginning of a "word" (per Any-Title, notUAX #29)
|
||||
// [:^WB=ALetter:] [:WB=Extend:]* [[:WB=MidLetter:][:WB=MidNumLet:]]? { Ij } → IJ ;
|
||||
if c.src[c.pSrc] != 'I' && c.src[c.pSrc] != 'i' {
|
||||
return title(c)
|
||||
}
|
||||
|
||||
if !c.writeString("I") || !c.next() {
|
||||
return false
|
||||
}
|
||||
if c.src[c.pSrc] == 'j' || c.src[c.pSrc] == 'J' {
|
||||
return c.writeString("J")
|
||||
}
|
||||
c.unreadRune()
|
||||
return true
|
||||
}
|
||||
|
||||
// Not part of CLDR, but see http://unicode.org/cldr/trac/ticket/7078.
|
||||
func afnlRewrite(c *context) {
|
||||
if c.hasPrefix("'") || c.hasPrefix("’") {
|
||||
c.isMidWord = true
|
||||
}
|
||||
}
|
||||
620
vendor/golang.org/x/text/cases/map_test.go
generated
vendored
Normal file
620
vendor/golang.org/x/text/cases/map_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,620 @@
|
|||
// Copyright 2014 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 cases
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/internal/testtext"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
lang string
|
||||
src interface{} // string, []string, or nil to skip test
|
||||
title interface{} // string, []string, or nil to skip test
|
||||
lower interface{} // string, []string, or nil to skip test
|
||||
upper interface{} // string, []string, or nil to skip test
|
||||
opts options
|
||||
}
|
||||
|
||||
// We don't support the NoFinalSigma option, but we use it to test the
|
||||
// underlying lower casers and to be able to compare differences in performance.
|
||||
func noFinalSigma(o *options) {
|
||||
o.noFinalSigma = true
|
||||
}
|
||||
|
||||
var testCases = []testCase{
|
||||
0: {
|
||||
lang: "und",
|
||||
src: "abc aBc ABC abC İsıI ΕΣΆΣ",
|
||||
title: "Abc Abc Abc Abc İsıi Εσάσ",
|
||||
lower: "abc abc abc abc i\u0307sıi εσάσ",
|
||||
upper: "ABC ABC ABC ABC İSII ΕΣΆΣ",
|
||||
opts: getOpts(noFinalSigma),
|
||||
},
|
||||
|
||||
1: {
|
||||
lang: "und",
|
||||
src: "abc aBc ABC abC İsıI ΕΣΆΣ Σ _Σ -Σ",
|
||||
title: "Abc Abc Abc Abc İsıi Εσάς Σ _Σ -Σ",
|
||||
lower: "abc abc abc abc i\u0307sıi εσάς σ _σ -σ",
|
||||
upper: "ABC ABC ABC ABC İSII ΕΣΆΣ Σ _Σ -Σ",
|
||||
},
|
||||
|
||||
2: { // Title cased runes.
|
||||
lang: supported,
|
||||
src: "DžA",
|
||||
title: "Dža",
|
||||
lower: "dža",
|
||||
upper: "DŽA",
|
||||
},
|
||||
|
||||
3: {
|
||||
// Title breaking.
|
||||
lang: supported,
|
||||
src: []string{
|
||||
"FOO CASE TEST",
|
||||
"DON'T DO THiS",
|
||||
"χωΡΊΣ χωΡΊΣ^a χωΡΊΣ:a χωΡΊΣ:^a χωΡΊΣ^ όμΩΣ Σ",
|
||||
"with-hyphens",
|
||||
"49ers 49ers",
|
||||
`"capitalize a^a -hyphen 0X _u a_u:a`,
|
||||
"MidNumLet a.b\u2018c\u2019d\u2024e\ufe52f\uff07f\uff0eg",
|
||||
"MidNum a,b;c\u037ed\u0589e\u060cf\u2044g\ufe50h",
|
||||
"\u0345 x\u3031x x\u05d0x \u05d0x a'.a a.a a4,a",
|
||||
},
|
||||
title: []string{
|
||||
"Foo Case Test",
|
||||
"Don't Do This",
|
||||
"Χωρίς Χωρίσ^A Χωρίσ:a Χωρίσ:^A Χωρίς^ Όμως Σ",
|
||||
"With-Hyphens",
|
||||
// Note that 49Ers is correct according to the spec.
|
||||
// TODO: provide some option to the user to treat different
|
||||
// characters as cased.
|
||||
"49Ers 49Ers",
|
||||
`"Capitalize A^A -Hyphen 0X _U A_u:a`,
|
||||
"Midnumlet A.b\u2018c\u2019d\u2024e\ufe52f\uff07f\uff0eg",
|
||||
"Midnum A,B;C\u037eD\u0589E\u060cF\u2044G\ufe50H",
|
||||
"\u0399 X\u3031X X\u05d0x \u05d0X A'.A A.a A4,A",
|
||||
},
|
||||
},
|
||||
|
||||
// TODO: These are known deviations from the options{} Unicode Word Breaking
|
||||
// Algorithm.
|
||||
// {
|
||||
// "und",
|
||||
// "x_\u3031_x a4,4a",
|
||||
// "X_\u3031_x A4,4a", // Currently is "X_\U3031_X A4,4A".
|
||||
// "x_\u3031_x a4,4a",
|
||||
// "X_\u3031_X A4,4A",
|
||||
// options{},
|
||||
// },
|
||||
|
||||
4: {
|
||||
// Tests title options
|
||||
lang: "und",
|
||||
src: "abc aBc ABC abC İsıI o'Brien",
|
||||
title: "Abc ABc ABC AbC İsıI O'Brien",
|
||||
opts: getOpts(NoLower),
|
||||
},
|
||||
|
||||
5: {
|
||||
lang: "el",
|
||||
src: "aBc ΟΔΌΣ Οδός Σο ΣΟ Σ oΣ ΟΣ σ ἕξ \u03ac",
|
||||
title: "Abc Οδός Οδός Σο Σο Σ Oς Ος Σ Ἕξ \u0386",
|
||||
lower: "abc οδός οδός σο σο σ oς ος σ ἕξ \u03ac",
|
||||
upper: "ABC ΟΔΟΣ ΟΔΟΣ ΣΟ ΣΟ Σ OΣ ΟΣ Σ ΕΞ \u0391", // Uppercase removes accents
|
||||
},
|
||||
|
||||
6: {
|
||||
lang: "tr az",
|
||||
src: "Isiİ İsıI I\u0307sIiİ İsıI\u0307 I\u0300\u0307",
|
||||
title: "Isii İsıı I\u0307sıii İsıi I\u0300\u0307",
|
||||
lower: "ısii isıı isıii isıi \u0131\u0300\u0307",
|
||||
upper: "ISİİ İSII I\u0307SIİİ İSII\u0307 I\u0300\u0307",
|
||||
},
|
||||
|
||||
7: {
|
||||
lang: "lt",
|
||||
src: "I Ï J J̈ Į Į̈ Ì Í Ĩ xi̇̈ xj̇̈ xį̇̈ xi̇̀ xi̇́ xi̇̃ XI XÏ XJ XJ̈ XĮ XĮ̈ XI̟̤",
|
||||
title: "I Ï J J̈ Į Į̈ Ì Í Ĩ Xi̇̈ Xj̇̈ Xį̇̈ Xi̇̀ Xi̇́ Xi̇̃ Xi Xi̇̈ Xj Xj̇̈ Xį Xį̇̈ Xi̟̤",
|
||||
lower: "i i̇̈ j j̇̈ į į̇̈ i̇̀ i̇́ i̇̃ xi̇̈ xj̇̈ xį̇̈ xi̇̀ xi̇́ xi̇̃ xi xi̇̈ xj xj̇̈ xį xį̇̈ xi̟̤",
|
||||
upper: "I Ï J J̈ Į Į̈ Ì Í Ĩ XÏ XJ̈ XĮ̈ XÌ XÍ XĨ XI XÏ XJ XJ̈ XĮ XĮ̈ XI̟̤",
|
||||
},
|
||||
|
||||
8: {
|
||||
lang: "lt",
|
||||
src: "\u012e\u0300 \u00cc i\u0307\u0300 i\u0307\u0301 i\u0307\u0303 i\u0307\u0308 i\u0300\u0307",
|
||||
title: "\u012e\u0300 \u00cc \u00cc \u00cd \u0128 \u00cf I\u0300\u0307",
|
||||
lower: "\u012f\u0307\u0300 i\u0307\u0300 i\u0307\u0300 i\u0307\u0301 i\u0307\u0303 i\u0307\u0308 i\u0300\u0307",
|
||||
upper: "\u012e\u0300 \u00cc \u00cc \u00cd \u0128 \u00cf I\u0300\u0307",
|
||||
},
|
||||
|
||||
9: {
|
||||
lang: "nl",
|
||||
src: "ijs IJs Ij Ijs İJ İJs aa aA 'ns 'S",
|
||||
title: "IJs IJs IJ IJs İj İjs Aa Aa 'ns 's",
|
||||
},
|
||||
|
||||
// Note: this specification is not currently part of CLDR. The same holds
|
||||
// for the leading apostrophe handling for Dutch.
|
||||
// See http://unicode.org/cldr/trac/ticket/7078.
|
||||
10: {
|
||||
lang: "af",
|
||||
src: "wag 'n bietjie",
|
||||
title: "Wag 'n Bietjie",
|
||||
lower: "wag 'n bietjie",
|
||||
upper: "WAG 'N BIETJIE",
|
||||
},
|
||||
}
|
||||
|
||||
func TestCaseMappings(t *testing.T) {
|
||||
for i, tt := range testCases {
|
||||
src, ok := tt.src.([]string)
|
||||
if !ok {
|
||||
src = strings.Split(tt.src.(string), " ")
|
||||
}
|
||||
|
||||
for _, lang := range strings.Split(tt.lang, " ") {
|
||||
tag := language.MustParse(lang)
|
||||
testEntry := func(name string, mk func(language.Tag, options) transform.Transformer, gold interface{}) {
|
||||
c := Caser{mk(tag, tt.opts)}
|
||||
if gold != nil {
|
||||
wants, ok := gold.([]string)
|
||||
if !ok {
|
||||
wants = strings.Split(gold.(string), " ")
|
||||
}
|
||||
for j, want := range wants {
|
||||
if got := c.String(src[j]); got != want {
|
||||
t.Errorf("%d:%s:\n%s.String(%+q):\ngot %+q;\nwant %+q", i, lang, name, src[j], got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
dst := make([]byte, 256) // big enough to hold any result
|
||||
src := []byte(strings.Join(src, " "))
|
||||
v := testtext.AllocsPerRun(20, func() {
|
||||
c.Transform(dst, src, true)
|
||||
})
|
||||
if v > 1.1 {
|
||||
t.Errorf("%d:%s:\n%s: number of allocs was %f; want 0", i, lang, name, v)
|
||||
}
|
||||
}
|
||||
testEntry("Upper", makeUpper, tt.upper)
|
||||
testEntry("Lower", makeLower, tt.lower)
|
||||
testEntry("Title", makeTitle, tt.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestAlloc tests that some mapping methods should not cause any allocation.
|
||||
func TestAlloc(t *testing.T) {
|
||||
dst := make([]byte, 256) // big enough to hold any result
|
||||
src := []byte(txtNonASCII)
|
||||
|
||||
for i, f := range []func() Caser{
|
||||
func() Caser { return Upper(language.Und) },
|
||||
func() Caser { return Lower(language.Und) },
|
||||
func() Caser { return Title(language.Und) },
|
||||
} {
|
||||
var c Caser
|
||||
v := testtext.AllocsPerRun(2, func() {
|
||||
c = f()
|
||||
})
|
||||
if v > 1 {
|
||||
// TODO: Right now only Upper has 1 allocation. Special-case Lower
|
||||
// and Title as well to have less allocations for the root locale.
|
||||
t.Skipf("%d:init: number of allocs was %f; want 0", i, v)
|
||||
}
|
||||
v = testtext.AllocsPerRun(2, func() {
|
||||
c.Transform(dst, src, true)
|
||||
})
|
||||
if v > 0 {
|
||||
t.Errorf("%d:transform: number of allocs was %f; want 0", i, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortBuffersAndOverflow(t *testing.T) {
|
||||
// minBufSize is the size of the buffer by which the casing operation in
|
||||
// this package are guaranteed to make progress.
|
||||
const minBufSize = norm.MaxSegmentSize
|
||||
|
||||
for i, tt := range []struct {
|
||||
desc, src, want string
|
||||
firstErr error
|
||||
dstSize, srcSize int
|
||||
t transform.Transformer
|
||||
}{{
|
||||
desc: "und upper: short dst",
|
||||
src: "abcdefg",
|
||||
want: "ABCDEFG",
|
||||
firstErr: transform.ErrShortDst,
|
||||
dstSize: 3,
|
||||
srcSize: minBufSize,
|
||||
t: Upper(language.Und),
|
||||
}, {
|
||||
desc: "und upper: short src",
|
||||
src: "123é56",
|
||||
want: "123É56",
|
||||
firstErr: transform.ErrShortSrc,
|
||||
dstSize: 4,
|
||||
srcSize: 4,
|
||||
t: Upper(language.Und),
|
||||
}, {
|
||||
desc: "und upper: no error on short",
|
||||
src: "12",
|
||||
want: "12",
|
||||
firstErr: nil,
|
||||
dstSize: 1,
|
||||
srcSize: 1,
|
||||
t: Upper(language.Und),
|
||||
}, {
|
||||
desc: "und lower: short dst",
|
||||
src: "ABCDEFG",
|
||||
want: "abcdefg",
|
||||
firstErr: transform.ErrShortDst,
|
||||
dstSize: 3,
|
||||
srcSize: minBufSize,
|
||||
t: Lower(language.Und),
|
||||
}, {
|
||||
desc: "und lower: short src",
|
||||
src: "123É56",
|
||||
want: "123é56",
|
||||
firstErr: transform.ErrShortSrc,
|
||||
dstSize: 4,
|
||||
srcSize: 4,
|
||||
t: Lower(language.Und),
|
||||
}, {
|
||||
desc: "und lower: no error on short",
|
||||
src: "12",
|
||||
want: "12",
|
||||
firstErr: nil,
|
||||
dstSize: 1,
|
||||
srcSize: 1,
|
||||
t: Lower(language.Und),
|
||||
}, {
|
||||
desc: "final sigma: no error",
|
||||
src: "ΟΣ",
|
||||
want: "Ος",
|
||||
dstSize: minBufSize,
|
||||
srcSize: minBufSize,
|
||||
t: Title(language.Und),
|
||||
}, {
|
||||
desc: "final sigma: short source",
|
||||
src: "ΟΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣ",
|
||||
want: "Οσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσς",
|
||||
firstErr: transform.ErrShortSrc,
|
||||
dstSize: minBufSize,
|
||||
srcSize: 10,
|
||||
t: Title(language.Und),
|
||||
}, {
|
||||
desc: "final sigma: short destination 1",
|
||||
src: "ΟΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣ",
|
||||
want: "Οσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσς",
|
||||
firstErr: transform.ErrShortDst,
|
||||
dstSize: 10,
|
||||
srcSize: minBufSize,
|
||||
t: Title(language.Und),
|
||||
}, {
|
||||
desc: "final sigma: short destination 2",
|
||||
src: "ΟΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣ",
|
||||
want: "Οσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσς",
|
||||
firstErr: transform.ErrShortDst,
|
||||
dstSize: 9,
|
||||
srcSize: minBufSize,
|
||||
t: Title(language.Und),
|
||||
}, {
|
||||
desc: "final sigma: short destination 3",
|
||||
src: "ΟΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣΣ",
|
||||
want: "Οσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσσς",
|
||||
firstErr: transform.ErrShortDst,
|
||||
dstSize: 8,
|
||||
srcSize: minBufSize,
|
||||
t: Title(language.Und),
|
||||
}, {
|
||||
desc: "clipped UTF-8 rune",
|
||||
src: "σσσσσσσσσσσ",
|
||||
want: "Σσσσσσσσσσσ",
|
||||
firstErr: transform.ErrShortSrc,
|
||||
dstSize: minBufSize,
|
||||
srcSize: 5,
|
||||
t: Title(language.Und),
|
||||
}, {
|
||||
desc: "clipped UTF-8 rune atEOF",
|
||||
src: "σσσ" + string([]byte{0xC0}),
|
||||
want: "Σσσ" + string([]byte{0xC0}),
|
||||
dstSize: minBufSize,
|
||||
srcSize: minBufSize,
|
||||
t: Title(language.Und),
|
||||
}, {
|
||||
// Note: the choice to change the final sigma at the end in case of
|
||||
// too many case ignorables is arbitrary. The main reason for this
|
||||
// choice is that it results in simpler code.
|
||||
desc: "final sigma: max ignorables",
|
||||
src: "ΟΣ" + strings.Repeat(".", maxIgnorable) + "a",
|
||||
want: "Οσ" + strings.Repeat(".", maxIgnorable) + "a",
|
||||
dstSize: minBufSize,
|
||||
srcSize: minBufSize,
|
||||
t: Title(language.Und),
|
||||
}, {
|
||||
// Note: the choice to change the final sigma at the end in case of
|
||||
// too many case ignorables is arbitrary. The main reason for this
|
||||
// choice is that it results in simpler code.
|
||||
desc: "final sigma: too many ignorables",
|
||||
src: "ΟΣ" + strings.Repeat(".", maxIgnorable+1) + "a",
|
||||
want: "Ος" + strings.Repeat(".", maxIgnorable+1) + "a",
|
||||
dstSize: minBufSize,
|
||||
srcSize: len("ΟΣ" + strings.Repeat(".", maxIgnorable+1)),
|
||||
t: Title(language.Und),
|
||||
}, {
|
||||
desc: "el upper: max ignorables",
|
||||
src: "ο" + strings.Repeat("\u0321", maxIgnorable-1) + "\u0313",
|
||||
want: "Ο" + strings.Repeat("\u0321", maxIgnorable-1),
|
||||
dstSize: minBufSize,
|
||||
srcSize: minBufSize,
|
||||
t: Upper(language.Greek),
|
||||
}, {
|
||||
desc: "el upper: too many ignorables",
|
||||
src: "ο" + strings.Repeat("\u0321", maxIgnorable) + "\u0313",
|
||||
want: "Ο" + strings.Repeat("\u0321", maxIgnorable) + "\u0313",
|
||||
dstSize: minBufSize,
|
||||
srcSize: len("ο" + strings.Repeat("\u0321", maxIgnorable)),
|
||||
t: Upper(language.Greek),
|
||||
}, {
|
||||
desc: "el upper: short dst",
|
||||
src: "123ο",
|
||||
want: "123Ο",
|
||||
firstErr: transform.ErrShortDst,
|
||||
dstSize: 3,
|
||||
srcSize: minBufSize,
|
||||
t: Upper(language.Greek),
|
||||
}, {
|
||||
desc: "lt lower: max ignorables",
|
||||
src: "I" + strings.Repeat("\u0321", maxIgnorable-1) + "\u0300",
|
||||
want: "i" + strings.Repeat("\u0321", maxIgnorable-1) + "\u0307\u0300",
|
||||
dstSize: minBufSize,
|
||||
srcSize: minBufSize,
|
||||
t: Lower(language.Lithuanian),
|
||||
}, {
|
||||
desc: "lt lower: too many ignorables",
|
||||
src: "I" + strings.Repeat("\u0321", maxIgnorable) + "\u0300",
|
||||
want: "i" + strings.Repeat("\u0321", maxIgnorable) + "\u0300",
|
||||
dstSize: minBufSize,
|
||||
srcSize: len("I" + strings.Repeat("\u0321", maxIgnorable)),
|
||||
t: Lower(language.Lithuanian),
|
||||
}, {
|
||||
desc: "lt lower: decomposition with short dst buffer 1",
|
||||
src: "aaaaa\u00cc", // U+00CC LATIN CAPITAL LETTER I GRAVE
|
||||
firstErr: transform.ErrShortDst,
|
||||
want: "aaaaai\u0307\u0300",
|
||||
dstSize: 5,
|
||||
srcSize: minBufSize,
|
||||
t: Lower(language.Lithuanian),
|
||||
}, {
|
||||
desc: "lt lower: decomposition with short dst buffer 2",
|
||||
src: "aaaa\u00cc", // U+00CC LATIN CAPITAL LETTER I GRAVE
|
||||
firstErr: transform.ErrShortDst,
|
||||
want: "aaaai\u0307\u0300",
|
||||
dstSize: 5,
|
||||
srcSize: minBufSize,
|
||||
t: Lower(language.Lithuanian),
|
||||
}, {
|
||||
desc: "lt upper: max ignorables",
|
||||
src: "i" + strings.Repeat("\u0321", maxIgnorable-1) + "\u0307\u0300",
|
||||
want: "I" + strings.Repeat("\u0321", maxIgnorable-1) + "\u0300",
|
||||
dstSize: minBufSize,
|
||||
srcSize: minBufSize,
|
||||
t: Upper(language.Lithuanian),
|
||||
}, {
|
||||
desc: "lt upper: too many ignorables",
|
||||
src: "i" + strings.Repeat("\u0321", maxIgnorable) + "\u0307\u0300",
|
||||
want: "I" + strings.Repeat("\u0321", maxIgnorable) + "\u0307\u0300",
|
||||
dstSize: minBufSize,
|
||||
srcSize: len("i" + strings.Repeat("\u0321", maxIgnorable)),
|
||||
t: Upper(language.Lithuanian),
|
||||
}, {
|
||||
desc: "lt upper: short dst",
|
||||
src: "12i\u0307\u0300",
|
||||
want: "12\u00cc",
|
||||
firstErr: transform.ErrShortDst,
|
||||
dstSize: 3,
|
||||
srcSize: minBufSize,
|
||||
t: Upper(language.Lithuanian),
|
||||
}, {
|
||||
desc: "aztr lower: max ignorables",
|
||||
src: "I" + strings.Repeat("\u0321", maxIgnorable-1) + "\u0307\u0300",
|
||||
want: "i" + strings.Repeat("\u0321", maxIgnorable-1) + "\u0300",
|
||||
dstSize: minBufSize,
|
||||
srcSize: minBufSize,
|
||||
t: Lower(language.Turkish),
|
||||
}, {
|
||||
desc: "aztr lower: too many ignorables",
|
||||
src: "I" + strings.Repeat("\u0321", maxIgnorable) + "\u0307\u0300",
|
||||
want: "\u0131" + strings.Repeat("\u0321", maxIgnorable) + "\u0307\u0300",
|
||||
dstSize: minBufSize,
|
||||
srcSize: len("I" + strings.Repeat("\u0321", maxIgnorable)),
|
||||
t: Lower(language.Turkish),
|
||||
}, {
|
||||
desc: "nl title: pre-IJ cutoff",
|
||||
src: " ij",
|
||||
want: " IJ",
|
||||
firstErr: transform.ErrShortDst,
|
||||
dstSize: 2,
|
||||
srcSize: minBufSize,
|
||||
t: Title(language.Dutch),
|
||||
}, {
|
||||
desc: "nl title: mid-IJ cutoff",
|
||||
src: " ij",
|
||||
want: " IJ",
|
||||
firstErr: transform.ErrShortDst,
|
||||
dstSize: 3,
|
||||
srcSize: minBufSize,
|
||||
t: Title(language.Dutch),
|
||||
}} {
|
||||
buf := make([]byte, tt.dstSize)
|
||||
got := []byte{}
|
||||
var nSrc, nDst int
|
||||
var err error
|
||||
for p := 0; p < len(tt.src); p += nSrc {
|
||||
q := p + tt.srcSize
|
||||
if q > len(tt.src) {
|
||||
q = len(tt.src)
|
||||
}
|
||||
nDst, nSrc, err = tt.t.Transform(buf, []byte(tt.src[p:q]), q == len(tt.src))
|
||||
got = append(got, buf[:nDst]...)
|
||||
|
||||
if p == 0 && err != tt.firstErr {
|
||||
t.Errorf("%d:%s:\n error was %v; want %v", i, tt.desc, err, tt.firstErr)
|
||||
break
|
||||
}
|
||||
}
|
||||
if string(got) != tt.want {
|
||||
t.Errorf("%d:%s:\ngot %+q;\nwant %+q", i, tt.desc, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var txtASCII = strings.Repeat("The quick brown fox jumps over the lazy dog. ", 50)
|
||||
|
||||
// Taken from http://creativecommons.org/licenses/by-sa/3.0/vn/
|
||||
const txt_vn = `Với các điều kiện sau: Ghi nhận công của tác giả. Nếu bạn sử
|
||||
dụng, chuyển đổi, hoặc xây dựng dự án từ nội dung được chia sẻ này, bạn phải áp
|
||||
dụng giấy phép này hoặc một giấy phép khác có các điều khoản tương tự như giấy
|
||||
phép này cho dự án của bạn. Hiểu rằng: Miễn — Bất kỳ các điều kiện nào trên đây
|
||||
cũng có thể được miễn bỏ nếu bạn được sự cho phép của người sở hữu bản quyền.
|
||||
Phạm vi công chúng — Khi tác phẩm hoặc bất kỳ chương nào của tác phẩm đã trong
|
||||
vùng dành cho công chúng theo quy định của pháp luật thì tình trạng của nó không
|
||||
bị ảnh hưởng bởi giấy phép trong bất kỳ trường hợp nào.`
|
||||
|
||||
// http://creativecommons.org/licenses/by-sa/2.5/cn/
|
||||
const txt_cn = `您可以自由: 复制、发行、展览、表演、放映、
|
||||
广播或通过信息网络传播本作品 创作演绎作品
|
||||
对本作品进行商业性使用 惟须遵守下列条件:
|
||||
署名 — 您必须按照作者或者许可人指定的方式对作品进行署名。
|
||||
相同方式共享 — 如果您改变、转换本作品或者以本作品为基础进行创作,
|
||||
您只能采用与本协议相同的许可协议发布基于本作品的演绎作品。`
|
||||
|
||||
// Taken from http://creativecommons.org/licenses/by-sa/1.0/deed.ru
|
||||
const txt_ru = `При обязательном соблюдении следующих условий: Attribution — Вы
|
||||
должны атрибутировать произведение (указывать автора и источник) в порядке,
|
||||
предусмотренном автором или лицензиаром (но только так, чтобы никоим образом не
|
||||
подразумевалось, что они поддерживают вас или использование вами данного
|
||||
произведения). Υπό τις ακόλουθες προϋποθέσεις:`
|
||||
|
||||
// Taken from http://creativecommons.org/licenses/by-sa/3.0/gr/
|
||||
const txt_gr = `Αναφορά Δημιουργού — Θα πρέπει να κάνετε την αναφορά στο έργο με
|
||||
τον τρόπο που έχει οριστεί από το δημιουργό ή το χορηγούντο την άδεια (χωρίς
|
||||
όμως να εννοείται με οποιονδήποτε τρόπο ότι εγκρίνουν εσάς ή τη χρήση του έργου
|
||||
από εσάς). Παρόμοια Διανομή — Εάν αλλοιώσετε, τροποποιήσετε ή δημιουργήσετε
|
||||
περαιτέρω βασισμένοι στο έργο θα μπορείτε να διανέμετε το έργο που θα προκύψει
|
||||
μόνο με την ίδια ή παρόμοια άδεια.`
|
||||
|
||||
const txtNonASCII = txt_vn + txt_cn + txt_ru + txt_gr
|
||||
|
||||
// TODO: Improve ASCII performance.
|
||||
|
||||
func benchFunc(b *testing.B, f func(b []byte) []byte, s string) {
|
||||
src := []byte(s)
|
||||
b.SetBytes(int64(len(src)))
|
||||
for i := 0; i < b.N; i++ {
|
||||
f(src)
|
||||
}
|
||||
}
|
||||
|
||||
func benchTransformer(b *testing.B, t transform.Transformer, s string) {
|
||||
src := []byte(s)
|
||||
dst := make([]byte, len(src))
|
||||
b.SetBytes(int64(len(src)))
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
t.Reset()
|
||||
t.Transform(dst, src, true)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
noSigma = options{noFinalSigma: true}
|
||||
)
|
||||
|
||||
func BenchmarkBytesToLower(b *testing.B) {
|
||||
benchFunc(b, bytes.ToLower, txtNonASCII)
|
||||
}
|
||||
|
||||
func BenchmarkSigmaLower(b *testing.B) {
|
||||
benchTransformer(b, makeLower(language.Und, options{}), txtNonASCII)
|
||||
}
|
||||
|
||||
func BenchmarkSimpleLower(b *testing.B) {
|
||||
benchTransformer(b, makeLower(language.Und, noSigma), txtNonASCII)
|
||||
}
|
||||
|
||||
func BenchmarkBytesToLowerASCII(b *testing.B) {
|
||||
benchFunc(b, bytes.ToLower, txtASCII)
|
||||
}
|
||||
|
||||
func BenchmarkSigmaLowerASCII(b *testing.B) {
|
||||
benchTransformer(b, makeLower(language.Und, options{}), txtASCII)
|
||||
}
|
||||
|
||||
func BenchmarkSimpleLowerASCII(b *testing.B) {
|
||||
benchTransformer(b, makeLower(language.Und, noSigma), txtASCII)
|
||||
}
|
||||
|
||||
func BenchmarkBytesToTitle(b *testing.B) {
|
||||
benchFunc(b, bytes.ToTitle, txtNonASCII)
|
||||
}
|
||||
|
||||
func BenchmarkSigmaTitle(b *testing.B) {
|
||||
benchTransformer(b, makeTitle(language.Und, options{}), txtNonASCII)
|
||||
}
|
||||
|
||||
func BenchmarkSimpleTitle(b *testing.B) {
|
||||
benchTransformer(b, makeTitle(language.Und, noSigma), txtNonASCII)
|
||||
}
|
||||
|
||||
func BenchmarkBytesToTitleASCII(b *testing.B) {
|
||||
benchFunc(b, bytes.ToTitle, txtASCII)
|
||||
}
|
||||
|
||||
func BenchmarkSigmaTitleASCII(b *testing.B) {
|
||||
benchTransformer(b, makeTitle(language.Und, options{}), txtASCII)
|
||||
}
|
||||
|
||||
func BenchmarkSimpleTitleASCII(b *testing.B) {
|
||||
benchTransformer(b, makeTitle(language.Und, noSigma), txtASCII)
|
||||
}
|
||||
|
||||
func BenchmarkBytesUpper(b *testing.B) {
|
||||
benchFunc(b, bytes.ToUpper, txtNonASCII)
|
||||
}
|
||||
|
||||
func BenchmarkUpper(b *testing.B) {
|
||||
benchTransformer(b, Upper(language.Und), txtNonASCII)
|
||||
}
|
||||
|
||||
func BenchmarkBytesUpperASCII(b *testing.B) {
|
||||
benchFunc(b, bytes.ToUpper, txtASCII)
|
||||
}
|
||||
|
||||
func BenchmarkUpperASCII(b *testing.B) {
|
||||
benchTransformer(b, Upper(language.Und), txtASCII)
|
||||
}
|
||||
|
||||
func BenchmarkUpperSmall(b *testing.B) {
|
||||
benchTransformer(b, Upper(language.Und), "При")
|
||||
}
|
||||
|
||||
func BenchmarkLowerSmall(b *testing.B) {
|
||||
benchTransformer(b, Lower(language.Und), "При")
|
||||
}
|
||||
|
||||
func BenchmarkTitleSmall(b *testing.B) {
|
||||
benchTransformer(b, Title(language.Und), "при")
|
||||
}
|
||||
2213
vendor/golang.org/x/text/cases/tables.go
generated
vendored
Normal file
2213
vendor/golang.org/x/text/cases/tables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1155
vendor/golang.org/x/text/cases/tables_test.go
generated
vendored
Normal file
1155
vendor/golang.org/x/text/cases/tables_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
213
vendor/golang.org/x/text/cases/trieval.go
generated
vendored
Normal file
213
vendor/golang.org/x/text/cases/trieval.go
generated
vendored
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
// This file was generated by go generate; DO NOT EDIT
|
||||
|
||||
package cases
|
||||
|
||||
// This file contains definitions for interpreting the trie value of the case
|
||||
// trie generated by "go run gen*.go". It is shared by both the generator
|
||||
// program and the resultant package. Sharing is achieved by the generator
|
||||
// copying gen_trieval.go to trieval.go and changing what's above this comment.
|
||||
|
||||
// info holds case information for a single rune. It is the value returned
|
||||
// by a trie lookup. Most mapping information can be stored in a single 16-bit
|
||||
// value. If not, for example when a rune is mapped to multiple runes, the value
|
||||
// stores some basic case data and an index into an array with additional data.
|
||||
//
|
||||
// The per-rune values have the following format:
|
||||
//
|
||||
// if (exception) {
|
||||
// 15..5 unsigned exception index
|
||||
// 4 unused
|
||||
// } else {
|
||||
// 15..8 XOR pattern or index to XOR pattern for case mapping
|
||||
// Only 13..8 are used for XOR patterns.
|
||||
// 7 inverseFold (fold to upper, not to lower)
|
||||
// 6 index: interpret the XOR pattern as an index
|
||||
// 5..4 CCC: zero (normal or break), above or other
|
||||
// }
|
||||
// 3 exception: interpret this value as an exception index
|
||||
// (TODO: is this bit necessary? Probably implied from case mode.)
|
||||
// 2..0 case mode
|
||||
//
|
||||
// For the non-exceptional cases, a rune must be either uncased, lowercase or
|
||||
// uppercase. If the rune is cased, the XOR pattern maps either a lowercase
|
||||
// rune to uppercase or an uppercase rune to lowercase (applied to the 10
|
||||
// least-significant bits of the rune).
|
||||
//
|
||||
// See the definitions below for a more detailed description of the various
|
||||
// bits.
|
||||
type info uint16
|
||||
|
||||
const (
|
||||
casedMask = 0x0003
|
||||
fullCasedMask = 0x0007
|
||||
ignorableMask = 0x0006
|
||||
ignorableValue = 0x0004
|
||||
|
||||
inverseFoldBit = 1 << 7
|
||||
|
||||
exceptionBit = 1 << 3
|
||||
exceptionShift = 5
|
||||
numExceptionBits = 11
|
||||
|
||||
xorIndexBit = 1 << 6
|
||||
xorShift = 8
|
||||
|
||||
// There is no mapping if all xor bits and the exception bit are zero.
|
||||
hasMappingMask = 0xffc0 | exceptionBit
|
||||
)
|
||||
|
||||
// The case mode bits encodes the case type of a rune. This includes uncased,
|
||||
// title, upper and lower case and case ignorable. (For a definition of these
|
||||
// terms see Chapter 3 of The Unicode Standard Core Specification.) In some rare
|
||||
// cases, a rune can be both cased and case-ignorable. This is encoded by
|
||||
// cIgnorableCased. A rune of this type is always lower case. Some runes are
|
||||
// cased while not having a mapping.
|
||||
//
|
||||
// A common pattern for scripts in the Unicode standard is for upper and lower
|
||||
// case runes to alternate for increasing rune values (e.g. the accented Latin
|
||||
// ranges starting from U+0100 and U+1E00 among others and some Cyrillic
|
||||
// characters). We use this property by defining a cXORCase mode, where the case
|
||||
// mode (always upper or lower case) is derived from the rune value. As the XOR
|
||||
// pattern for case mappings is often identical for successive runes, using
|
||||
// cXORCase can result in large series of identical trie values. This, in turn,
|
||||
// allows us to better compress the trie blocks.
|
||||
const (
|
||||
cUncased info = iota // 000
|
||||
cTitle // 001
|
||||
cLower // 010
|
||||
cUpper // 011
|
||||
cIgnorableUncased // 100
|
||||
cIgnorableCased // 101 // lower case if mappings exist
|
||||
cXORCase // 11x // case is cLower | ((rune&1) ^ x)
|
||||
|
||||
maxCaseMode = cUpper
|
||||
)
|
||||
|
||||
func (c info) isCased() bool {
|
||||
return c&casedMask != 0
|
||||
}
|
||||
|
||||
func (c info) isCaseIgnorable() bool {
|
||||
return c&ignorableMask == ignorableValue
|
||||
}
|
||||
|
||||
func (c info) isCaseIgnorableAndNonBreakStarter() bool {
|
||||
return c&(fullCasedMask|cccMask) == (ignorableValue | cccZero)
|
||||
}
|
||||
|
||||
func (c info) isNotCasedAndNotCaseIgnorable() bool {
|
||||
return c&fullCasedMask == 0
|
||||
}
|
||||
|
||||
func (c info) isCaseIgnorableAndNotCased() bool {
|
||||
return c&fullCasedMask == cIgnorableUncased
|
||||
}
|
||||
|
||||
// The case mapping implementation will need to know about various Canonical
|
||||
// Combining Class (CCC) values. We encode two of these in the trie value:
|
||||
// cccZero (0) and cccAbove (230). If the value is cccOther, it means that
|
||||
// CCC(r) > 0, but not 230. A value of cccBreak means that CCC(r) == 0 and that
|
||||
// the rune also has the break category Break (see below).
|
||||
const (
|
||||
cccBreak info = iota << 4
|
||||
cccZero
|
||||
cccAbove
|
||||
cccOther
|
||||
|
||||
cccMask = cccBreak | cccZero | cccAbove | cccOther
|
||||
)
|
||||
|
||||
const (
|
||||
starter = 0
|
||||
above = 230
|
||||
iotaSubscript = 240
|
||||
)
|
||||
|
||||
// The exceptions slice holds data that does not fit in a normal info entry.
|
||||
// The entry is pointed to by the exception index in an entry. It has the
|
||||
// following format:
|
||||
//
|
||||
// Header
|
||||
// byte 0:
|
||||
// 7..6 unused
|
||||
// 5..4 CCC type (same bits as entry)
|
||||
// 3 unused
|
||||
// 2..0 length of fold
|
||||
//
|
||||
// byte 1:
|
||||
// 7..6 unused
|
||||
// 5..3 length of 1st mapping of case type
|
||||
// 2..0 length of 2nd mapping of case type
|
||||
//
|
||||
// case 1st 2nd
|
||||
// lower -> upper, title
|
||||
// upper -> lower, title
|
||||
// title -> lower, upper
|
||||
//
|
||||
// Lengths with the value 0x7 indicate no value and implies no change.
|
||||
// A length of 0 indicates a mapping to zero-length string.
|
||||
//
|
||||
// Body bytes:
|
||||
// case folding bytes
|
||||
// lowercase mapping bytes
|
||||
// uppercase mapping bytes
|
||||
// titlecase mapping bytes
|
||||
// closure mapping bytes (for NFKC_Casefold). (TODO)
|
||||
//
|
||||
// Fallbacks:
|
||||
// missing fold -> lower
|
||||
// missing title -> upper
|
||||
// all missing -> original rune
|
||||
//
|
||||
// exceptions starts with a dummy byte to enforce that there is no zero index
|
||||
// value.
|
||||
const (
|
||||
lengthMask = 0x07
|
||||
lengthBits = 3
|
||||
noChange = 0
|
||||
)
|
||||
|
||||
// References to generated trie.
|
||||
|
||||
var trie = newCaseTrie(0)
|
||||
|
||||
var sparse = sparseBlocks{
|
||||
values: sparseValues[:],
|
||||
offsets: sparseOffsets[:],
|
||||
}
|
||||
|
||||
// Sparse block lookup code.
|
||||
|
||||
// valueRange is an entry in a sparse block.
|
||||
type valueRange struct {
|
||||
value uint16
|
||||
lo, hi byte
|
||||
}
|
||||
|
||||
type sparseBlocks struct {
|
||||
values []valueRange
|
||||
offsets []uint16
|
||||
}
|
||||
|
||||
// lookup returns the value from values block n for byte b using binary search.
|
||||
func (s *sparseBlocks) lookup(n uint32, b byte) uint16 {
|
||||
lo := s.offsets[n]
|
||||
hi := s.offsets[n+1]
|
||||
for lo < hi {
|
||||
m := lo + (hi-lo)/2
|
||||
r := s.values[m]
|
||||
if r.lo <= b && b <= r.hi {
|
||||
return r.value
|
||||
}
|
||||
if b < r.lo {
|
||||
hi = m
|
||||
} else {
|
||||
lo = m + 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// lastRuneForTesting is the last rune used for testing. Everything after this
|
||||
// is boring.
|
||||
const lastRuneForTesting = rune(0x1FFFF)
|
||||
1
vendor/golang.org/x/text/codereview.cfg
generated
vendored
Normal file
1
vendor/golang.org/x/text/codereview.cfg
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
issuerepo: golang/go
|
||||
699
vendor/golang.org/x/text/collate/build/builder.go
generated
vendored
Normal file
699
vendor/golang.org/x/text/collate/build/builder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,699 @@
|
|||
// Copyright 2012 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 build // import "golang.org/x/text/collate/build"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/collate/colltab"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// TODO: optimizations:
|
||||
// - expandElem is currently 20K. By putting unique colElems in a separate
|
||||
// table and having a byte array of indexes into this table, we can reduce
|
||||
// the total size to about 7K. By also factoring out the length bytes, we
|
||||
// can reduce this to about 6K.
|
||||
// - trie valueBlocks are currently 100K. There are a lot of sparse blocks
|
||||
// and many consecutive values with the same stride. This can be further
|
||||
// compacted.
|
||||
// - Compress secondary weights into 8 bits.
|
||||
// - Some LDML specs specify a context element. Currently we simply concatenate
|
||||
// those. Context can be implemented using the contraction trie. If Builder
|
||||
// could analyze and detect when using a context makes sense, there is no
|
||||
// need to expose this construct in the API.
|
||||
|
||||
// A Builder builds a root collation table. The user must specify the
|
||||
// collation elements for each entry. A common use will be to base the weights
|
||||
// on those specified in the allkeys* file as provided by the UCA or CLDR.
|
||||
type Builder struct {
|
||||
index *trieBuilder
|
||||
root ordering
|
||||
locale []*Tailoring
|
||||
t *table
|
||||
err error
|
||||
built bool
|
||||
|
||||
minNonVar int // lowest primary recorded for a variable
|
||||
varTop int // highest primary recorded for a non-variable
|
||||
|
||||
// indexes used for reusing expansions and contractions
|
||||
expIndex map[string]int // positions of expansions keyed by their string representation
|
||||
ctHandle map[string]ctHandle // contraction handles keyed by a concatenation of the suffixes
|
||||
ctElem map[string]int // contraction elements keyed by their string representation
|
||||
}
|
||||
|
||||
// A Tailoring builds a collation table based on another collation table.
|
||||
// The table is defined by specifying tailorings to the underlying table.
|
||||
// See http://unicode.org/reports/tr35/ for an overview of tailoring
|
||||
// collation tables. The CLDR contains pre-defined tailorings for a variety
|
||||
// of languages (See http://www.unicode.org/Public/cldr/<version>/core.zip.)
|
||||
type Tailoring struct {
|
||||
id string
|
||||
builder *Builder
|
||||
index *ordering
|
||||
|
||||
anchor *entry
|
||||
before bool
|
||||
}
|
||||
|
||||
// NewBuilder returns a new Builder.
|
||||
func NewBuilder() *Builder {
|
||||
return &Builder{
|
||||
index: newTrieBuilder(),
|
||||
root: makeRootOrdering(),
|
||||
expIndex: make(map[string]int),
|
||||
ctHandle: make(map[string]ctHandle),
|
||||
ctElem: make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
||||
// Tailoring returns a Tailoring for the given locale. One should
|
||||
// have completed all calls to Add before calling Tailoring.
|
||||
func (b *Builder) Tailoring(loc language.Tag) *Tailoring {
|
||||
t := &Tailoring{
|
||||
id: loc.String(),
|
||||
builder: b,
|
||||
index: b.root.clone(),
|
||||
}
|
||||
t.index.id = t.id
|
||||
b.locale = append(b.locale, t)
|
||||
return t
|
||||
}
|
||||
|
||||
// Add adds an entry to the collation element table, mapping
|
||||
// a slice of runes to a sequence of collation elements.
|
||||
// A collation element is specified as list of weights: []int{primary, secondary, ...}.
|
||||
// The entries are typically obtained from a collation element table
|
||||
// as defined in http://www.unicode.org/reports/tr10/#Data_Table_Format.
|
||||
// Note that the collation elements specified by colelems are only used
|
||||
// as a guide. The actual weights generated by Builder may differ.
|
||||
// The argument variables is a list of indices into colelems that should contain
|
||||
// a value for each colelem that is a variable. (See the reference above.)
|
||||
func (b *Builder) Add(runes []rune, colelems [][]int, variables []int) error {
|
||||
str := string(runes)
|
||||
elems := make([]rawCE, len(colelems))
|
||||
for i, ce := range colelems {
|
||||
if len(ce) == 0 {
|
||||
break
|
||||
}
|
||||
elems[i] = makeRawCE(ce, 0)
|
||||
if len(ce) == 1 {
|
||||
elems[i].w[1] = defaultSecondary
|
||||
}
|
||||
if len(ce) <= 2 {
|
||||
elems[i].w[2] = defaultTertiary
|
||||
}
|
||||
if len(ce) <= 3 {
|
||||
elems[i].w[3] = ce[0]
|
||||
}
|
||||
}
|
||||
for i, ce := range elems {
|
||||
p := ce.w[0]
|
||||
isvar := false
|
||||
for _, j := range variables {
|
||||
if i == j {
|
||||
isvar = true
|
||||
}
|
||||
}
|
||||
if isvar {
|
||||
if p >= b.minNonVar && b.minNonVar > 0 {
|
||||
return fmt.Errorf("primary value %X of variable is larger than the smallest non-variable %X", p, b.minNonVar)
|
||||
}
|
||||
if p > b.varTop {
|
||||
b.varTop = p
|
||||
}
|
||||
} else if p > 1 { // 1 is a special primary value reserved for FFFE
|
||||
if p <= b.varTop {
|
||||
return fmt.Errorf("primary value %X of non-variable is smaller than the highest variable %X", p, b.varTop)
|
||||
}
|
||||
if b.minNonVar == 0 || p < b.minNonVar {
|
||||
b.minNonVar = p
|
||||
}
|
||||
}
|
||||
}
|
||||
elems, err := convertLargeWeights(elems)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cccs := []uint8{}
|
||||
nfd := norm.NFD.String(str)
|
||||
for i := range nfd {
|
||||
cccs = append(cccs, norm.NFD.PropertiesString(nfd[i:]).CCC())
|
||||
}
|
||||
if len(cccs) < len(elems) {
|
||||
if len(cccs) > 2 {
|
||||
return fmt.Errorf("number of decomposed characters should be greater or equal to the number of collation elements for len(colelems) > 3 (%d < %d)", len(cccs), len(elems))
|
||||
}
|
||||
p := len(elems) - 1
|
||||
for ; p > 0 && elems[p].w[0] == 0; p-- {
|
||||
elems[p].ccc = cccs[len(cccs)-1]
|
||||
}
|
||||
for ; p >= 0; p-- {
|
||||
elems[p].ccc = cccs[0]
|
||||
}
|
||||
} else {
|
||||
for i := range elems {
|
||||
elems[i].ccc = cccs[i]
|
||||
}
|
||||
}
|
||||
// doNorm in collate.go assumes that the following conditions hold.
|
||||
if len(elems) > 1 && len(cccs) > 1 && cccs[0] != 0 && cccs[0] != cccs[len(cccs)-1] {
|
||||
return fmt.Errorf("incompatible CCC values for expansion %X (%d)", runes, cccs)
|
||||
}
|
||||
b.root.newEntry(str, elems)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tailoring) setAnchor(anchor string) error {
|
||||
anchor = norm.NFC.String(anchor)
|
||||
a := t.index.find(anchor)
|
||||
if a == nil {
|
||||
a = t.index.newEntry(anchor, nil)
|
||||
a.implicit = true
|
||||
a.modified = true
|
||||
for _, r := range []rune(anchor) {
|
||||
e := t.index.find(string(r))
|
||||
e.lock = true
|
||||
}
|
||||
}
|
||||
t.anchor = a
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAnchor sets the point after which elements passed in subsequent calls to
|
||||
// Insert will be inserted. It is equivalent to the reset directive in an LDML
|
||||
// specification. See Insert for an example.
|
||||
// SetAnchor supports the following logical reset positions:
|
||||
// <first_tertiary_ignorable/>, <last_teriary_ignorable/>, <first_primary_ignorable/>,
|
||||
// and <last_non_ignorable/>.
|
||||
func (t *Tailoring) SetAnchor(anchor string) error {
|
||||
if err := t.setAnchor(anchor); err != nil {
|
||||
return err
|
||||
}
|
||||
t.before = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAnchorBefore is similar to SetAnchor, except that subsequent calls to
|
||||
// Insert will insert entries before the anchor.
|
||||
func (t *Tailoring) SetAnchorBefore(anchor string) error {
|
||||
if err := t.setAnchor(anchor); err != nil {
|
||||
return err
|
||||
}
|
||||
t.before = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert sets the ordering of str relative to the entry set by the previous
|
||||
// call to SetAnchor or Insert. The argument extend corresponds
|
||||
// to the extend elements as defined in LDML. A non-empty value for extend
|
||||
// will cause the collation elements corresponding to extend to be appended
|
||||
// to the collation elements generated for the entry added by Insert.
|
||||
// This has the same net effect as sorting str after the string anchor+extend.
|
||||
// See http://www.unicode.org/reports/tr10/#Tailoring_Example for details
|
||||
// on parametric tailoring and http://unicode.org/reports/tr35/#Collation_Elements
|
||||
// for full details on LDML.
|
||||
//
|
||||
// Examples: create a tailoring for Swedish, where "ä" is ordered after "z"
|
||||
// at the primary sorting level:
|
||||
// t := b.Tailoring("se")
|
||||
// t.SetAnchor("z")
|
||||
// t.Insert(colltab.Primary, "ä", "")
|
||||
// Order "ü" after "ue" at the secondary sorting level:
|
||||
// t.SetAnchor("ue")
|
||||
// t.Insert(colltab.Secondary, "ü","")
|
||||
// or
|
||||
// t.SetAnchor("u")
|
||||
// t.Insert(colltab.Secondary, "ü", "e")
|
||||
// Order "q" afer "ab" at the secondary level and "Q" after "q"
|
||||
// at the tertiary level:
|
||||
// t.SetAnchor("ab")
|
||||
// t.Insert(colltab.Secondary, "q", "")
|
||||
// t.Insert(colltab.Tertiary, "Q", "")
|
||||
// Order "b" before "a":
|
||||
// t.SetAnchorBefore("a")
|
||||
// t.Insert(colltab.Primary, "b", "")
|
||||
// Order "0" after the last primary ignorable:
|
||||
// t.SetAnchor("<last_primary_ignorable/>")
|
||||
// t.Insert(colltab.Primary, "0", "")
|
||||
func (t *Tailoring) Insert(level colltab.Level, str, extend string) error {
|
||||
if t.anchor == nil {
|
||||
return fmt.Errorf("%s:Insert: no anchor point set for tailoring of %s", t.id, str)
|
||||
}
|
||||
str = norm.NFC.String(str)
|
||||
e := t.index.find(str)
|
||||
if e == nil {
|
||||
e = t.index.newEntry(str, nil)
|
||||
} else if e.logical != noAnchor {
|
||||
return fmt.Errorf("%s:Insert: cannot reinsert logical reset position %q", t.id, e.str)
|
||||
}
|
||||
if e.lock {
|
||||
return fmt.Errorf("%s:Insert: cannot reinsert element %q", t.id, e.str)
|
||||
}
|
||||
a := t.anchor
|
||||
// Find the first element after the anchor which differs at a level smaller or
|
||||
// equal to the given level. Then insert at this position.
|
||||
// See http://unicode.org/reports/tr35/#Collation_Elements, Section 5.14.5 for details.
|
||||
e.before = t.before
|
||||
if t.before {
|
||||
t.before = false
|
||||
if a.prev == nil {
|
||||
a.insertBefore(e)
|
||||
} else {
|
||||
for a = a.prev; a.level > level; a = a.prev {
|
||||
}
|
||||
a.insertAfter(e)
|
||||
}
|
||||
e.level = level
|
||||
} else {
|
||||
for ; a.level > level; a = a.next {
|
||||
}
|
||||
e.level = a.level
|
||||
if a != e {
|
||||
a.insertAfter(e)
|
||||
a.level = level
|
||||
} else {
|
||||
// We don't set a to prev itself. This has the effect of the entry
|
||||
// getting new collation elements that are an increment of itself.
|
||||
// This is intentional.
|
||||
a.prev.level = level
|
||||
}
|
||||
}
|
||||
e.extend = norm.NFD.String(extend)
|
||||
e.exclude = false
|
||||
e.modified = true
|
||||
e.elems = nil
|
||||
t.anchor = e
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ordering) getWeight(e *entry) []rawCE {
|
||||
if len(e.elems) == 0 && e.logical == noAnchor {
|
||||
if e.implicit {
|
||||
for _, r := range e.runes {
|
||||
e.elems = append(e.elems, o.getWeight(o.find(string(r)))...)
|
||||
}
|
||||
} else if e.before {
|
||||
count := [colltab.Identity + 1]int{}
|
||||
a := e
|
||||
for ; a.elems == nil && !a.implicit; a = a.next {
|
||||
count[a.level]++
|
||||
}
|
||||
e.elems = []rawCE{makeRawCE(a.elems[0].w, a.elems[0].ccc)}
|
||||
for i := colltab.Primary; i < colltab.Quaternary; i++ {
|
||||
if count[i] != 0 {
|
||||
e.elems[0].w[i] -= count[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if e.prev != nil {
|
||||
o.verifyWeights(e.prev, e, e.prev.level)
|
||||
}
|
||||
} else {
|
||||
prev := e.prev
|
||||
e.elems = nextWeight(prev.level, o.getWeight(prev))
|
||||
o.verifyWeights(e, e.next, e.level)
|
||||
}
|
||||
}
|
||||
return e.elems
|
||||
}
|
||||
|
||||
func (o *ordering) addExtension(e *entry) {
|
||||
if ex := o.find(e.extend); ex != nil {
|
||||
e.elems = append(e.elems, ex.elems...)
|
||||
} else {
|
||||
for _, r := range []rune(e.extend) {
|
||||
e.elems = append(e.elems, o.find(string(r)).elems...)
|
||||
}
|
||||
}
|
||||
e.extend = ""
|
||||
}
|
||||
|
||||
func (o *ordering) verifyWeights(a, b *entry, level colltab.Level) error {
|
||||
if level == colltab.Identity || b == nil || b.elems == nil || a.elems == nil {
|
||||
return nil
|
||||
}
|
||||
for i := colltab.Primary; i < level; i++ {
|
||||
if a.elems[0].w[i] < b.elems[0].w[i] {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if a.elems[0].w[level] >= b.elems[0].w[level] {
|
||||
err := fmt.Errorf("%s:overflow: collation elements of %q (%X) overflows those of %q (%X) at level %d (%X >= %X)", o.id, a.str, a.runes, b.str, b.runes, level, a.elems, b.elems)
|
||||
log.Println(err)
|
||||
// TODO: return the error instead, or better, fix the conflicting entry by making room.
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) error(e error) {
|
||||
if e != nil {
|
||||
b.err = e
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) errorID(locale string, e error) {
|
||||
if e != nil {
|
||||
b.err = fmt.Errorf("%s:%v", locale, e)
|
||||
}
|
||||
}
|
||||
|
||||
// patchNorm ensures that NFC and NFD counterparts are consistent.
|
||||
func (o *ordering) patchNorm() {
|
||||
// Insert the NFD counterparts, if necessary.
|
||||
for _, e := range o.ordered {
|
||||
nfd := norm.NFD.String(e.str)
|
||||
if nfd != e.str {
|
||||
if e0 := o.find(nfd); e0 != nil && !e0.modified {
|
||||
e0.elems = e.elems
|
||||
} else if e.modified && !equalCEArrays(o.genColElems(nfd), e.elems) {
|
||||
e := o.newEntry(nfd, e.elems)
|
||||
e.modified = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update unchanged composed forms if one of their parts changed.
|
||||
for _, e := range o.ordered {
|
||||
nfd := norm.NFD.String(e.str)
|
||||
if e.modified || nfd == e.str {
|
||||
continue
|
||||
}
|
||||
if e0 := o.find(nfd); e0 != nil {
|
||||
e.elems = e0.elems
|
||||
} else {
|
||||
e.elems = o.genColElems(nfd)
|
||||
if norm.NFD.LastBoundary([]byte(nfd)) == 0 {
|
||||
r := []rune(nfd)
|
||||
head := string(r[0])
|
||||
tail := ""
|
||||
for i := 1; i < len(r); i++ {
|
||||
s := norm.NFC.String(head + string(r[i]))
|
||||
if e0 := o.find(s); e0 != nil && e0.modified {
|
||||
head = s
|
||||
} else {
|
||||
tail += string(r[i])
|
||||
}
|
||||
}
|
||||
e.elems = append(o.genColElems(head), o.genColElems(tail)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Exclude entries for which the individual runes generate the same collation elements.
|
||||
for _, e := range o.ordered {
|
||||
if len(e.runes) > 1 && equalCEArrays(o.genColElems(e.str), e.elems) {
|
||||
e.exclude = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) buildOrdering(o *ordering) {
|
||||
for _, e := range o.ordered {
|
||||
o.getWeight(e)
|
||||
}
|
||||
for _, e := range o.ordered {
|
||||
o.addExtension(e)
|
||||
}
|
||||
o.patchNorm()
|
||||
o.sort()
|
||||
simplify(o)
|
||||
b.processExpansions(o) // requires simplify
|
||||
b.processContractions(o) // requires simplify
|
||||
|
||||
t := newNode()
|
||||
for e := o.front(); e != nil; e, _ = e.nextIndexed() {
|
||||
if !e.skip() {
|
||||
ce, err := e.encode()
|
||||
b.errorID(o.id, err)
|
||||
t.insert(e.runes[0], ce)
|
||||
}
|
||||
}
|
||||
o.handle = b.index.addTrie(t)
|
||||
}
|
||||
|
||||
func (b *Builder) build() (*table, error) {
|
||||
if b.built {
|
||||
return b.t, b.err
|
||||
}
|
||||
b.built = true
|
||||
b.t = &table{
|
||||
maxContractLen: utf8.UTFMax,
|
||||
variableTop: uint32(b.varTop),
|
||||
}
|
||||
|
||||
b.buildOrdering(&b.root)
|
||||
b.t.root = b.root.handle
|
||||
for _, t := range b.locale {
|
||||
b.buildOrdering(t.index)
|
||||
if b.err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
i, err := b.index.generate()
|
||||
b.t.index = *i
|
||||
b.error(err)
|
||||
return b.t, b.err
|
||||
}
|
||||
|
||||
// Build builds the root Collator.
|
||||
// TODO: return Weighter instead
|
||||
func (b *Builder) Build() (colltab.Weighter, error) {
|
||||
t, err := b.build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
table := colltab.Init(t)
|
||||
if table == nil {
|
||||
panic("generated table of incompatible type")
|
||||
}
|
||||
return table, nil
|
||||
}
|
||||
|
||||
// Build builds a Collator for Tailoring t.
|
||||
func (t *Tailoring) Build() (colltab.Weighter, error) {
|
||||
// TODO: implement.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Print prints the tables for b and all its Tailorings as a Go file
|
||||
// that can be included in the Collate package.
|
||||
func (b *Builder) Print(w io.Writer) (n int, err error) {
|
||||
p := func(nn int, e error) {
|
||||
n += nn
|
||||
if err == nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
t, err := b.build()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
p(fmt.Fprintf(w, `var availableLocales = "und`))
|
||||
for _, loc := range b.locale {
|
||||
if loc.id != "und" {
|
||||
p(fmt.Fprintf(w, ",%s", loc.id))
|
||||
}
|
||||
}
|
||||
p(fmt.Fprint(w, "\"\n\n"))
|
||||
p(fmt.Fprintf(w, "const varTop = 0x%x\n\n", b.varTop))
|
||||
p(fmt.Fprintln(w, "var locales = [...]tableIndex{"))
|
||||
for _, loc := range b.locale {
|
||||
if loc.id == "und" {
|
||||
p(t.fprintIndex(w, loc.index.handle, loc.id))
|
||||
}
|
||||
}
|
||||
for _, loc := range b.locale {
|
||||
if loc.id != "und" {
|
||||
p(t.fprintIndex(w, loc.index.handle, loc.id))
|
||||
}
|
||||
}
|
||||
p(fmt.Fprint(w, "}\n\n"))
|
||||
n, _, err = t.fprint(w, "main")
|
||||
return
|
||||
}
|
||||
|
||||
// reproducibleFromNFKD checks whether the given expansion could be generated
|
||||
// from an NFKD expansion.
|
||||
func reproducibleFromNFKD(e *entry, exp, nfkd []rawCE) bool {
|
||||
// Length must be equal.
|
||||
if len(exp) != len(nfkd) {
|
||||
return false
|
||||
}
|
||||
for i, ce := range exp {
|
||||
// Primary and secondary values should be equal.
|
||||
if ce.w[0] != nfkd[i].w[0] || ce.w[1] != nfkd[i].w[1] {
|
||||
return false
|
||||
}
|
||||
// Tertiary values should be equal to maxTertiary for third element onwards.
|
||||
// TODO: there seem to be a lot of cases in CLDR (e.g. ㏭ in zh.xml) that can
|
||||
// simply be dropped. Try this out by dropping the following code.
|
||||
if i >= 2 && ce.w[2] != maxTertiary {
|
||||
return false
|
||||
}
|
||||
if _, err := makeCE(ce); err != nil {
|
||||
// Simply return false. The error will be caught elsewhere.
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func simplify(o *ordering) {
|
||||
// Runes that are a starter of a contraction should not be removed.
|
||||
// (To date, there is only Kannada character 0CCA.)
|
||||
keep := make(map[rune]bool)
|
||||
for e := o.front(); e != nil; e, _ = e.nextIndexed() {
|
||||
if len(e.runes) > 1 {
|
||||
keep[e.runes[0]] = true
|
||||
}
|
||||
}
|
||||
// Tag entries for which the runes NFKD decompose to identical values.
|
||||
for e := o.front(); e != nil; e, _ = e.nextIndexed() {
|
||||
s := e.str
|
||||
nfkd := norm.NFKD.String(s)
|
||||
nfd := norm.NFD.String(s)
|
||||
if e.decompose || len(e.runes) > 1 || len(e.elems) == 1 || keep[e.runes[0]] || nfkd == nfd {
|
||||
continue
|
||||
}
|
||||
if reproducibleFromNFKD(e, e.elems, o.genColElems(nfkd)) {
|
||||
e.decompose = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// appendExpansion converts the given collation sequence to
|
||||
// collation elements and adds them to the expansion table.
|
||||
// It returns an index to the expansion table.
|
||||
func (b *Builder) appendExpansion(e *entry) int {
|
||||
t := b.t
|
||||
i := len(t.expandElem)
|
||||
ce := uint32(len(e.elems))
|
||||
t.expandElem = append(t.expandElem, ce)
|
||||
for _, w := range e.elems {
|
||||
ce, err := makeCE(w)
|
||||
if err != nil {
|
||||
b.error(err)
|
||||
return -1
|
||||
}
|
||||
t.expandElem = append(t.expandElem, ce)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// processExpansions extracts data necessary to generate
|
||||
// the extraction tables.
|
||||
func (b *Builder) processExpansions(o *ordering) {
|
||||
for e := o.front(); e != nil; e, _ = e.nextIndexed() {
|
||||
if !e.expansion() {
|
||||
continue
|
||||
}
|
||||
key := fmt.Sprintf("%v", e.elems)
|
||||
i, ok := b.expIndex[key]
|
||||
if !ok {
|
||||
i = b.appendExpansion(e)
|
||||
b.expIndex[key] = i
|
||||
}
|
||||
e.expansionIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) processContractions(o *ordering) {
|
||||
// Collate contractions per starter rune.
|
||||
starters := []rune{}
|
||||
cm := make(map[rune][]*entry)
|
||||
for e := o.front(); e != nil; e, _ = e.nextIndexed() {
|
||||
if e.contraction() {
|
||||
if len(e.str) > b.t.maxContractLen {
|
||||
b.t.maxContractLen = len(e.str)
|
||||
}
|
||||
r := e.runes[0]
|
||||
if _, ok := cm[r]; !ok {
|
||||
starters = append(starters, r)
|
||||
}
|
||||
cm[r] = append(cm[r], e)
|
||||
}
|
||||
}
|
||||
// Add entries of single runes that are at a start of a contraction.
|
||||
for e := o.front(); e != nil; e, _ = e.nextIndexed() {
|
||||
if !e.contraction() {
|
||||
r := e.runes[0]
|
||||
if _, ok := cm[r]; ok {
|
||||
cm[r] = append(cm[r], e)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Build the tries for the contractions.
|
||||
t := b.t
|
||||
for _, r := range starters {
|
||||
l := cm[r]
|
||||
// Compute suffix strings. There are 31 different contraction suffix
|
||||
// sets for 715 contractions and 82 contraction starter runes as of
|
||||
// version 6.0.0.
|
||||
sufx := []string{}
|
||||
hasSingle := false
|
||||
for _, e := range l {
|
||||
if len(e.runes) > 1 {
|
||||
sufx = append(sufx, string(e.runes[1:]))
|
||||
} else {
|
||||
hasSingle = true
|
||||
}
|
||||
}
|
||||
if !hasSingle {
|
||||
b.error(fmt.Errorf("no single entry for starter rune %U found", r))
|
||||
continue
|
||||
}
|
||||
// Unique the suffix set.
|
||||
sort.Strings(sufx)
|
||||
key := strings.Join(sufx, "\n")
|
||||
handle, ok := b.ctHandle[key]
|
||||
if !ok {
|
||||
var err error
|
||||
handle, err = t.contractTries.appendTrie(sufx)
|
||||
if err != nil {
|
||||
b.error(err)
|
||||
}
|
||||
b.ctHandle[key] = handle
|
||||
}
|
||||
// Bucket sort entries in index order.
|
||||
es := make([]*entry, len(l))
|
||||
for _, e := range l {
|
||||
var p, sn int
|
||||
if len(e.runes) > 1 {
|
||||
str := []byte(string(e.runes[1:]))
|
||||
p, sn = t.contractTries.lookup(handle, str)
|
||||
if sn != len(str) {
|
||||
log.Fatalf("%s: processContractions: unexpected length for '%X'; len=%d; want %d", o.id, e.runes, sn, len(str))
|
||||
}
|
||||
}
|
||||
if es[p] != nil {
|
||||
log.Fatalf("%s: multiple contractions for position %d for rune %U", o.id, p, e.runes[0])
|
||||
}
|
||||
es[p] = e
|
||||
}
|
||||
// Create collation elements for contractions.
|
||||
elems := []uint32{}
|
||||
for _, e := range es {
|
||||
ce, err := e.encodeBase()
|
||||
b.errorID(o.id, err)
|
||||
elems = append(elems, ce)
|
||||
}
|
||||
key = fmt.Sprintf("%v", elems)
|
||||
i, ok := b.ctElem[key]
|
||||
if !ok {
|
||||
i = len(t.contractElem)
|
||||
b.ctElem[key] = i
|
||||
t.contractElem = append(t.contractElem, elems...)
|
||||
}
|
||||
// Store info in entry for starter rune.
|
||||
es[0].contractionIndex = i
|
||||
es[0].contractionHandle = handle
|
||||
}
|
||||
}
|
||||
290
vendor/golang.org/x/text/collate/build/builder_test.go
generated
vendored
Normal file
290
vendor/golang.org/x/text/collate/build/builder_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
// Copyright 2012 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 build
|
||||
|
||||
import "testing"
|
||||
|
||||
// cjk returns an implicit collation element for a CJK rune.
|
||||
func cjk(r rune) []rawCE {
|
||||
// A CJK character C is represented in the DUCET as
|
||||
// [.AAAA.0020.0002.C][.BBBB.0000.0000.C]
|
||||
// Where AAAA is the most significant 15 bits plus a base value.
|
||||
// Any base value will work for the test, so we pick the common value of FB40.
|
||||
const base = 0xFB40
|
||||
return []rawCE{
|
||||
{w: []int{base + int(r>>15), defaultSecondary, defaultTertiary, int(r)}},
|
||||
{w: []int{int(r&0x7FFF) | 0x8000, 0, 0, int(r)}},
|
||||
}
|
||||
}
|
||||
|
||||
func pCE(p int) []rawCE {
|
||||
return mkCE([]int{p, defaultSecondary, defaultTertiary, 0}, 0)
|
||||
}
|
||||
|
||||
func pqCE(p, q int) []rawCE {
|
||||
return mkCE([]int{p, defaultSecondary, defaultTertiary, q}, 0)
|
||||
}
|
||||
|
||||
func ptCE(p, t int) []rawCE {
|
||||
return mkCE([]int{p, defaultSecondary, t, 0}, 0)
|
||||
}
|
||||
|
||||
func ptcCE(p, t int, ccc uint8) []rawCE {
|
||||
return mkCE([]int{p, defaultSecondary, t, 0}, ccc)
|
||||
}
|
||||
|
||||
func sCE(s int) []rawCE {
|
||||
return mkCE([]int{0, s, defaultTertiary, 0}, 0)
|
||||
}
|
||||
|
||||
func stCE(s, t int) []rawCE {
|
||||
return mkCE([]int{0, s, t, 0}, 0)
|
||||
}
|
||||
|
||||
func scCE(s int, ccc uint8) []rawCE {
|
||||
return mkCE([]int{0, s, defaultTertiary, 0}, ccc)
|
||||
}
|
||||
|
||||
func mkCE(w []int, ccc uint8) []rawCE {
|
||||
return []rawCE{rawCE{w, ccc}}
|
||||
}
|
||||
|
||||
// ducetElem is used to define test data that is used to generate a table.
|
||||
type ducetElem struct {
|
||||
str string
|
||||
ces []rawCE
|
||||
}
|
||||
|
||||
func newBuilder(t *testing.T, ducet []ducetElem) *Builder {
|
||||
b := NewBuilder()
|
||||
for _, e := range ducet {
|
||||
ces := [][]int{}
|
||||
for _, ce := range e.ces {
|
||||
ces = append(ces, ce.w)
|
||||
}
|
||||
if err := b.Add([]rune(e.str), ces, nil); err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
}
|
||||
b.t = &table{}
|
||||
b.root.sort()
|
||||
return b
|
||||
}
|
||||
|
||||
type convertTest struct {
|
||||
in, out []rawCE
|
||||
err bool
|
||||
}
|
||||
|
||||
var convLargeTests = []convertTest{
|
||||
{pCE(0xFB39), pCE(0xFB39), false},
|
||||
{cjk(0x2F9B2), pqCE(0x3F9B2, 0x2F9B2), false},
|
||||
{pCE(0xFB40), pCE(0), true},
|
||||
{append(pCE(0xFB40), pCE(0)[0]), pCE(0), true},
|
||||
{pCE(0xFFFE), pCE(illegalOffset), false},
|
||||
{pCE(0xFFFF), pCE(illegalOffset + 1), false},
|
||||
}
|
||||
|
||||
func TestConvertLarge(t *testing.T) {
|
||||
for i, tt := range convLargeTests {
|
||||
e := new(entry)
|
||||
for _, ce := range tt.in {
|
||||
e.elems = append(e.elems, makeRawCE(ce.w, ce.ccc))
|
||||
}
|
||||
elems, err := convertLargeWeights(e.elems)
|
||||
if tt.err {
|
||||
if err == nil {
|
||||
t.Errorf("%d: expected error; none found", i)
|
||||
}
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("%d: unexpected error: %v", i, err)
|
||||
}
|
||||
if !equalCEArrays(elems, tt.out) {
|
||||
t.Errorf("%d: conversion was %x; want %x", i, elems, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collation element table for simplify tests.
|
||||
var simplifyTest = []ducetElem{
|
||||
{"\u0300", sCE(30)}, // grave
|
||||
{"\u030C", sCE(40)}, // caron
|
||||
{"A", ptCE(100, 8)},
|
||||
{"D", ptCE(104, 8)},
|
||||
{"E", ptCE(105, 8)},
|
||||
{"I", ptCE(110, 8)},
|
||||
{"z", ptCE(130, 8)},
|
||||
{"\u05F2", append(ptCE(200, 4), ptCE(200, 4)[0])},
|
||||
{"\u05B7", sCE(80)},
|
||||
{"\u00C0", append(ptCE(100, 8), sCE(30)...)}, // A with grave, can be removed
|
||||
{"\u00C8", append(ptCE(105, 8), sCE(30)...)}, // E with grave
|
||||
{"\uFB1F", append(ptCE(200, 4), ptCE(200, 4)[0], sCE(80)[0])}, // eliminated by NFD
|
||||
{"\u00C8\u0302", ptCE(106, 8)}, // block previous from simplifying
|
||||
{"\u01C5", append(ptCE(104, 9), ptCE(130, 4)[0], stCE(40, maxTertiary)[0])}, // eliminated by NFKD
|
||||
// no removal: tertiary value of third element is not maxTertiary
|
||||
{"\u2162", append(ptCE(110, 9), ptCE(110, 4)[0], ptCE(110, 8)[0])},
|
||||
}
|
||||
|
||||
var genColTests = []ducetElem{
|
||||
{"\uFA70", pqCE(0x1FA70, 0xFA70)},
|
||||
{"A\u0300", append(ptCE(100, 8), sCE(30)...)},
|
||||
{"A\u0300\uFA70", append(ptCE(100, 8), sCE(30)[0], pqCE(0x1FA70, 0xFA70)[0])},
|
||||
{"A\u0300A\u0300", append(ptCE(100, 8), sCE(30)[0], ptCE(100, 8)[0], sCE(30)[0])},
|
||||
}
|
||||
|
||||
func TestGenColElems(t *testing.T) {
|
||||
b := newBuilder(t, simplifyTest[:5])
|
||||
|
||||
for i, tt := range genColTests {
|
||||
res := b.root.genColElems(tt.str)
|
||||
if !equalCEArrays(tt.ces, res) {
|
||||
t.Errorf("%d: result %X; want %X", i, res, tt.ces)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type strArray []string
|
||||
|
||||
func (sa strArray) contains(s string) bool {
|
||||
for _, e := range sa {
|
||||
if e == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var simplifyRemoved = strArray{"\u00C0", "\uFB1F"}
|
||||
var simplifyMarked = strArray{"\u01C5"}
|
||||
|
||||
func TestSimplify(t *testing.T) {
|
||||
b := newBuilder(t, simplifyTest)
|
||||
o := &b.root
|
||||
simplify(o)
|
||||
|
||||
for i, tt := range simplifyTest {
|
||||
if simplifyRemoved.contains(tt.str) {
|
||||
continue
|
||||
}
|
||||
e := o.find(tt.str)
|
||||
if e.str != tt.str || !equalCEArrays(e.elems, tt.ces) {
|
||||
t.Errorf("%d: found element %s -> %X; want %s -> %X", i, e.str, e.elems, tt.str, tt.ces)
|
||||
break
|
||||
}
|
||||
}
|
||||
var i, k int
|
||||
for e := o.front(); e != nil; e, _ = e.nextIndexed() {
|
||||
gold := simplifyMarked.contains(e.str)
|
||||
if gold {
|
||||
k++
|
||||
}
|
||||
if gold != e.decompose {
|
||||
t.Errorf("%d: %s has decompose %v; want %v", i, e.str, e.decompose, gold)
|
||||
}
|
||||
i++
|
||||
}
|
||||
if k != len(simplifyMarked) {
|
||||
t.Errorf(" an entry that should be marked as decompose was deleted")
|
||||
}
|
||||
}
|
||||
|
||||
var expandTest = []ducetElem{
|
||||
{"\u0300", append(scCE(29, 230), scCE(30, 230)...)},
|
||||
{"\u00C0", append(ptCE(100, 8), scCE(30, 230)...)},
|
||||
{"\u00C8", append(ptCE(105, 8), scCE(30, 230)...)},
|
||||
{"\u00C9", append(ptCE(105, 8), scCE(30, 230)...)}, // identical expansion
|
||||
{"\u05F2", append(ptCE(200, 4), ptCE(200, 4)[0], ptCE(200, 4)[0])},
|
||||
{"\u01FF", append(ptCE(200, 4), ptcCE(201, 4, 0)[0], scCE(30, 230)[0])},
|
||||
}
|
||||
|
||||
func TestExpand(t *testing.T) {
|
||||
const (
|
||||
totalExpansions = 5
|
||||
totalElements = 2 + 2 + 2 + 3 + 3 + totalExpansions
|
||||
)
|
||||
b := newBuilder(t, expandTest)
|
||||
o := &b.root
|
||||
b.processExpansions(o)
|
||||
|
||||
e := o.front()
|
||||
for _, tt := range expandTest {
|
||||
exp := b.t.expandElem[e.expansionIndex:]
|
||||
if int(exp[0]) != len(tt.ces) {
|
||||
t.Errorf("%U: len(expansion)==%d; want %d", []rune(tt.str)[0], exp[0], len(tt.ces))
|
||||
}
|
||||
exp = exp[1:]
|
||||
for j, w := range tt.ces {
|
||||
if ce, _ := makeCE(w); exp[j] != ce {
|
||||
t.Errorf("%U: element %d is %X; want %X", []rune(tt.str)[0], j, exp[j], ce)
|
||||
}
|
||||
}
|
||||
e, _ = e.nextIndexed()
|
||||
}
|
||||
// Verify uniquing.
|
||||
if len(b.t.expandElem) != totalElements {
|
||||
t.Errorf("len(expandElem)==%d; want %d", len(b.t.expandElem), totalElements)
|
||||
}
|
||||
}
|
||||
|
||||
var contractTest = []ducetElem{
|
||||
{"abc", pCE(102)},
|
||||
{"abd", pCE(103)},
|
||||
{"a", pCE(100)},
|
||||
{"ab", pCE(101)},
|
||||
{"ac", pCE(104)},
|
||||
{"bcd", pCE(202)},
|
||||
{"b", pCE(200)},
|
||||
{"bc", pCE(201)},
|
||||
{"bd", pCE(203)},
|
||||
// shares suffixes with a*
|
||||
{"Ab", pCE(301)},
|
||||
{"A", pCE(300)},
|
||||
{"Ac", pCE(304)},
|
||||
{"Abc", pCE(302)},
|
||||
{"Abd", pCE(303)},
|
||||
// starter to be ignored
|
||||
{"z", pCE(1000)},
|
||||
}
|
||||
|
||||
func TestContract(t *testing.T) {
|
||||
const (
|
||||
totalElements = 5 + 5 + 4
|
||||
)
|
||||
b := newBuilder(t, contractTest)
|
||||
o := &b.root
|
||||
b.processContractions(o)
|
||||
|
||||
indexMap := make(map[int]bool)
|
||||
handleMap := make(map[rune]*entry)
|
||||
for e := o.front(); e != nil; e, _ = e.nextIndexed() {
|
||||
if e.contractionHandle.n > 0 {
|
||||
handleMap[e.runes[0]] = e
|
||||
indexMap[e.contractionHandle.index] = true
|
||||
}
|
||||
}
|
||||
// Verify uniquing.
|
||||
if len(indexMap) != 2 {
|
||||
t.Errorf("number of tries is %d; want %d", len(indexMap), 2)
|
||||
}
|
||||
for _, tt := range contractTest {
|
||||
e, ok := handleMap[[]rune(tt.str)[0]]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
str := tt.str[1:]
|
||||
offset, n := b.t.contractTries.lookup(e.contractionHandle, []byte(str))
|
||||
if len(str) != n {
|
||||
t.Errorf("%s: bytes consumed==%d; want %d", tt.str, n, len(str))
|
||||
}
|
||||
ce := b.t.contractElem[offset+e.contractionIndex]
|
||||
if want, _ := makeCE(tt.ces[0]); want != ce {
|
||||
t.Errorf("%s: element %X; want %X", tt.str, ce, want)
|
||||
}
|
||||
}
|
||||
if len(b.t.contractElem) != totalElements {
|
||||
t.Errorf("len(expandElem)==%d; want %d", len(b.t.contractElem), totalElements)
|
||||
}
|
||||
}
|
||||
294
vendor/golang.org/x/text/collate/build/colelem.go
generated
vendored
Normal file
294
vendor/golang.org/x/text/collate/build/colelem.go
generated
vendored
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
// Copyright 2012 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 build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/collate/colltab"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSecondary = 0x20
|
||||
defaultTertiary = 0x2
|
||||
maxTertiary = 0x1F
|
||||
)
|
||||
|
||||
type rawCE struct {
|
||||
w []int
|
||||
ccc uint8
|
||||
}
|
||||
|
||||
func makeRawCE(w []int, ccc uint8) rawCE {
|
||||
ce := rawCE{w: make([]int, 4), ccc: ccc}
|
||||
copy(ce.w, w)
|
||||
return ce
|
||||
}
|
||||
|
||||
// A collation element is represented as an uint32.
|
||||
// In the typical case, a rune maps to a single collation element. If a rune
|
||||
// can be the start of a contraction or expands into multiple collation elements,
|
||||
// then the collation element that is associated with a rune will have a special
|
||||
// form to represent such m to n mappings. Such special collation elements
|
||||
// have a value >= 0x80000000.
|
||||
|
||||
const (
|
||||
maxPrimaryBits = 21
|
||||
maxSecondaryBits = 12
|
||||
maxTertiaryBits = 8
|
||||
)
|
||||
|
||||
func makeCE(ce rawCE) (uint32, error) {
|
||||
v, e := colltab.MakeElem(ce.w[0], ce.w[1], ce.w[2], ce.ccc)
|
||||
return uint32(v), e
|
||||
}
|
||||
|
||||
// For contractions, collation elements are of the form
|
||||
// 110bbbbb bbbbbbbb iiiiiiii iiiinnnn, where
|
||||
// - n* is the size of the first node in the contraction trie.
|
||||
// - i* is the index of the first node in the contraction trie.
|
||||
// - b* is the offset into the contraction collation element table.
|
||||
// See contract.go for details on the contraction trie.
|
||||
const (
|
||||
contractID = 0xC0000000
|
||||
maxNBits = 4
|
||||
maxTrieIndexBits = 12
|
||||
maxContractOffsetBits = 13
|
||||
)
|
||||
|
||||
func makeContractIndex(h ctHandle, offset int) (uint32, error) {
|
||||
if h.n >= 1<<maxNBits {
|
||||
return 0, fmt.Errorf("size of contraction trie node too large: %d >= %d", h.n, 1<<maxNBits)
|
||||
}
|
||||
if h.index >= 1<<maxTrieIndexBits {
|
||||
return 0, fmt.Errorf("size of contraction trie offset too large: %d >= %d", h.index, 1<<maxTrieIndexBits)
|
||||
}
|
||||
if offset >= 1<<maxContractOffsetBits {
|
||||
return 0, fmt.Errorf("contraction offset out of bounds: %x >= %x", offset, 1<<maxContractOffsetBits)
|
||||
}
|
||||
ce := uint32(contractID)
|
||||
ce += uint32(offset << (maxNBits + maxTrieIndexBits))
|
||||
ce += uint32(h.index << maxNBits)
|
||||
ce += uint32(h.n)
|
||||
return ce, nil
|
||||
}
|
||||
|
||||
// For expansions, collation elements are of the form
|
||||
// 11100000 00000000 bbbbbbbb bbbbbbbb,
|
||||
// where b* is the index into the expansion sequence table.
|
||||
const (
|
||||
expandID = 0xE0000000
|
||||
maxExpandIndexBits = 16
|
||||
)
|
||||
|
||||
func makeExpandIndex(index int) (uint32, error) {
|
||||
if index >= 1<<maxExpandIndexBits {
|
||||
return 0, fmt.Errorf("expansion index out of bounds: %x >= %x", index, 1<<maxExpandIndexBits)
|
||||
}
|
||||
return expandID + uint32(index), nil
|
||||
}
|
||||
|
||||
// Each list of collation elements corresponding to an expansion starts with
|
||||
// a header indicating the length of the sequence.
|
||||
func makeExpansionHeader(n int) (uint32, error) {
|
||||
return uint32(n), nil
|
||||
}
|
||||
|
||||
// Some runes can be expanded using NFKD decomposition. Instead of storing the full
|
||||
// sequence of collation elements, we decompose the rune and lookup the collation
|
||||
// elements for each rune in the decomposition and modify the tertiary weights.
|
||||
// The collation element, in this case, is of the form
|
||||
// 11110000 00000000 wwwwwwww vvvvvvvv, where
|
||||
// - v* is the replacement tertiary weight for the first rune,
|
||||
// - w* is the replacement tertiary weight for the second rune,
|
||||
// Tertiary weights of subsequent runes should be replaced with maxTertiary.
|
||||
// See http://www.unicode.org/reports/tr10/#Compatibility_Decompositions for more details.
|
||||
const (
|
||||
decompID = 0xF0000000
|
||||
)
|
||||
|
||||
func makeDecompose(t1, t2 int) (uint32, error) {
|
||||
if t1 >= 256 || t1 < 0 {
|
||||
return 0, fmt.Errorf("first tertiary weight out of bounds: %d >= 256", t1)
|
||||
}
|
||||
if t2 >= 256 || t2 < 0 {
|
||||
return 0, fmt.Errorf("second tertiary weight out of bounds: %d >= 256", t2)
|
||||
}
|
||||
return uint32(t2<<8+t1) + decompID, nil
|
||||
}
|
||||
|
||||
const (
|
||||
// These constants were taken from http://www.unicode.org/versions/Unicode6.0.0/ch12.pdf.
|
||||
minUnified rune = 0x4E00
|
||||
maxUnified = 0x9FFF
|
||||
minCompatibility = 0xF900
|
||||
maxCompatibility = 0xFAFF
|
||||
minRare = 0x3400
|
||||
maxRare = 0x4DBF
|
||||
)
|
||||
const (
|
||||
commonUnifiedOffset = 0x10000
|
||||
rareUnifiedOffset = 0x20000 // largest rune in common is U+FAFF
|
||||
otherOffset = 0x50000 // largest rune in rare is U+2FA1D
|
||||
illegalOffset = otherOffset + int(unicode.MaxRune)
|
||||
maxPrimary = illegalOffset + 1
|
||||
)
|
||||
|
||||
// implicitPrimary returns the primary weight for the a rune
|
||||
// for which there is no entry for the rune in the collation table.
|
||||
// We take a different approach from the one specified in
|
||||
// http://unicode.org/reports/tr10/#Implicit_Weights,
|
||||
// but preserve the resulting relative ordering of the runes.
|
||||
func implicitPrimary(r rune) int {
|
||||
if unicode.Is(unicode.Ideographic, r) {
|
||||
if r >= minUnified && r <= maxUnified {
|
||||
// The most common case for CJK.
|
||||
return int(r) + commonUnifiedOffset
|
||||
}
|
||||
if r >= minCompatibility && r <= maxCompatibility {
|
||||
// This will typically not hit. The DUCET explicitly specifies mappings
|
||||
// for all characters that do not decompose.
|
||||
return int(r) + commonUnifiedOffset
|
||||
}
|
||||
return int(r) + rareUnifiedOffset
|
||||
}
|
||||
return int(r) + otherOffset
|
||||
}
|
||||
|
||||
// convertLargeWeights converts collation elements with large
|
||||
// primaries (either double primaries or for illegal runes)
|
||||
// to our own representation.
|
||||
// A CJK character C is represented in the DUCET as
|
||||
// [.FBxx.0020.0002.C][.BBBB.0000.0000.C]
|
||||
// We will rewrite these characters to a single CE.
|
||||
// We assume the CJK values start at 0x8000.
|
||||
// See http://unicode.org/reports/tr10/#Implicit_Weights
|
||||
func convertLargeWeights(elems []rawCE) (res []rawCE, err error) {
|
||||
const (
|
||||
cjkPrimaryStart = 0xFB40
|
||||
rarePrimaryStart = 0xFB80
|
||||
otherPrimaryStart = 0xFBC0
|
||||
illegalPrimary = 0xFFFE
|
||||
highBitsMask = 0x3F
|
||||
lowBitsMask = 0x7FFF
|
||||
lowBitsFlag = 0x8000
|
||||
shiftBits = 15
|
||||
)
|
||||
for i := 0; i < len(elems); i++ {
|
||||
ce := elems[i].w
|
||||
p := ce[0]
|
||||
if p < cjkPrimaryStart {
|
||||
continue
|
||||
}
|
||||
if p > 0xFFFF {
|
||||
return elems, fmt.Errorf("found primary weight %X; should be <= 0xFFFF", p)
|
||||
}
|
||||
if p >= illegalPrimary {
|
||||
ce[0] = illegalOffset + p - illegalPrimary
|
||||
} else {
|
||||
if i+1 >= len(elems) {
|
||||
return elems, fmt.Errorf("second part of double primary weight missing: %v", elems)
|
||||
}
|
||||
if elems[i+1].w[0]&lowBitsFlag == 0 {
|
||||
return elems, fmt.Errorf("malformed second part of double primary weight: %v", elems)
|
||||
}
|
||||
np := ((p & highBitsMask) << shiftBits) + elems[i+1].w[0]&lowBitsMask
|
||||
switch {
|
||||
case p < rarePrimaryStart:
|
||||
np += commonUnifiedOffset
|
||||
case p < otherPrimaryStart:
|
||||
np += rareUnifiedOffset
|
||||
default:
|
||||
p += otherOffset
|
||||
}
|
||||
ce[0] = np
|
||||
for j := i + 1; j+1 < len(elems); j++ {
|
||||
elems[j] = elems[j+1]
|
||||
}
|
||||
elems = elems[:len(elems)-1]
|
||||
}
|
||||
}
|
||||
return elems, nil
|
||||
}
|
||||
|
||||
// nextWeight computes the first possible collation weights following elems
|
||||
// for the given level.
|
||||
func nextWeight(level colltab.Level, elems []rawCE) []rawCE {
|
||||
if level == colltab.Identity {
|
||||
next := make([]rawCE, len(elems))
|
||||
copy(next, elems)
|
||||
return next
|
||||
}
|
||||
next := []rawCE{makeRawCE(elems[0].w, elems[0].ccc)}
|
||||
next[0].w[level]++
|
||||
if level < colltab.Secondary {
|
||||
next[0].w[colltab.Secondary] = defaultSecondary
|
||||
}
|
||||
if level < colltab.Tertiary {
|
||||
next[0].w[colltab.Tertiary] = defaultTertiary
|
||||
}
|
||||
// Filter entries that cannot influence ordering.
|
||||
for _, ce := range elems[1:] {
|
||||
skip := true
|
||||
for i := colltab.Primary; i < level; i++ {
|
||||
skip = skip && ce.w[i] == 0
|
||||
}
|
||||
if !skip {
|
||||
next = append(next, ce)
|
||||
}
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
func nextVal(elems []rawCE, i int, level colltab.Level) (index, value int) {
|
||||
for ; i < len(elems) && elems[i].w[level] == 0; i++ {
|
||||
}
|
||||
if i < len(elems) {
|
||||
return i, elems[i].w[level]
|
||||
}
|
||||
return i, 0
|
||||
}
|
||||
|
||||
// compareWeights returns -1 if a < b, 1 if a > b, or 0 otherwise.
|
||||
// It also returns the collation level at which the difference is found.
|
||||
func compareWeights(a, b []rawCE) (result int, level colltab.Level) {
|
||||
for level := colltab.Primary; level < colltab.Identity; level++ {
|
||||
var va, vb int
|
||||
for ia, ib := 0, 0; ia < len(a) || ib < len(b); ia, ib = ia+1, ib+1 {
|
||||
ia, va = nextVal(a, ia, level)
|
||||
ib, vb = nextVal(b, ib, level)
|
||||
if va != vb {
|
||||
if va < vb {
|
||||
return -1, level
|
||||
} else {
|
||||
return 1, level
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, colltab.Identity
|
||||
}
|
||||
|
||||
func equalCE(a, b rawCE) bool {
|
||||
for i := 0; i < 3; i++ {
|
||||
if b.w[i] != a.w[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func equalCEArrays(a, b []rawCE) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if !equalCE(a[i], b[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
215
vendor/golang.org/x/text/collate/build/colelem_test.go
generated
vendored
Normal file
215
vendor/golang.org/x/text/collate/build/colelem_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
// Copyright 2012 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 build
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/collate/colltab"
|
||||
)
|
||||
|
||||
type ceTest struct {
|
||||
f func(in []int) (uint32, error)
|
||||
arg []int
|
||||
val uint32
|
||||
}
|
||||
|
||||
func normalCE(in []int) (ce uint32, err error) {
|
||||
return makeCE(rawCE{w: in[:3], ccc: uint8(in[3])})
|
||||
}
|
||||
|
||||
func expandCE(in []int) (ce uint32, err error) {
|
||||
return makeExpandIndex(in[0])
|
||||
}
|
||||
|
||||
func contractCE(in []int) (ce uint32, err error) {
|
||||
return makeContractIndex(ctHandle{in[0], in[1]}, in[2])
|
||||
}
|
||||
|
||||
func decompCE(in []int) (ce uint32, err error) {
|
||||
return makeDecompose(in[0], in[1])
|
||||
}
|
||||
|
||||
var ceTests = []ceTest{
|
||||
{normalCE, []int{0, 0, 0, 0}, 0xA0000000},
|
||||
{normalCE, []int{0, 0x28, 3, 0}, 0xA0002803},
|
||||
{normalCE, []int{0, 0x28, 3, 0xFF}, 0xAFF02803},
|
||||
{normalCE, []int{100, defaultSecondary, 3, 0}, 0x0000C883},
|
||||
// non-ignorable primary with non-default secondary
|
||||
{normalCE, []int{100, 0x28, defaultTertiary, 0}, 0x4000C828},
|
||||
{normalCE, []int{100, defaultSecondary + 8, 3, 0}, 0x0000C983},
|
||||
{normalCE, []int{100, 0, 3, 0}, 0xFFFF}, // non-ignorable primary with non-supported secondary
|
||||
{normalCE, []int{100, 1, 3, 0}, 0xFFFF},
|
||||
{normalCE, []int{1 << maxPrimaryBits, defaultSecondary, 0, 0}, 0xFFFF},
|
||||
{normalCE, []int{0, 1 << maxSecondaryBits, 0, 0}, 0xFFFF},
|
||||
{normalCE, []int{100, defaultSecondary, 1 << maxTertiaryBits, 0}, 0xFFFF},
|
||||
{normalCE, []int{0x123, defaultSecondary, 8, 0xFF}, 0x88FF0123},
|
||||
{normalCE, []int{0x123, defaultSecondary + 1, 8, 0xFF}, 0xFFFF},
|
||||
|
||||
{contractCE, []int{0, 0, 0}, 0xC0000000},
|
||||
{contractCE, []int{1, 1, 1}, 0xC0010011},
|
||||
{contractCE, []int{1, (1 << maxNBits) - 1, 1}, 0xC001001F},
|
||||
{contractCE, []int{(1 << maxTrieIndexBits) - 1, 1, 1}, 0xC001FFF1},
|
||||
{contractCE, []int{1, 1, (1 << maxContractOffsetBits) - 1}, 0xDFFF0011},
|
||||
{contractCE, []int{1, (1 << maxNBits), 1}, 0xFFFF},
|
||||
{contractCE, []int{(1 << maxTrieIndexBits), 1, 1}, 0xFFFF},
|
||||
{contractCE, []int{1, (1 << maxContractOffsetBits), 1}, 0xFFFF},
|
||||
|
||||
{expandCE, []int{0}, 0xE0000000},
|
||||
{expandCE, []int{5}, 0xE0000005},
|
||||
{expandCE, []int{(1 << maxExpandIndexBits) - 1}, 0xE000FFFF},
|
||||
{expandCE, []int{1 << maxExpandIndexBits}, 0xFFFF},
|
||||
|
||||
{decompCE, []int{0, 0}, 0xF0000000},
|
||||
{decompCE, []int{1, 1}, 0xF0000101},
|
||||
{decompCE, []int{0x1F, 0x1F}, 0xF0001F1F},
|
||||
{decompCE, []int{256, 0x1F}, 0xFFFF},
|
||||
{decompCE, []int{0x1F, 256}, 0xFFFF},
|
||||
}
|
||||
|
||||
func TestColElem(t *testing.T) {
|
||||
for i, tt := range ceTests {
|
||||
in := make([]int, len(tt.arg))
|
||||
copy(in, tt.arg)
|
||||
ce, err := tt.f(in)
|
||||
if tt.val == 0xFFFF {
|
||||
if err == nil {
|
||||
t.Errorf("%d: expected error for args %x", i, tt.arg)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("%d: unexpected error: %v", i, err.Error())
|
||||
}
|
||||
if ce != tt.val {
|
||||
t.Errorf("%d: colElem=%X; want %X", i, ce, tt.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mkRawCES(in [][]int) []rawCE {
|
||||
out := []rawCE{}
|
||||
for _, w := range in {
|
||||
out = append(out, rawCE{w: w})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type weightsTest struct {
|
||||
a, b [][]int
|
||||
level colltab.Level
|
||||
result int
|
||||
}
|
||||
|
||||
var nextWeightTests = []weightsTest{
|
||||
{
|
||||
a: [][]int{{100, 20, 5, 0}},
|
||||
b: [][]int{{101, defaultSecondary, defaultTertiary, 0}},
|
||||
level: colltab.Primary,
|
||||
},
|
||||
{
|
||||
a: [][]int{{100, 20, 5, 0}},
|
||||
b: [][]int{{100, 21, defaultTertiary, 0}},
|
||||
level: colltab.Secondary,
|
||||
},
|
||||
{
|
||||
a: [][]int{{100, 20, 5, 0}},
|
||||
b: [][]int{{100, 20, 6, 0}},
|
||||
level: colltab.Tertiary,
|
||||
},
|
||||
{
|
||||
a: [][]int{{100, 20, 5, 0}},
|
||||
b: [][]int{{100, 20, 5, 0}},
|
||||
level: colltab.Identity,
|
||||
},
|
||||
}
|
||||
|
||||
var extra = [][]int{{200, 32, 8, 0}, {0, 32, 8, 0}, {0, 0, 8, 0}, {0, 0, 0, 0}}
|
||||
|
||||
func TestNextWeight(t *testing.T) {
|
||||
for i, tt := range nextWeightTests {
|
||||
test := func(l colltab.Level, tt weightsTest, a, gold [][]int) {
|
||||
res := nextWeight(tt.level, mkRawCES(a))
|
||||
if !equalCEArrays(mkRawCES(gold), res) {
|
||||
t.Errorf("%d:%d: expected weights %d; found %d", i, l, gold, res)
|
||||
}
|
||||
}
|
||||
test(-1, tt, tt.a, tt.b)
|
||||
for l := colltab.Primary; l <= colltab.Tertiary; l++ {
|
||||
if tt.level <= l {
|
||||
test(l, tt, append(tt.a, extra[l]), tt.b)
|
||||
} else {
|
||||
test(l, tt, append(tt.a, extra[l]), append(tt.b, extra[l]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var compareTests = []weightsTest{
|
||||
{
|
||||
[][]int{{100, 20, 5, 0}},
|
||||
[][]int{{100, 20, 5, 0}},
|
||||
colltab.Identity,
|
||||
0,
|
||||
},
|
||||
{
|
||||
[][]int{{100, 20, 5, 0}, extra[0]},
|
||||
[][]int{{100, 20, 5, 1}},
|
||||
colltab.Primary,
|
||||
1,
|
||||
},
|
||||
{
|
||||
[][]int{{100, 20, 5, 0}},
|
||||
[][]int{{101, 20, 5, 0}},
|
||||
colltab.Primary,
|
||||
-1,
|
||||
},
|
||||
{
|
||||
[][]int{{101, 20, 5, 0}},
|
||||
[][]int{{100, 20, 5, 0}},
|
||||
colltab.Primary,
|
||||
1,
|
||||
},
|
||||
{
|
||||
[][]int{{100, 0, 0, 0}, {0, 20, 5, 0}},
|
||||
[][]int{{0, 20, 5, 0}, {100, 0, 0, 0}},
|
||||
colltab.Identity,
|
||||
0,
|
||||
},
|
||||
{
|
||||
[][]int{{100, 20, 5, 0}},
|
||||
[][]int{{100, 21, 5, 0}},
|
||||
colltab.Secondary,
|
||||
-1,
|
||||
},
|
||||
{
|
||||
[][]int{{100, 20, 5, 0}},
|
||||
[][]int{{100, 20, 2, 0}},
|
||||
colltab.Tertiary,
|
||||
1,
|
||||
},
|
||||
{
|
||||
[][]int{{100, 20, 5, 1}},
|
||||
[][]int{{100, 20, 5, 2}},
|
||||
colltab.Quaternary,
|
||||
-1,
|
||||
},
|
||||
}
|
||||
|
||||
func TestCompareWeights(t *testing.T) {
|
||||
for i, tt := range compareTests {
|
||||
test := func(tt weightsTest, a, b [][]int) {
|
||||
res, level := compareWeights(mkRawCES(a), mkRawCES(b))
|
||||
if res != tt.result {
|
||||
t.Errorf("%d: expected comparisson result %d; found %d", i, tt.result, res)
|
||||
}
|
||||
if level != tt.level {
|
||||
t.Errorf("%d: expected level %d; found %d", i, tt.level, level)
|
||||
}
|
||||
}
|
||||
test(tt, tt.a, tt.b)
|
||||
test(tt, append(tt.a, extra[0]), append(tt.b, extra[0]))
|
||||
}
|
||||
}
|
||||
307
vendor/golang.org/x/text/collate/build/contract.go
generated
vendored
Normal file
307
vendor/golang.org/x/text/collate/build/contract.go
generated
vendored
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
// Copyright 2012 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 build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// This file contains code for detecting contractions and generating
|
||||
// the necessary tables.
|
||||
// Any Unicode Collation Algorithm (UCA) table entry that has more than
|
||||
// one rune one the left-hand side is called a contraction.
|
||||
// See http://www.unicode.org/reports/tr10/#Contractions for more details.
|
||||
//
|
||||
// We define the following terms:
|
||||
// initial: a rune that appears as the first rune in a contraction.
|
||||
// suffix: a sequence of runes succeeding the initial rune
|
||||
// in a given contraction.
|
||||
// non-initial: a rune that appears in a suffix.
|
||||
//
|
||||
// A rune may be both a initial and a non-initial and may be so in
|
||||
// many contractions. An initial may typically also appear by itself.
|
||||
// In case of ambiguities, the UCA requires we match the longest
|
||||
// contraction.
|
||||
//
|
||||
// Many contraction rules share the same set of possible suffixes.
|
||||
// We store sets of suffixes in a trie that associates an index with
|
||||
// each suffix in the set. This index can be used to look up a
|
||||
// collation element associated with the (starter rune, suffix) pair.
|
||||
//
|
||||
// The trie is defined on a UTF-8 byte sequence.
|
||||
// The overall trie is represented as an array of ctEntries. Each node of the trie
|
||||
// is represented as a subsequence of ctEntries, where each entry corresponds to
|
||||
// a possible match of a next character in the search string. An entry
|
||||
// also includes the length and offset to the next sequence of entries
|
||||
// to check in case of a match.
|
||||
|
||||
const (
|
||||
final = 0
|
||||
noIndex = 0xFF
|
||||
)
|
||||
|
||||
// ctEntry associates to a matching byte an offset and/or next sequence of
|
||||
// bytes to check. A ctEntry c is called final if a match means that the
|
||||
// longest suffix has been found. An entry c is final if c.n == 0.
|
||||
// A single final entry can match a range of characters to an offset.
|
||||
// A non-final entry always matches a single byte. Note that a non-final
|
||||
// entry might still resemble a completed suffix.
|
||||
// Examples:
|
||||
// The suffix strings "ab" and "ac" can be represented as:
|
||||
// []ctEntry{
|
||||
// {'a', 1, 1, noIndex}, // 'a' by itself does not match, so i is 0xFF.
|
||||
// {'b', 'c', 0, 1}, // "ab" -> 1, "ac" -> 2
|
||||
// }
|
||||
//
|
||||
// The suffix strings "ab", "abc", "abd", and "abcd" can be represented as:
|
||||
// []ctEntry{
|
||||
// {'a', 1, 1, noIndex}, // 'a' must be followed by 'b'.
|
||||
// {'b', 1, 2, 1}, // "ab" -> 1, may be followed by 'c' or 'd'.
|
||||
// {'d', 'd', final, 3}, // "abd" -> 3
|
||||
// {'c', 4, 1, 2}, // "abc" -> 2, may be followed by 'd'.
|
||||
// {'d', 'd', final, 4}, // "abcd" -> 4
|
||||
// }
|
||||
// See genStateTests in contract_test.go for more examples.
|
||||
type ctEntry struct {
|
||||
l uint8 // non-final: byte value to match; final: lowest match in range.
|
||||
h uint8 // non-final: relative index to next block; final: highest match in range.
|
||||
n uint8 // non-final: length of next block; final: final
|
||||
i uint8 // result offset. Will be noIndex if more bytes are needed to complete.
|
||||
}
|
||||
|
||||
// contractTrieSet holds a set of contraction tries. The tries are stored
|
||||
// consecutively in the entry field.
|
||||
type contractTrieSet []struct{ l, h, n, i uint8 }
|
||||
|
||||
// ctHandle is used to identify a trie in the trie set, consisting in an offset
|
||||
// in the array and the size of the first node.
|
||||
type ctHandle struct {
|
||||
index, n int
|
||||
}
|
||||
|
||||
// appendTrie adds a new trie for the given suffixes to the trie set and returns
|
||||
// a handle to it. The handle will be invalid on error.
|
||||
func (ct *contractTrieSet) appendTrie(suffixes []string) (ctHandle, error) {
|
||||
es := make([]stridx, len(suffixes))
|
||||
for i, s := range suffixes {
|
||||
es[i].str = s
|
||||
}
|
||||
sort.Sort(offsetSort(es))
|
||||
for i := range es {
|
||||
es[i].index = i + 1
|
||||
}
|
||||
sort.Sort(genidxSort(es))
|
||||
i := len(*ct)
|
||||
n, err := ct.genStates(es)
|
||||
if err != nil {
|
||||
*ct = (*ct)[:i]
|
||||
return ctHandle{}, err
|
||||
}
|
||||
return ctHandle{i, n}, nil
|
||||
}
|
||||
|
||||
// genStates generates ctEntries for a given suffix set and returns
|
||||
// the number of entries for the first node.
|
||||
func (ct *contractTrieSet) genStates(sis []stridx) (int, error) {
|
||||
if len(sis) == 0 {
|
||||
return 0, fmt.Errorf("genStates: list of suffices must be non-empty")
|
||||
}
|
||||
start := len(*ct)
|
||||
// create entries for differing first bytes.
|
||||
for _, si := range sis {
|
||||
s := si.str
|
||||
if len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
added := false
|
||||
c := s[0]
|
||||
if len(s) > 1 {
|
||||
for j := len(*ct) - 1; j >= start; j-- {
|
||||
if (*ct)[j].l == c {
|
||||
added = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !added {
|
||||
*ct = append(*ct, ctEntry{l: c, i: noIndex})
|
||||
}
|
||||
} else {
|
||||
for j := len(*ct) - 1; j >= start; j-- {
|
||||
// Update the offset for longer suffixes with the same byte.
|
||||
if (*ct)[j].l == c {
|
||||
(*ct)[j].i = uint8(si.index)
|
||||
added = true
|
||||
}
|
||||
// Extend range of final ctEntry, if possible.
|
||||
if (*ct)[j].h+1 == c {
|
||||
(*ct)[j].h = c
|
||||
added = true
|
||||
}
|
||||
}
|
||||
if !added {
|
||||
*ct = append(*ct, ctEntry{l: c, h: c, n: final, i: uint8(si.index)})
|
||||
}
|
||||
}
|
||||
}
|
||||
n := len(*ct) - start
|
||||
// Append nodes for the remainder of the suffixes for each ctEntry.
|
||||
sp := 0
|
||||
for i, end := start, len(*ct); i < end; i++ {
|
||||
fe := (*ct)[i]
|
||||
if fe.h == 0 { // uninitialized non-final
|
||||
ln := len(*ct) - start - n
|
||||
if ln > 0xFF {
|
||||
return 0, fmt.Errorf("genStates: relative block offset too large: %d > 255", ln)
|
||||
}
|
||||
fe.h = uint8(ln)
|
||||
// Find first non-final strings with same byte as current entry.
|
||||
for ; sis[sp].str[0] != fe.l; sp++ {
|
||||
}
|
||||
se := sp + 1
|
||||
for ; se < len(sis) && len(sis[se].str) > 1 && sis[se].str[0] == fe.l; se++ {
|
||||
}
|
||||
sl := sis[sp:se]
|
||||
sp = se
|
||||
for i, si := range sl {
|
||||
sl[i].str = si.str[1:]
|
||||
}
|
||||
nn, err := ct.genStates(sl)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
fe.n = uint8(nn)
|
||||
(*ct)[i] = fe
|
||||
}
|
||||
}
|
||||
sort.Sort(entrySort((*ct)[start : start+n]))
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// There may be both a final and non-final entry for a byte if the byte
|
||||
// is implied in a range of matches in the final entry.
|
||||
// We need to ensure that the non-final entry comes first in that case.
|
||||
type entrySort contractTrieSet
|
||||
|
||||
func (fe entrySort) Len() int { return len(fe) }
|
||||
func (fe entrySort) Swap(i, j int) { fe[i], fe[j] = fe[j], fe[i] }
|
||||
func (fe entrySort) Less(i, j int) bool {
|
||||
return fe[i].l > fe[j].l
|
||||
}
|
||||
|
||||
// stridx is used for sorting suffixes and their associated offsets.
|
||||
type stridx struct {
|
||||
str string
|
||||
index int
|
||||
}
|
||||
|
||||
// For computing the offsets, we first sort by size, and then by string.
|
||||
// This ensures that strings that only differ in the last byte by 1
|
||||
// are sorted consecutively in increasing order such that they can
|
||||
// be packed as a range in a final ctEntry.
|
||||
type offsetSort []stridx
|
||||
|
||||
func (si offsetSort) Len() int { return len(si) }
|
||||
func (si offsetSort) Swap(i, j int) { si[i], si[j] = si[j], si[i] }
|
||||
func (si offsetSort) Less(i, j int) bool {
|
||||
if len(si[i].str) != len(si[j].str) {
|
||||
return len(si[i].str) > len(si[j].str)
|
||||
}
|
||||
return si[i].str < si[j].str
|
||||
}
|
||||
|
||||
// For indexing, we want to ensure that strings are sorted in string order, where
|
||||
// for strings with the same prefix, we put longer strings before shorter ones.
|
||||
type genidxSort []stridx
|
||||
|
||||
func (si genidxSort) Len() int { return len(si) }
|
||||
func (si genidxSort) Swap(i, j int) { si[i], si[j] = si[j], si[i] }
|
||||
func (si genidxSort) Less(i, j int) bool {
|
||||
if strings.HasPrefix(si[j].str, si[i].str) {
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(si[i].str, si[j].str) {
|
||||
return true
|
||||
}
|
||||
return si[i].str < si[j].str
|
||||
}
|
||||
|
||||
// lookup matches the longest suffix in str and returns the associated offset
|
||||
// and the number of bytes consumed.
|
||||
func (ct *contractTrieSet) lookup(h ctHandle, str []byte) (index, ns int) {
|
||||
states := (*ct)[h.index:]
|
||||
p := 0
|
||||
n := h.n
|
||||
for i := 0; i < n && p < len(str); {
|
||||
e := states[i]
|
||||
c := str[p]
|
||||
if c >= e.l {
|
||||
if e.l == c {
|
||||
p++
|
||||
if e.i != noIndex {
|
||||
index, ns = int(e.i), p
|
||||
}
|
||||
if e.n != final {
|
||||
// set to new state
|
||||
i, states, n = 0, states[int(e.h)+n:], int(e.n)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
continue
|
||||
} else if e.n == final && c <= e.h {
|
||||
p++
|
||||
return int(c-e.l) + int(e.i), p
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// print writes the contractTrieSet t as compilable Go code to w. It returns
|
||||
// the total number of bytes written and the size of the resulting data structure in bytes.
|
||||
func (t *contractTrieSet) print(w io.Writer, name string) (n, size int, err error) {
|
||||
update3 := func(nn, sz int, e error) {
|
||||
n += nn
|
||||
if err == nil {
|
||||
err = e
|
||||
}
|
||||
size += sz
|
||||
}
|
||||
update2 := func(nn int, e error) { update3(nn, 0, e) }
|
||||
|
||||
update3(t.printArray(w, name))
|
||||
update2(fmt.Fprintf(w, "var %sContractTrieSet = ", name))
|
||||
update3(t.printStruct(w, name))
|
||||
update2(fmt.Fprintln(w))
|
||||
return
|
||||
}
|
||||
|
||||
func (ct contractTrieSet) printArray(w io.Writer, name string) (n, size int, err error) {
|
||||
p := func(f string, a ...interface{}) {
|
||||
nn, e := fmt.Fprintf(w, f, a...)
|
||||
n += nn
|
||||
if err == nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
size = len(ct) * 4
|
||||
p("// %sCTEntries: %d entries, %d bytes\n", name, len(ct), size)
|
||||
p("var %sCTEntries = [%d]struct{l,h,n,i uint8}{\n", name, len(ct))
|
||||
for _, fe := range ct {
|
||||
p("\t{0x%X, 0x%X, %d, %d},\n", fe.l, fe.h, fe.n, fe.i)
|
||||
}
|
||||
p("}\n")
|
||||
return
|
||||
}
|
||||
|
||||
func (ct contractTrieSet) printStruct(w io.Writer, name string) (n, size int, err error) {
|
||||
n, err = fmt.Fprintf(w, "contractTrieSet( %sCTEntries[:] )", name)
|
||||
size = int(reflect.TypeOf(ct).Size())
|
||||
return
|
||||
}
|
||||
264
vendor/golang.org/x/text/collate/build/contract_test.go
generated
vendored
Normal file
264
vendor/golang.org/x/text/collate/build/contract_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
// Copyright 2012 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 build
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var largetosmall = []stridx{
|
||||
{"a", 5},
|
||||
{"ab", 4},
|
||||
{"abc", 3},
|
||||
{"abcd", 2},
|
||||
{"abcde", 1},
|
||||
{"abcdef", 0},
|
||||
}
|
||||
|
||||
var offsetSortTests = [][]stridx{
|
||||
{
|
||||
{"bcde", 1},
|
||||
{"bc", 5},
|
||||
{"ab", 4},
|
||||
{"bcd", 3},
|
||||
{"abcd", 0},
|
||||
{"abc", 2},
|
||||
},
|
||||
largetosmall,
|
||||
}
|
||||
|
||||
func TestOffsetSort(t *testing.T) {
|
||||
for i, st := range offsetSortTests {
|
||||
sort.Sort(offsetSort(st))
|
||||
for j, si := range st {
|
||||
if j != si.index {
|
||||
t.Errorf("%d: failed: %v", i, st)
|
||||
}
|
||||
}
|
||||
}
|
||||
for i, tt := range genStateTests {
|
||||
// ensure input is well-formed
|
||||
sort.Sort(offsetSort(tt.in))
|
||||
for j, si := range tt.in {
|
||||
if si.index != j+1 {
|
||||
t.Errorf("%dth sort failed: %v", i, tt.in)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var genidxtest1 = []stridx{
|
||||
{"bcde", 3},
|
||||
{"bc", 6},
|
||||
{"ab", 2},
|
||||
{"bcd", 5},
|
||||
{"abcd", 0},
|
||||
{"abc", 1},
|
||||
{"bcdf", 4},
|
||||
}
|
||||
|
||||
var genidxSortTests = [][]stridx{
|
||||
genidxtest1,
|
||||
largetosmall,
|
||||
}
|
||||
|
||||
func TestGenIdxSort(t *testing.T) {
|
||||
for i, st := range genidxSortTests {
|
||||
sort.Sort(genidxSort(st))
|
||||
for j, si := range st {
|
||||
if j != si.index {
|
||||
t.Errorf("%dth sort failed %v", i, st)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var entrySortTests = []contractTrieSet{
|
||||
{
|
||||
{10, 0, 1, 3},
|
||||
{99, 0, 1, 0},
|
||||
{20, 50, 0, 2},
|
||||
{30, 0, 1, 1},
|
||||
},
|
||||
}
|
||||
|
||||
func TestEntrySort(t *testing.T) {
|
||||
for i, et := range entrySortTests {
|
||||
sort.Sort(entrySort(et))
|
||||
for j, fe := range et {
|
||||
if j != int(fe.i) {
|
||||
t.Errorf("%dth sort failed %v", i, et)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type GenStateTest struct {
|
||||
in []stridx
|
||||
firstBlockLen int
|
||||
out contractTrieSet
|
||||
}
|
||||
|
||||
var genStateTests = []GenStateTest{
|
||||
{[]stridx{
|
||||
{"abc", 1},
|
||||
},
|
||||
1,
|
||||
contractTrieSet{
|
||||
{'a', 0, 1, noIndex},
|
||||
{'b', 0, 1, noIndex},
|
||||
{'c', 'c', final, 1},
|
||||
},
|
||||
},
|
||||
{[]stridx{
|
||||
{"abc", 1},
|
||||
{"abd", 2},
|
||||
{"abe", 3},
|
||||
},
|
||||
1,
|
||||
contractTrieSet{
|
||||
{'a', 0, 1, noIndex},
|
||||
{'b', 0, 1, noIndex},
|
||||
{'c', 'e', final, 1},
|
||||
},
|
||||
},
|
||||
{[]stridx{
|
||||
{"abc", 1},
|
||||
{"ab", 2},
|
||||
{"a", 3},
|
||||
},
|
||||
1,
|
||||
contractTrieSet{
|
||||
{'a', 0, 1, 3},
|
||||
{'b', 0, 1, 2},
|
||||
{'c', 'c', final, 1},
|
||||
},
|
||||
},
|
||||
{[]stridx{
|
||||
{"abc", 1},
|
||||
{"abd", 2},
|
||||
{"ab", 3},
|
||||
{"ac", 4},
|
||||
{"a", 5},
|
||||
{"b", 6},
|
||||
},
|
||||
2,
|
||||
contractTrieSet{
|
||||
{'b', 'b', final, 6},
|
||||
{'a', 0, 2, 5},
|
||||
{'c', 'c', final, 4},
|
||||
{'b', 0, 1, 3},
|
||||
{'c', 'd', final, 1},
|
||||
},
|
||||
},
|
||||
{[]stridx{
|
||||
{"bcde", 2},
|
||||
{"bc", 7},
|
||||
{"ab", 6},
|
||||
{"bcd", 5},
|
||||
{"abcd", 1},
|
||||
{"abc", 4},
|
||||
{"bcdf", 3},
|
||||
},
|
||||
2,
|
||||
contractTrieSet{
|
||||
{'b', 3, 1, noIndex},
|
||||
{'a', 0, 1, noIndex},
|
||||
{'b', 0, 1, 6},
|
||||
{'c', 0, 1, 4},
|
||||
{'d', 'd', final, 1},
|
||||
{'c', 0, 1, 7},
|
||||
{'d', 0, 1, 5},
|
||||
{'e', 'f', final, 2},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestGenStates(t *testing.T) {
|
||||
for i, tt := range genStateTests {
|
||||
si := []stridx{}
|
||||
for _, e := range tt.in {
|
||||
si = append(si, e)
|
||||
}
|
||||
// ensure input is well-formed
|
||||
sort.Sort(genidxSort(si))
|
||||
ct := contractTrieSet{}
|
||||
n, _ := ct.genStates(si)
|
||||
if nn := tt.firstBlockLen; nn != n {
|
||||
t.Errorf("%d: block len %v; want %v", i, n, nn)
|
||||
}
|
||||
if lv, lw := len(ct), len(tt.out); lv != lw {
|
||||
t.Errorf("%d: len %v; want %v", i, lv, lw)
|
||||
continue
|
||||
}
|
||||
for j, fe := range tt.out {
|
||||
const msg = "%d:%d: value %s=%v; want %v"
|
||||
if fe.l != ct[j].l {
|
||||
t.Errorf(msg, i, j, "l", ct[j].l, fe.l)
|
||||
}
|
||||
if fe.h != ct[j].h {
|
||||
t.Errorf(msg, i, j, "h", ct[j].h, fe.h)
|
||||
}
|
||||
if fe.n != ct[j].n {
|
||||
t.Errorf(msg, i, j, "n", ct[j].n, fe.n)
|
||||
}
|
||||
if fe.i != ct[j].i {
|
||||
t.Errorf(msg, i, j, "i", ct[j].i, fe.i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupContraction(t *testing.T) {
|
||||
for i, tt := range genStateTests {
|
||||
input := []string{}
|
||||
for _, e := range tt.in {
|
||||
input = append(input, e.str)
|
||||
}
|
||||
cts := contractTrieSet{}
|
||||
h, _ := cts.appendTrie(input)
|
||||
for j, si := range tt.in {
|
||||
str := si.str
|
||||
for _, s := range []string{str, str + "X"} {
|
||||
msg := "%d:%d: %s(%s) %v; want %v"
|
||||
idx, sn := cts.lookup(h, []byte(s))
|
||||
if idx != si.index {
|
||||
t.Errorf(msg, i, j, "index", s, idx, si.index)
|
||||
}
|
||||
if sn != len(str) {
|
||||
t.Errorf(msg, i, j, "sn", s, sn, len(str))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintContractionTrieSet(t *testing.T) {
|
||||
testdata := contractTrieSet(genStateTests[4].out)
|
||||
buf := &bytes.Buffer{}
|
||||
testdata.print(buf, "test")
|
||||
if contractTrieOutput != buf.String() {
|
||||
t.Errorf("output differs; found\n%s", buf.String())
|
||||
println(string(buf.Bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
const contractTrieOutput = `// testCTEntries: 8 entries, 32 bytes
|
||||
var testCTEntries = [8]struct{l,h,n,i uint8}{
|
||||
{0x62, 0x3, 1, 255},
|
||||
{0x61, 0x0, 1, 255},
|
||||
{0x62, 0x0, 1, 6},
|
||||
{0x63, 0x0, 1, 4},
|
||||
{0x64, 0x64, 0, 1},
|
||||
{0x63, 0x0, 1, 7},
|
||||
{0x64, 0x0, 1, 5},
|
||||
{0x65, 0x66, 0, 2},
|
||||
}
|
||||
var testContractTrieSet = contractTrieSet( testCTEntries[:] )
|
||||
`
|
||||
393
vendor/golang.org/x/text/collate/build/order.go
generated
vendored
Normal file
393
vendor/golang.org/x/text/collate/build/order.go
generated
vendored
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
// Copyright 2012 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 build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/collate/colltab"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
type logicalAnchor int
|
||||
|
||||
const (
|
||||
firstAnchor logicalAnchor = -1
|
||||
noAnchor = 0
|
||||
lastAnchor = 1
|
||||
)
|
||||
|
||||
// entry is used to keep track of a single entry in the collation element table
|
||||
// during building. Examples of entries can be found in the Default Unicode
|
||||
// Collation Element Table.
|
||||
// See http://www.unicode.org/Public/UCA/6.0.0/allkeys.txt.
|
||||
type entry struct {
|
||||
str string // same as string(runes)
|
||||
runes []rune
|
||||
elems []rawCE // the collation elements
|
||||
extend string // weights of extend to be appended to elems
|
||||
before bool // weights relative to next instead of previous.
|
||||
lock bool // entry is used in extension and can no longer be moved.
|
||||
|
||||
// prev, next, and level are used to keep track of tailorings.
|
||||
prev, next *entry
|
||||
level colltab.Level // next differs at this level
|
||||
skipRemove bool // do not unlink when removed
|
||||
|
||||
decompose bool // can use NFKD decomposition to generate elems
|
||||
exclude bool // do not include in table
|
||||
implicit bool // derived, is not included in the list
|
||||
modified bool // entry was modified in tailoring
|
||||
logical logicalAnchor
|
||||
|
||||
expansionIndex int // used to store index into expansion table
|
||||
contractionHandle ctHandle
|
||||
contractionIndex int // index into contraction elements
|
||||
}
|
||||
|
||||
func (e *entry) String() string {
|
||||
return fmt.Sprintf("%X (%q) -> %X (ch:%x; ci:%d, ei:%d)",
|
||||
e.runes, e.str, e.elems, e.contractionHandle, e.contractionIndex, e.expansionIndex)
|
||||
}
|
||||
|
||||
func (e *entry) skip() bool {
|
||||
return e.contraction()
|
||||
}
|
||||
|
||||
func (e *entry) expansion() bool {
|
||||
return !e.decompose && len(e.elems) > 1
|
||||
}
|
||||
|
||||
func (e *entry) contraction() bool {
|
||||
return len(e.runes) > 1
|
||||
}
|
||||
|
||||
func (e *entry) contractionStarter() bool {
|
||||
return e.contractionHandle.n != 0
|
||||
}
|
||||
|
||||
// nextIndexed gets the next entry that needs to be stored in the table.
|
||||
// It returns the entry and the collation level at which the next entry differs
|
||||
// from the current entry.
|
||||
// Entries that can be explicitly derived and logical reset positions are
|
||||
// examples of entries that will not be indexed.
|
||||
func (e *entry) nextIndexed() (*entry, colltab.Level) {
|
||||
level := e.level
|
||||
for e = e.next; e != nil && (e.exclude || len(e.elems) == 0); e = e.next {
|
||||
if e.level < level {
|
||||
level = e.level
|
||||
}
|
||||
}
|
||||
return e, level
|
||||
}
|
||||
|
||||
// remove unlinks entry e from the sorted chain and clears the collation
|
||||
// elements. e may not be at the front or end of the list. This should always
|
||||
// be the case, as the front and end of the list are always logical anchors,
|
||||
// which may not be removed.
|
||||
func (e *entry) remove() {
|
||||
if e.logical != noAnchor {
|
||||
log.Fatalf("may not remove anchor %q", e.str)
|
||||
}
|
||||
// TODO: need to set e.prev.level to e.level if e.level is smaller?
|
||||
e.elems = nil
|
||||
if !e.skipRemove {
|
||||
if e.prev != nil {
|
||||
e.prev.next = e.next
|
||||
}
|
||||
if e.next != nil {
|
||||
e.next.prev = e.prev
|
||||
}
|
||||
}
|
||||
e.skipRemove = false
|
||||
}
|
||||
|
||||
// insertAfter inserts n after e.
|
||||
func (e *entry) insertAfter(n *entry) {
|
||||
if e == n {
|
||||
panic("e == anchor")
|
||||
}
|
||||
if e == nil {
|
||||
panic("unexpected nil anchor")
|
||||
}
|
||||
n.remove()
|
||||
n.decompose = false // redo decomposition test
|
||||
|
||||
n.next = e.next
|
||||
n.prev = e
|
||||
if e.next != nil {
|
||||
e.next.prev = n
|
||||
}
|
||||
e.next = n
|
||||
}
|
||||
|
||||
// insertBefore inserts n before e.
|
||||
func (e *entry) insertBefore(n *entry) {
|
||||
if e == n {
|
||||
panic("e == anchor")
|
||||
}
|
||||
if e == nil {
|
||||
panic("unexpected nil anchor")
|
||||
}
|
||||
n.remove()
|
||||
n.decompose = false // redo decomposition test
|
||||
|
||||
n.prev = e.prev
|
||||
n.next = e
|
||||
if e.prev != nil {
|
||||
e.prev.next = n
|
||||
}
|
||||
e.prev = n
|
||||
}
|
||||
|
||||
func (e *entry) encodeBase() (ce uint32, err error) {
|
||||
switch {
|
||||
case e.expansion():
|
||||
ce, err = makeExpandIndex(e.expansionIndex)
|
||||
default:
|
||||
if e.decompose {
|
||||
log.Fatal("decompose should be handled elsewhere")
|
||||
}
|
||||
ce, err = makeCE(e.elems[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *entry) encode() (ce uint32, err error) {
|
||||
if e.skip() {
|
||||
log.Fatal("cannot build colElem for entry that should be skipped")
|
||||
}
|
||||
switch {
|
||||
case e.decompose:
|
||||
t1 := e.elems[0].w[2]
|
||||
t2 := 0
|
||||
if len(e.elems) > 1 {
|
||||
t2 = e.elems[1].w[2]
|
||||
}
|
||||
ce, err = makeDecompose(t1, t2)
|
||||
case e.contractionStarter():
|
||||
ce, err = makeContractIndex(e.contractionHandle, e.contractionIndex)
|
||||
default:
|
||||
if len(e.runes) > 1 {
|
||||
log.Fatal("colElem: contractions are handled in contraction trie")
|
||||
}
|
||||
ce, err = e.encodeBase()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// entryLess returns true if a sorts before b and false otherwise.
|
||||
func entryLess(a, b *entry) bool {
|
||||
if res, _ := compareWeights(a.elems, b.elems); res != 0 {
|
||||
return res == -1
|
||||
}
|
||||
if a.logical != noAnchor {
|
||||
return a.logical == firstAnchor
|
||||
}
|
||||
if b.logical != noAnchor {
|
||||
return b.logical == lastAnchor
|
||||
}
|
||||
return a.str < b.str
|
||||
}
|
||||
|
||||
type sortedEntries []*entry
|
||||
|
||||
func (s sortedEntries) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s sortedEntries) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
func (s sortedEntries) Less(i, j int) bool {
|
||||
return entryLess(s[i], s[j])
|
||||
}
|
||||
|
||||
type ordering struct {
|
||||
id string
|
||||
entryMap map[string]*entry
|
||||
ordered []*entry
|
||||
handle *trieHandle
|
||||
}
|
||||
|
||||
// insert inserts e into both entryMap and ordered.
|
||||
// Note that insert simply appends e to ordered. To reattain a sorted
|
||||
// order, o.sort() should be called.
|
||||
func (o *ordering) insert(e *entry) {
|
||||
if e.logical == noAnchor {
|
||||
o.entryMap[e.str] = e
|
||||
} else {
|
||||
// Use key format as used in UCA rules.
|
||||
o.entryMap[fmt.Sprintf("[%s]", e.str)] = e
|
||||
// Also add index entry for XML format.
|
||||
o.entryMap[fmt.Sprintf("<%s/>", strings.Replace(e.str, " ", "_", -1))] = e
|
||||
}
|
||||
o.ordered = append(o.ordered, e)
|
||||
}
|
||||
|
||||
// newEntry creates a new entry for the given info and inserts it into
|
||||
// the index.
|
||||
func (o *ordering) newEntry(s string, ces []rawCE) *entry {
|
||||
e := &entry{
|
||||
runes: []rune(s),
|
||||
elems: ces,
|
||||
str: s,
|
||||
}
|
||||
o.insert(e)
|
||||
return e
|
||||
}
|
||||
|
||||
// find looks up and returns the entry for the given string.
|
||||
// It returns nil if str is not in the index and if an implicit value
|
||||
// cannot be derived, that is, if str represents more than one rune.
|
||||
func (o *ordering) find(str string) *entry {
|
||||
e := o.entryMap[str]
|
||||
if e == nil {
|
||||
r := []rune(str)
|
||||
if len(r) == 1 {
|
||||
const (
|
||||
firstHangul = 0xAC00
|
||||
lastHangul = 0xD7A3
|
||||
)
|
||||
if r[0] >= firstHangul && r[0] <= lastHangul {
|
||||
ce := []rawCE{}
|
||||
nfd := norm.NFD.String(str)
|
||||
for _, r := range nfd {
|
||||
ce = append(ce, o.find(string(r)).elems...)
|
||||
}
|
||||
e = o.newEntry(nfd, ce)
|
||||
} else {
|
||||
e = o.newEntry(string(r[0]), []rawCE{
|
||||
{w: []int{
|
||||
implicitPrimary(r[0]),
|
||||
defaultSecondary,
|
||||
defaultTertiary,
|
||||
int(r[0]),
|
||||
},
|
||||
},
|
||||
})
|
||||
e.modified = true
|
||||
}
|
||||
e.exclude = true // do not index implicits
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// makeRootOrdering returns a newly initialized ordering value and populates
|
||||
// it with a set of logical reset points that can be used as anchors.
|
||||
// The anchors first_tertiary_ignorable and __END__ will always sort at
|
||||
// the beginning and end, respectively. This means that prev and next are non-nil
|
||||
// for any indexed entry.
|
||||
func makeRootOrdering() ordering {
|
||||
const max = unicode.MaxRune
|
||||
o := ordering{
|
||||
entryMap: make(map[string]*entry),
|
||||
}
|
||||
insert := func(typ logicalAnchor, s string, ce []int) {
|
||||
e := &entry{
|
||||
elems: []rawCE{{w: ce}},
|
||||
str: s,
|
||||
exclude: true,
|
||||
logical: typ,
|
||||
}
|
||||
o.insert(e)
|
||||
}
|
||||
insert(firstAnchor, "first tertiary ignorable", []int{0, 0, 0, 0})
|
||||
insert(lastAnchor, "last tertiary ignorable", []int{0, 0, 0, max})
|
||||
insert(lastAnchor, "last primary ignorable", []int{0, defaultSecondary, defaultTertiary, max})
|
||||
insert(lastAnchor, "last non ignorable", []int{maxPrimary, defaultSecondary, defaultTertiary, max})
|
||||
insert(lastAnchor, "__END__", []int{1 << maxPrimaryBits, defaultSecondary, defaultTertiary, max})
|
||||
return o
|
||||
}
|
||||
|
||||
// patchForInsert eleminates entries from the list with more than one collation element.
|
||||
// The next and prev fields of the eliminated entries still point to appropriate
|
||||
// values in the newly created list.
|
||||
// It requires that sort has been called.
|
||||
func (o *ordering) patchForInsert() {
|
||||
for i := 0; i < len(o.ordered)-1; {
|
||||
e := o.ordered[i]
|
||||
lev := e.level
|
||||
n := e.next
|
||||
for ; n != nil && len(n.elems) > 1; n = n.next {
|
||||
if n.level < lev {
|
||||
lev = n.level
|
||||
}
|
||||
n.skipRemove = true
|
||||
}
|
||||
for ; o.ordered[i] != n; i++ {
|
||||
o.ordered[i].level = lev
|
||||
o.ordered[i].next = n
|
||||
o.ordered[i+1].prev = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clone copies all ordering of es into a new ordering value.
|
||||
func (o *ordering) clone() *ordering {
|
||||
o.sort()
|
||||
oo := ordering{
|
||||
entryMap: make(map[string]*entry),
|
||||
}
|
||||
for _, e := range o.ordered {
|
||||
ne := &entry{
|
||||
runes: e.runes,
|
||||
elems: e.elems,
|
||||
str: e.str,
|
||||
decompose: e.decompose,
|
||||
exclude: e.exclude,
|
||||
logical: e.logical,
|
||||
}
|
||||
oo.insert(ne)
|
||||
}
|
||||
oo.sort() // link all ordering.
|
||||
oo.patchForInsert()
|
||||
return &oo
|
||||
}
|
||||
|
||||
// front returns the first entry to be indexed.
|
||||
// It assumes that sort() has been called.
|
||||
func (o *ordering) front() *entry {
|
||||
e := o.ordered[0]
|
||||
if e.prev != nil {
|
||||
log.Panicf("unexpected first entry: %v", e)
|
||||
}
|
||||
// The first entry is always a logical position, which should not be indexed.
|
||||
e, _ = e.nextIndexed()
|
||||
return e
|
||||
}
|
||||
|
||||
// sort sorts all ordering based on their collation elements and initializes
|
||||
// the prev, next, and level fields accordingly.
|
||||
func (o *ordering) sort() {
|
||||
sort.Sort(sortedEntries(o.ordered))
|
||||
l := o.ordered
|
||||
for i := 1; i < len(l); i++ {
|
||||
k := i - 1
|
||||
l[k].next = l[i]
|
||||
_, l[k].level = compareWeights(l[k].elems, l[i].elems)
|
||||
l[i].prev = l[k]
|
||||
}
|
||||
}
|
||||
|
||||
// genColElems generates a collation element array from the runes in str. This
|
||||
// assumes that all collation elements have already been added to the Builder.
|
||||
func (o *ordering) genColElems(str string) []rawCE {
|
||||
elems := []rawCE{}
|
||||
for _, r := range []rune(str) {
|
||||
for _, ce := range o.find(string(r)).elems {
|
||||
if ce.w[0] != 0 || ce.w[1] != 0 || ce.w[2] != 0 {
|
||||
elems = append(elems, ce)
|
||||
}
|
||||
}
|
||||
}
|
||||
return elems
|
||||
}
|
||||
229
vendor/golang.org/x/text/collate/build/order_test.go
generated
vendored
Normal file
229
vendor/golang.org/x/text/collate/build/order_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
// Copyright 2012 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 build
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/collate/colltab"
|
||||
)
|
||||
|
||||
type entryTest struct {
|
||||
f func(in []int) (uint32, error)
|
||||
arg []int
|
||||
val uint32
|
||||
}
|
||||
|
||||
// makeList returns a list of entries of length n+2, with n normal
|
||||
// entries plus a leading and trailing anchor.
|
||||
func makeList(n int) []*entry {
|
||||
es := make([]*entry, n+2)
|
||||
weights := []rawCE{{w: []int{100, 20, 5, 0}}}
|
||||
for i := range es {
|
||||
runes := []rune{rune(i)}
|
||||
es[i] = &entry{
|
||||
runes: runes,
|
||||
elems: weights,
|
||||
}
|
||||
weights = nextWeight(colltab.Primary, weights)
|
||||
}
|
||||
for i := 1; i < len(es); i++ {
|
||||
es[i-1].next = es[i]
|
||||
es[i].prev = es[i-1]
|
||||
_, es[i-1].level = compareWeights(es[i-1].elems, es[i].elems)
|
||||
}
|
||||
es[0].exclude = true
|
||||
es[0].logical = firstAnchor
|
||||
es[len(es)-1].exclude = true
|
||||
es[len(es)-1].logical = lastAnchor
|
||||
return es
|
||||
}
|
||||
|
||||
func TestNextIndexed(t *testing.T) {
|
||||
const n = 5
|
||||
es := makeList(n)
|
||||
for i := int64(0); i < 1<<n; i++ {
|
||||
mask := strconv.FormatInt(i+(1<<n), 2)
|
||||
for i, c := range mask {
|
||||
es[i].exclude = c == '1'
|
||||
}
|
||||
e := es[0]
|
||||
for i, c := range mask {
|
||||
if c == '0' {
|
||||
e, _ = e.nextIndexed()
|
||||
if e != es[i] {
|
||||
t.Errorf("%d: expected entry %d; found %d", i, es[i].elems, e.elems)
|
||||
}
|
||||
}
|
||||
}
|
||||
if e, _ = e.nextIndexed(); e != nil {
|
||||
t.Errorf("%d: expected nil entry; found %d", i, e.elems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
const n = 5
|
||||
for i := int64(0); i < 1<<n; i++ {
|
||||
es := makeList(n)
|
||||
mask := strconv.FormatInt(i+(1<<n), 2)
|
||||
for i, c := range mask {
|
||||
if c == '0' {
|
||||
es[i].remove()
|
||||
}
|
||||
}
|
||||
e := es[0]
|
||||
for i, c := range mask {
|
||||
if c == '1' {
|
||||
if e != es[i] {
|
||||
t.Errorf("%d: expected entry %d; found %d", i, es[i].elems, e.elems)
|
||||
}
|
||||
e, _ = e.nextIndexed()
|
||||
}
|
||||
}
|
||||
if e != nil {
|
||||
t.Errorf("%d: expected nil entry; found %d", i, e.elems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nextPerm generates the next permutation of the array. The starting
|
||||
// permutation is assumed to be a list of integers sorted in increasing order.
|
||||
// It returns false if there are no more permuations left.
|
||||
func nextPerm(a []int) bool {
|
||||
i := len(a) - 2
|
||||
for ; i >= 0; i-- {
|
||||
if a[i] < a[i+1] {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i < 0 {
|
||||
return false
|
||||
}
|
||||
for j := len(a) - 1; j >= i; j-- {
|
||||
if a[j] > a[i] {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
for j := i + 1; j < (len(a)+i+1)/2; j++ {
|
||||
a[j], a[len(a)+i-j] = a[len(a)+i-j], a[j]
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestInsertAfter(t *testing.T) {
|
||||
const n = 5
|
||||
orig := makeList(n)
|
||||
perm := make([]int, n)
|
||||
for i := range perm {
|
||||
perm[i] = i + 1
|
||||
}
|
||||
for ok := true; ok; ok = nextPerm(perm) {
|
||||
es := makeList(n)
|
||||
last := es[0]
|
||||
for _, i := range perm {
|
||||
last.insertAfter(es[i])
|
||||
last = es[i]
|
||||
}
|
||||
for _, e := range es {
|
||||
e.elems = es[0].elems
|
||||
}
|
||||
e := es[0]
|
||||
for _, i := range perm {
|
||||
e, _ = e.nextIndexed()
|
||||
if e.runes[0] != orig[i].runes[0] {
|
||||
t.Errorf("%d:%d: expected entry %X; found %X", perm, i, orig[i].runes, e.runes)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertBefore(t *testing.T) {
|
||||
const n = 5
|
||||
orig := makeList(n)
|
||||
perm := make([]int, n)
|
||||
for i := range perm {
|
||||
perm[i] = i + 1
|
||||
}
|
||||
for ok := true; ok; ok = nextPerm(perm) {
|
||||
es := makeList(n)
|
||||
last := es[len(es)-1]
|
||||
for _, i := range perm {
|
||||
last.insertBefore(es[i])
|
||||
last = es[i]
|
||||
}
|
||||
for _, e := range es {
|
||||
e.elems = es[0].elems
|
||||
}
|
||||
e := es[0]
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
e, _ = e.nextIndexed()
|
||||
if e.runes[0] != rune(perm[i]) {
|
||||
t.Errorf("%d:%d: expected entry %X; found %X", perm, i, orig[i].runes, e.runes)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type entryLessTest struct {
|
||||
a, b *entry
|
||||
res bool
|
||||
}
|
||||
|
||||
var (
|
||||
w1 = []rawCE{{w: []int{100, 20, 5, 5}}}
|
||||
w2 = []rawCE{{w: []int{101, 20, 5, 5}}}
|
||||
)
|
||||
|
||||
var entryLessTests = []entryLessTest{
|
||||
{&entry{str: "a", elems: w1},
|
||||
&entry{str: "a", elems: w1},
|
||||
false,
|
||||
},
|
||||
{&entry{str: "a", elems: w1},
|
||||
&entry{str: "a", elems: w2},
|
||||
true,
|
||||
},
|
||||
{&entry{str: "a", elems: w1},
|
||||
&entry{str: "b", elems: w1},
|
||||
true,
|
||||
},
|
||||
{&entry{str: "a", elems: w2},
|
||||
&entry{str: "a", elems: w1},
|
||||
false,
|
||||
},
|
||||
{&entry{str: "c", elems: w1},
|
||||
&entry{str: "b", elems: w1},
|
||||
false,
|
||||
},
|
||||
{&entry{str: "a", elems: w1, logical: firstAnchor},
|
||||
&entry{str: "a", elems: w1},
|
||||
true,
|
||||
},
|
||||
{&entry{str: "a", elems: w1},
|
||||
&entry{str: "b", elems: w1, logical: firstAnchor},
|
||||
false,
|
||||
},
|
||||
{&entry{str: "b", elems: w1},
|
||||
&entry{str: "a", elems: w1, logical: lastAnchor},
|
||||
true,
|
||||
},
|
||||
{&entry{str: "a", elems: w1, logical: lastAnchor},
|
||||
&entry{str: "c", elems: w1},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
func TestEntryLess(t *testing.T) {
|
||||
for i, tt := range entryLessTests {
|
||||
if res := entryLess(tt.a, tt.b); res != tt.res {
|
||||
t.Errorf("%d: was %v; want %v", i, res, tt.res)
|
||||
}
|
||||
}
|
||||
}
|
||||
120
vendor/golang.org/x/text/collate/build/table.go
generated
vendored
Normal file
120
vendor/golang.org/x/text/collate/build/table.go
generated
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2012 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 build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// table is an intermediate structure that roughly resembles the table in collate.
|
||||
// It implements the non-exported interface collate.tableInitializer
|
||||
type table struct {
|
||||
index trie // main trie
|
||||
root *trieHandle
|
||||
|
||||
// expansion info
|
||||
expandElem []uint32
|
||||
|
||||
// contraction info
|
||||
contractTries contractTrieSet
|
||||
contractElem []uint32
|
||||
maxContractLen int
|
||||
variableTop uint32
|
||||
}
|
||||
|
||||
func (t *table) TrieIndex() []uint16 {
|
||||
return t.index.index
|
||||
}
|
||||
|
||||
func (t *table) TrieValues() []uint32 {
|
||||
return t.index.values
|
||||
}
|
||||
|
||||
func (t *table) FirstBlockOffsets() (i, v uint16) {
|
||||
return t.root.lookupStart, t.root.valueStart
|
||||
}
|
||||
|
||||
func (t *table) ExpandElems() []uint32 {
|
||||
return t.expandElem
|
||||
}
|
||||
|
||||
func (t *table) ContractTries() []struct{ l, h, n, i uint8 } {
|
||||
return t.contractTries
|
||||
}
|
||||
|
||||
func (t *table) ContractElems() []uint32 {
|
||||
return t.contractElem
|
||||
}
|
||||
|
||||
func (t *table) MaxContractLen() int {
|
||||
return t.maxContractLen
|
||||
}
|
||||
|
||||
func (t *table) VariableTop() uint32 {
|
||||
return t.variableTop
|
||||
}
|
||||
|
||||
// print writes the table as Go compilable code to w. It prefixes the
|
||||
// variable names with name. It returns the number of bytes written
|
||||
// and the size of the resulting table.
|
||||
func (t *table) fprint(w io.Writer, name string) (n, size int, err error) {
|
||||
update := func(nn, sz int, e error) {
|
||||
n += nn
|
||||
if err == nil {
|
||||
err = e
|
||||
}
|
||||
size += sz
|
||||
}
|
||||
// Write arrays needed for the structure.
|
||||
update(printColElems(w, t.expandElem, name+"ExpandElem"))
|
||||
update(printColElems(w, t.contractElem, name+"ContractElem"))
|
||||
update(t.index.printArrays(w, name))
|
||||
update(t.contractTries.printArray(w, name))
|
||||
|
||||
nn, e := fmt.Fprintf(w, "// Total size of %sTable is %d bytes\n", name, size)
|
||||
update(nn, 0, e)
|
||||
return
|
||||
}
|
||||
|
||||
func (t *table) fprintIndex(w io.Writer, h *trieHandle, id string) (n int, err error) {
|
||||
p := func(f string, a ...interface{}) {
|
||||
nn, e := fmt.Fprintf(w, f, a...)
|
||||
n += nn
|
||||
if err == nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
p("\t{ // %s\n", id)
|
||||
p("\t\tlookupOffset: 0x%x,\n", h.lookupStart)
|
||||
p("\t\tvaluesOffset: 0x%x,\n", h.valueStart)
|
||||
p("\t},\n")
|
||||
return
|
||||
}
|
||||
|
||||
func printColElems(w io.Writer, a []uint32, name string) (n, sz int, err error) {
|
||||
p := func(f string, a ...interface{}) {
|
||||
nn, e := fmt.Fprintf(w, f, a...)
|
||||
n += nn
|
||||
if err == nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
sz = len(a) * int(reflect.TypeOf(uint32(0)).Size())
|
||||
p("// %s: %d entries, %d bytes\n", name, len(a), sz)
|
||||
p("var %s = [%d]uint32 {", name, len(a))
|
||||
for i, c := range a {
|
||||
switch {
|
||||
case i%64 == 0:
|
||||
p("\n\t// Block %d, offset 0x%x\n", i/64, i)
|
||||
case (i%64)%6 == 0:
|
||||
p("\n\t")
|
||||
}
|
||||
p("0x%.8X, ", c)
|
||||
}
|
||||
p("\n}\n\n")
|
||||
return
|
||||
}
|
||||
290
vendor/golang.org/x/text/collate/build/trie.go
generated
vendored
Normal file
290
vendor/golang.org/x/text/collate/build/trie.go
generated
vendored
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// The trie in this file is used to associate the first full character
|
||||
// in a UTF-8 string to a collation element.
|
||||
// All but the last byte in a UTF-8 byte sequence are
|
||||
// used to look up offsets in the index table to be used for the next byte.
|
||||
// The last byte is used to index into a table of collation elements.
|
||||
// This file contains the code for the generation of the trie.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
blockSize = 64
|
||||
blockOffset = 2 // Subtract 2 blocks to compensate for the 0x80 added to continuation bytes.
|
||||
)
|
||||
|
||||
type trieHandle struct {
|
||||
lookupStart uint16 // offset in table for first byte
|
||||
valueStart uint16 // offset in table for first byte
|
||||
}
|
||||
|
||||
type trie struct {
|
||||
index []uint16
|
||||
values []uint32
|
||||
}
|
||||
|
||||
// trieNode is the intermediate trie structure used for generating a trie.
|
||||
type trieNode struct {
|
||||
index []*trieNode
|
||||
value []uint32
|
||||
b byte
|
||||
refValue uint16
|
||||
refIndex uint16
|
||||
}
|
||||
|
||||
func newNode() *trieNode {
|
||||
return &trieNode{
|
||||
index: make([]*trieNode, 64),
|
||||
value: make([]uint32, 128), // root node size is 128 instead of 64
|
||||
}
|
||||
}
|
||||
|
||||
func (n *trieNode) isInternal() bool {
|
||||
return n.value != nil
|
||||
}
|
||||
|
||||
func (n *trieNode) insert(r rune, value uint32) {
|
||||
const maskx = 0x3F // mask out two most-significant bits
|
||||
str := string(r)
|
||||
if len(str) == 1 {
|
||||
n.value[str[0]] = value
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(str)-1; i++ {
|
||||
b := str[i] & maskx
|
||||
if n.index == nil {
|
||||
n.index = make([]*trieNode, blockSize)
|
||||
}
|
||||
nn := n.index[b]
|
||||
if nn == nil {
|
||||
nn = &trieNode{}
|
||||
nn.b = b
|
||||
n.index[b] = nn
|
||||
}
|
||||
n = nn
|
||||
}
|
||||
if n.value == nil {
|
||||
n.value = make([]uint32, blockSize)
|
||||
}
|
||||
b := str[len(str)-1] & maskx
|
||||
n.value[b] = value
|
||||
}
|
||||
|
||||
type trieBuilder struct {
|
||||
t *trie
|
||||
|
||||
roots []*trieHandle
|
||||
|
||||
lookupBlocks []*trieNode
|
||||
valueBlocks []*trieNode
|
||||
|
||||
lookupBlockIdx map[uint32]*trieNode
|
||||
valueBlockIdx map[uint32]*trieNode
|
||||
}
|
||||
|
||||
func newTrieBuilder() *trieBuilder {
|
||||
index := &trieBuilder{}
|
||||
index.lookupBlocks = make([]*trieNode, 0)
|
||||
index.valueBlocks = make([]*trieNode, 0)
|
||||
index.lookupBlockIdx = make(map[uint32]*trieNode)
|
||||
index.valueBlockIdx = make(map[uint32]*trieNode)
|
||||
// The third nil is the default null block. The other two blocks
|
||||
// are used to guarantee an offset of at least 3 for each block.
|
||||
index.lookupBlocks = append(index.lookupBlocks, nil, nil, nil)
|
||||
index.t = &trie{}
|
||||
return index
|
||||
}
|
||||
|
||||
func (b *trieBuilder) computeOffsets(n *trieNode) *trieNode {
|
||||
hasher := fnv.New32()
|
||||
if n.index != nil {
|
||||
for i, nn := range n.index {
|
||||
var vi, vv uint16
|
||||
if nn != nil {
|
||||
nn = b.computeOffsets(nn)
|
||||
n.index[i] = nn
|
||||
vi = nn.refIndex
|
||||
vv = nn.refValue
|
||||
}
|
||||
hasher.Write([]byte{byte(vi >> 8), byte(vi)})
|
||||
hasher.Write([]byte{byte(vv >> 8), byte(vv)})
|
||||
}
|
||||
h := hasher.Sum32()
|
||||
nn, ok := b.lookupBlockIdx[h]
|
||||
if !ok {
|
||||
n.refIndex = uint16(len(b.lookupBlocks)) - blockOffset
|
||||
b.lookupBlocks = append(b.lookupBlocks, n)
|
||||
b.lookupBlockIdx[h] = n
|
||||
} else {
|
||||
n = nn
|
||||
}
|
||||
} else {
|
||||
for _, v := range n.value {
|
||||
hasher.Write([]byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)})
|
||||
}
|
||||
h := hasher.Sum32()
|
||||
nn, ok := b.valueBlockIdx[h]
|
||||
if !ok {
|
||||
n.refValue = uint16(len(b.valueBlocks)) - blockOffset
|
||||
n.refIndex = n.refValue
|
||||
b.valueBlocks = append(b.valueBlocks, n)
|
||||
b.valueBlockIdx[h] = n
|
||||
} else {
|
||||
n = nn
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (b *trieBuilder) addStartValueBlock(n *trieNode) uint16 {
|
||||
hasher := fnv.New32()
|
||||
for _, v := range n.value[:2*blockSize] {
|
||||
hasher.Write([]byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)})
|
||||
}
|
||||
h := hasher.Sum32()
|
||||
nn, ok := b.valueBlockIdx[h]
|
||||
if !ok {
|
||||
n.refValue = uint16(len(b.valueBlocks))
|
||||
n.refIndex = n.refValue
|
||||
b.valueBlocks = append(b.valueBlocks, n)
|
||||
// Add a dummy block to accommodate the double block size.
|
||||
b.valueBlocks = append(b.valueBlocks, nil)
|
||||
b.valueBlockIdx[h] = n
|
||||
} else {
|
||||
n = nn
|
||||
}
|
||||
return n.refValue
|
||||
}
|
||||
|
||||
func genValueBlock(t *trie, n *trieNode) {
|
||||
if n != nil {
|
||||
for _, v := range n.value {
|
||||
t.values = append(t.values, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func genLookupBlock(t *trie, n *trieNode) {
|
||||
for _, nn := range n.index {
|
||||
v := uint16(0)
|
||||
if nn != nil {
|
||||
if n.index != nil {
|
||||
v = nn.refIndex
|
||||
} else {
|
||||
v = nn.refValue
|
||||
}
|
||||
}
|
||||
t.index = append(t.index, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *trieBuilder) addTrie(n *trieNode) *trieHandle {
|
||||
h := &trieHandle{}
|
||||
b.roots = append(b.roots, h)
|
||||
h.valueStart = b.addStartValueBlock(n)
|
||||
if len(b.roots) == 1 {
|
||||
// We insert a null block after the first start value block.
|
||||
// This ensures that continuation bytes UTF-8 sequences of length
|
||||
// greater than 2 will automatically hit a null block if there
|
||||
// was an undefined entry.
|
||||
b.valueBlocks = append(b.valueBlocks, nil)
|
||||
}
|
||||
n = b.computeOffsets(n)
|
||||
// Offset by one extra block as the first byte starts at 0xC0 instead of 0x80.
|
||||
h.lookupStart = n.refIndex - 1
|
||||
return h
|
||||
}
|
||||
|
||||
// generate generates and returns the trie for n.
|
||||
func (b *trieBuilder) generate() (t *trie, err error) {
|
||||
t = b.t
|
||||
if len(b.valueBlocks) >= 1<<16 {
|
||||
return nil, fmt.Errorf("maximum number of value blocks exceeded (%d > %d)", len(b.valueBlocks), 1<<16)
|
||||
}
|
||||
if len(b.lookupBlocks) >= 1<<16 {
|
||||
return nil, fmt.Errorf("maximum number of lookup blocks exceeded (%d > %d)", len(b.lookupBlocks), 1<<16)
|
||||
}
|
||||
genValueBlock(t, b.valueBlocks[0])
|
||||
genValueBlock(t, &trieNode{value: make([]uint32, 64)})
|
||||
for i := 2; i < len(b.valueBlocks); i++ {
|
||||
genValueBlock(t, b.valueBlocks[i])
|
||||
}
|
||||
n := &trieNode{index: make([]*trieNode, 64)}
|
||||
genLookupBlock(t, n)
|
||||
genLookupBlock(t, n)
|
||||
genLookupBlock(t, n)
|
||||
for i := 3; i < len(b.lookupBlocks); i++ {
|
||||
genLookupBlock(t, b.lookupBlocks[i])
|
||||
}
|
||||
return b.t, nil
|
||||
}
|
||||
|
||||
func (t *trie) printArrays(w io.Writer, name string) (n, size int, err error) {
|
||||
p := func(f string, a ...interface{}) {
|
||||
nn, e := fmt.Fprintf(w, f, a...)
|
||||
n += nn
|
||||
if err == nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
nv := len(t.values)
|
||||
p("// %sValues: %d entries, %d bytes\n", name, nv, nv*4)
|
||||
p("// Block 2 is the null block.\n")
|
||||
p("var %sValues = [%d]uint32 {", name, nv)
|
||||
var printnewline bool
|
||||
for i, v := range t.values {
|
||||
if i%blockSize == 0 {
|
||||
p("\n\t// Block %#x, offset %#x", i/blockSize, i)
|
||||
}
|
||||
if i%4 == 0 {
|
||||
printnewline = true
|
||||
}
|
||||
if v != 0 {
|
||||
if printnewline {
|
||||
p("\n\t")
|
||||
printnewline = false
|
||||
}
|
||||
p("%#04x:%#08x, ", i, v)
|
||||
}
|
||||
}
|
||||
p("\n}\n\n")
|
||||
ni := len(t.index)
|
||||
p("// %sLookup: %d entries, %d bytes\n", name, ni, ni*2)
|
||||
p("// Block 0 is the null block.\n")
|
||||
p("var %sLookup = [%d]uint16 {", name, ni)
|
||||
printnewline = false
|
||||
for i, v := range t.index {
|
||||
if i%blockSize == 0 {
|
||||
p("\n\t// Block %#x, offset %#x", i/blockSize, i)
|
||||
}
|
||||
if i%8 == 0 {
|
||||
printnewline = true
|
||||
}
|
||||
if v != 0 {
|
||||
if printnewline {
|
||||
p("\n\t")
|
||||
printnewline = false
|
||||
}
|
||||
p("%#03x:%#02x, ", i, v)
|
||||
}
|
||||
}
|
||||
p("\n}\n\n")
|
||||
return n, nv*4 + ni*2, err
|
||||
}
|
||||
|
||||
func (t *trie) printStruct(w io.Writer, handle *trieHandle, name string) (n, sz int, err error) {
|
||||
const msg = "trie{ %sLookup[%d:], %sValues[%d:], %sLookup[:], %sValues[:]}"
|
||||
n, err = fmt.Fprintf(w, msg, name, handle.lookupStart*blockSize, name, handle.valueStart*blockSize, name, name)
|
||||
sz += int(reflect.TypeOf(trie{}).Size())
|
||||
return
|
||||
}
|
||||
107
vendor/golang.org/x/text/collate/build/trie_test.go
generated
vendored
Normal file
107
vendor/golang.org/x/text/collate/build/trie_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2012 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 build
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// We take the smallest, largest and an arbitrary value for each
|
||||
// of the UTF-8 sequence lengths.
|
||||
var testRunes = []rune{
|
||||
0x01, 0x0C, 0x7F, // 1-byte sequences
|
||||
0x80, 0x100, 0x7FF, // 2-byte sequences
|
||||
0x800, 0x999, 0xFFFF, // 3-byte sequences
|
||||
0x10000, 0x10101, 0x10FFFF, // 4-byte sequences
|
||||
0x200, 0x201, 0x202, 0x210, 0x215, // five entries in one sparse block
|
||||
}
|
||||
|
||||
func makeTestTrie(t *testing.T) trie {
|
||||
n := newNode()
|
||||
for i, r := range testRunes {
|
||||
n.insert(r, uint32(i))
|
||||
}
|
||||
idx := newTrieBuilder()
|
||||
idx.addTrie(n)
|
||||
tr, err := idx.generate()
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
return *tr
|
||||
}
|
||||
|
||||
func TestGenerateTrie(t *testing.T) {
|
||||
testdata := makeTestTrie(t)
|
||||
buf := &bytes.Buffer{}
|
||||
testdata.printArrays(buf, "test")
|
||||
fmt.Fprintf(buf, "var testTrie = ")
|
||||
testdata.printStruct(buf, &trieHandle{19, 0}, "test")
|
||||
if output != buf.String() {
|
||||
t.Error("output differs")
|
||||
}
|
||||
}
|
||||
|
||||
var output = `// testValues: 832 entries, 3328 bytes
|
||||
// Block 2 is the null block.
|
||||
var testValues = [832]uint32 {
|
||||
// Block 0x0, offset 0x0
|
||||
0x000c:0x00000001,
|
||||
// Block 0x1, offset 0x40
|
||||
0x007f:0x00000002,
|
||||
// Block 0x2, offset 0x80
|
||||
// Block 0x3, offset 0xc0
|
||||
0x00c0:0x00000003,
|
||||
// Block 0x4, offset 0x100
|
||||
0x0100:0x00000004,
|
||||
// Block 0x5, offset 0x140
|
||||
0x0140:0x0000000c, 0x0141:0x0000000d, 0x0142:0x0000000e,
|
||||
0x0150:0x0000000f,
|
||||
0x0155:0x00000010,
|
||||
// Block 0x6, offset 0x180
|
||||
0x01bf:0x00000005,
|
||||
// Block 0x7, offset 0x1c0
|
||||
0x01c0:0x00000006,
|
||||
// Block 0x8, offset 0x200
|
||||
0x0219:0x00000007,
|
||||
// Block 0x9, offset 0x240
|
||||
0x027f:0x00000008,
|
||||
// Block 0xa, offset 0x280
|
||||
0x0280:0x00000009,
|
||||
// Block 0xb, offset 0x2c0
|
||||
0x02c1:0x0000000a,
|
||||
// Block 0xc, offset 0x300
|
||||
0x033f:0x0000000b,
|
||||
}
|
||||
|
||||
// testLookup: 640 entries, 1280 bytes
|
||||
// Block 0 is the null block.
|
||||
var testLookup = [640]uint16 {
|
||||
// Block 0x0, offset 0x0
|
||||
// Block 0x1, offset 0x40
|
||||
// Block 0x2, offset 0x80
|
||||
// Block 0x3, offset 0xc0
|
||||
0x0e0:0x05, 0x0e6:0x06,
|
||||
// Block 0x4, offset 0x100
|
||||
0x13f:0x07,
|
||||
// Block 0x5, offset 0x140
|
||||
0x140:0x08, 0x144:0x09,
|
||||
// Block 0x6, offset 0x180
|
||||
0x190:0x03,
|
||||
// Block 0x7, offset 0x1c0
|
||||
0x1ff:0x0a,
|
||||
// Block 0x8, offset 0x200
|
||||
0x20f:0x05,
|
||||
// Block 0x9, offset 0x240
|
||||
0x242:0x01, 0x244:0x02,
|
||||
0x248:0x03,
|
||||
0x25f:0x04,
|
||||
0x260:0x01,
|
||||
0x26f:0x02,
|
||||
0x270:0x04, 0x274:0x06,
|
||||
}
|
||||
|
||||
var testTrie = trie{ testLookup[1216:], testValues[0:], testLookup[:], testValues[:]}`
|
||||
405
vendor/golang.org/x/text/collate/collate.go
generated
vendored
Normal file
405
vendor/golang.org/x/text/collate/collate.go
generated
vendored
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// TODO: remove hard-coded versions when we have implemented fractional weights.
|
||||
// The current implementation is incompatible with later CLDR versions.
|
||||
//go:generate go run maketables.go -cldr=23 -unicode=6.2.0
|
||||
|
||||
// Package collate contains types for comparing and sorting Unicode strings
|
||||
// according to a given collation order. Package locale provides a high-level
|
||||
// interface to collation. Users should typically use that package instead.
|
||||
package collate // import "golang.org/x/text/collate"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/collate/colltab"
|
||||
newcolltab "golang.org/x/text/internal/colltab"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Collator provides functionality for comparing strings for a given
|
||||
// collation order.
|
||||
type Collator struct {
|
||||
options
|
||||
|
||||
sorter sorter
|
||||
|
||||
_iter [2]iter
|
||||
}
|
||||
|
||||
func (c *Collator) iter(i int) *iter {
|
||||
// TODO: evaluate performance for making the second iterator optional.
|
||||
return &c._iter[i]
|
||||
}
|
||||
|
||||
// Supported returns the list of languages for which collating differs from its parent.
|
||||
func Supported() []language.Tag {
|
||||
// TODO: use language.Coverage instead.
|
||||
|
||||
t := make([]language.Tag, len(tags))
|
||||
copy(t, tags)
|
||||
return t
|
||||
}
|
||||
|
||||
func init() {
|
||||
ids := strings.Split(availableLocales, ",")
|
||||
tags = make([]language.Tag, len(ids))
|
||||
for i, s := range ids {
|
||||
tags[i] = language.Raw.MustParse(s)
|
||||
}
|
||||
}
|
||||
|
||||
var tags []language.Tag
|
||||
|
||||
// New returns a new Collator initialized for the given locale.
|
||||
func New(t language.Tag, o ...Option) *Collator {
|
||||
index := newcolltab.MatchLang(t, tags)
|
||||
c := newCollator(colltab.Init(locales[index]))
|
||||
|
||||
// Set options from the user-supplied tag.
|
||||
c.setFromTag(t)
|
||||
|
||||
// Set the user-supplied options.
|
||||
c.setOptions(o)
|
||||
|
||||
c.init()
|
||||
return c
|
||||
}
|
||||
|
||||
// NewFromTable returns a new Collator for the given Weighter.
|
||||
func NewFromTable(w colltab.Weighter, o ...Option) *Collator {
|
||||
c := newCollator(w)
|
||||
c.setOptions(o)
|
||||
c.init()
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Collator) init() {
|
||||
if c.numeric {
|
||||
c.t = colltab.NewNumericWeighter(c.t)
|
||||
}
|
||||
c._iter[0].init(c)
|
||||
c._iter[1].init(c)
|
||||
}
|
||||
|
||||
// Buffer holds keys generated by Key and KeyString.
|
||||
type Buffer struct {
|
||||
buf [4096]byte
|
||||
key []byte
|
||||
}
|
||||
|
||||
func (b *Buffer) init() {
|
||||
if b.key == nil {
|
||||
b.key = b.buf[:0]
|
||||
}
|
||||
}
|
||||
|
||||
// Reset clears the buffer from previous results generated by Key and KeyString.
|
||||
func (b *Buffer) Reset() {
|
||||
b.key = b.key[:0]
|
||||
}
|
||||
|
||||
// Compare returns an integer comparing the two byte slices.
|
||||
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
|
||||
func (c *Collator) Compare(a, b []byte) int {
|
||||
// TODO: skip identical prefixes once we have a fast way to detect if a rune is
|
||||
// part of a contraction. This would lead to roughly a 10% speedup for the colcmp regtest.
|
||||
c.iter(0).SetInput(a)
|
||||
c.iter(1).SetInput(b)
|
||||
if res := c.compare(); res != 0 {
|
||||
return res
|
||||
}
|
||||
if !c.ignore[colltab.Identity] {
|
||||
return bytes.Compare(a, b)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// CompareString returns an integer comparing the two strings.
|
||||
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
|
||||
func (c *Collator) CompareString(a, b string) int {
|
||||
// TODO: skip identical prefixes once we have a fast way to detect if a rune is
|
||||
// part of a contraction. This would lead to roughly a 10% speedup for the colcmp regtest.
|
||||
c.iter(0).SetInputString(a)
|
||||
c.iter(1).SetInputString(b)
|
||||
if res := c.compare(); res != 0 {
|
||||
return res
|
||||
}
|
||||
if !c.ignore[colltab.Identity] {
|
||||
if a < b {
|
||||
return -1
|
||||
} else if a > b {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func compareLevel(f func(i *iter) int, a, b *iter) int {
|
||||
a.pce = 0
|
||||
b.pce = 0
|
||||
for {
|
||||
va := f(a)
|
||||
vb := f(b)
|
||||
if va != vb {
|
||||
if va < vb {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
} else if va == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *Collator) compare() int {
|
||||
ia, ib := c.iter(0), c.iter(1)
|
||||
// Process primary level
|
||||
if c.alternate != altShifted {
|
||||
// TODO: implement script reordering
|
||||
if res := compareLevel((*iter).nextPrimary, ia, ib); res != 0 {
|
||||
return res
|
||||
}
|
||||
} else {
|
||||
// TODO: handle shifted
|
||||
}
|
||||
if !c.ignore[colltab.Secondary] {
|
||||
f := (*iter).nextSecondary
|
||||
if c.backwards {
|
||||
f = (*iter).prevSecondary
|
||||
}
|
||||
if res := compareLevel(f, ia, ib); res != 0 {
|
||||
return res
|
||||
}
|
||||
}
|
||||
// TODO: special case handling (Danish?)
|
||||
if !c.ignore[colltab.Tertiary] || c.caseLevel {
|
||||
if res := compareLevel((*iter).nextTertiary, ia, ib); res != 0 {
|
||||
return res
|
||||
}
|
||||
if !c.ignore[colltab.Quaternary] {
|
||||
if res := compareLevel((*iter).nextQuaternary, ia, ib); res != 0 {
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Key returns the collation key for str.
|
||||
// Passing the buffer buf may avoid memory allocations.
|
||||
// The returned slice will point to an allocation in Buffer and will remain
|
||||
// valid until the next call to buf.Reset().
|
||||
func (c *Collator) Key(buf *Buffer, str []byte) []byte {
|
||||
// See http://www.unicode.org/reports/tr10/#Main_Algorithm for more details.
|
||||
buf.init()
|
||||
return c.key(buf, c.getColElems(str))
|
||||
}
|
||||
|
||||
// KeyFromString returns the collation key for str.
|
||||
// Passing the buffer buf may avoid memory allocations.
|
||||
// The returned slice will point to an allocation in Buffer and will retain
|
||||
// valid until the next call to buf.ResetKeys().
|
||||
func (c *Collator) KeyFromString(buf *Buffer, str string) []byte {
|
||||
// See http://www.unicode.org/reports/tr10/#Main_Algorithm for more details.
|
||||
buf.init()
|
||||
return c.key(buf, c.getColElemsString(str))
|
||||
}
|
||||
|
||||
func (c *Collator) key(buf *Buffer, w []colltab.Elem) []byte {
|
||||
processWeights(c.alternate, c.t.Top(), w)
|
||||
kn := len(buf.key)
|
||||
c.keyFromElems(buf, w)
|
||||
return buf.key[kn:]
|
||||
}
|
||||
|
||||
func (c *Collator) getColElems(str []byte) []colltab.Elem {
|
||||
i := c.iter(0)
|
||||
i.SetInput(str)
|
||||
for i.Next() {
|
||||
}
|
||||
return i.Elems
|
||||
}
|
||||
|
||||
func (c *Collator) getColElemsString(str string) []colltab.Elem {
|
||||
i := c.iter(0)
|
||||
i.SetInputString(str)
|
||||
for i.Next() {
|
||||
}
|
||||
return i.Elems
|
||||
}
|
||||
|
||||
type iter struct {
|
||||
wa [512]colltab.Elem
|
||||
|
||||
newcolltab.Iter
|
||||
pce int
|
||||
}
|
||||
|
||||
func (i *iter) init(c *Collator) {
|
||||
i.Weighter = c.t
|
||||
i.Elems = i.wa[:0]
|
||||
}
|
||||
|
||||
func (i *iter) nextPrimary() int {
|
||||
for {
|
||||
for ; i.pce < i.N; i.pce++ {
|
||||
if v := i.Elems[i.pce].Primary(); v != 0 {
|
||||
i.pce++
|
||||
return v
|
||||
}
|
||||
}
|
||||
if !i.Next() {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
panic("should not reach here")
|
||||
}
|
||||
|
||||
func (i *iter) nextSecondary() int {
|
||||
for ; i.pce < len(i.Elems); i.pce++ {
|
||||
if v := i.Elems[i.pce].Secondary(); v != 0 {
|
||||
i.pce++
|
||||
return v
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (i *iter) prevSecondary() int {
|
||||
for ; i.pce < len(i.Elems); i.pce++ {
|
||||
if v := i.Elems[len(i.Elems)-i.pce-1].Secondary(); v != 0 {
|
||||
i.pce++
|
||||
return v
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (i *iter) nextTertiary() int {
|
||||
for ; i.pce < len(i.Elems); i.pce++ {
|
||||
if v := i.Elems[i.pce].Tertiary(); v != 0 {
|
||||
i.pce++
|
||||
return int(v)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (i *iter) nextQuaternary() int {
|
||||
for ; i.pce < len(i.Elems); i.pce++ {
|
||||
if v := i.Elems[i.pce].Quaternary(); v != 0 {
|
||||
i.pce++
|
||||
return v
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func appendPrimary(key []byte, p int) []byte {
|
||||
// Convert to variable length encoding; supports up to 23 bits.
|
||||
if p <= 0x7FFF {
|
||||
key = append(key, uint8(p>>8), uint8(p))
|
||||
} else {
|
||||
key = append(key, uint8(p>>16)|0x80, uint8(p>>8), uint8(p))
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// keyFromElems converts the weights ws to a compact sequence of bytes.
|
||||
// The result will be appended to the byte buffer in buf.
|
||||
func (c *Collator) keyFromElems(buf *Buffer, ws []colltab.Elem) {
|
||||
for _, v := range ws {
|
||||
if w := v.Primary(); w > 0 {
|
||||
buf.key = appendPrimary(buf.key, w)
|
||||
}
|
||||
}
|
||||
if !c.ignore[colltab.Secondary] {
|
||||
buf.key = append(buf.key, 0, 0)
|
||||
// TODO: we can use one 0 if we can guarantee that all non-zero weights are > 0xFF.
|
||||
if !c.backwards {
|
||||
for _, v := range ws {
|
||||
if w := v.Secondary(); w > 0 {
|
||||
buf.key = append(buf.key, uint8(w>>8), uint8(w))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := len(ws) - 1; i >= 0; i-- {
|
||||
if w := ws[i].Secondary(); w > 0 {
|
||||
buf.key = append(buf.key, uint8(w>>8), uint8(w))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if c.caseLevel {
|
||||
buf.key = append(buf.key, 0, 0)
|
||||
}
|
||||
if !c.ignore[colltab.Tertiary] || c.caseLevel {
|
||||
buf.key = append(buf.key, 0, 0)
|
||||
for _, v := range ws {
|
||||
if w := v.Tertiary(); w > 0 {
|
||||
buf.key = append(buf.key, uint8(w))
|
||||
}
|
||||
}
|
||||
// Derive the quaternary weights from the options and other levels.
|
||||
// Note that we represent MaxQuaternary as 0xFF. The first byte of the
|
||||
// representation of a primary weight is always smaller than 0xFF,
|
||||
// so using this single byte value will compare correctly.
|
||||
if !c.ignore[colltab.Quaternary] && c.alternate >= altShifted {
|
||||
if c.alternate == altShiftTrimmed {
|
||||
lastNonFFFF := len(buf.key)
|
||||
buf.key = append(buf.key, 0)
|
||||
for _, v := range ws {
|
||||
if w := v.Quaternary(); w == colltab.MaxQuaternary {
|
||||
buf.key = append(buf.key, 0xFF)
|
||||
} else if w > 0 {
|
||||
buf.key = appendPrimary(buf.key, w)
|
||||
lastNonFFFF = len(buf.key)
|
||||
}
|
||||
}
|
||||
buf.key = buf.key[:lastNonFFFF]
|
||||
} else {
|
||||
buf.key = append(buf.key, 0)
|
||||
for _, v := range ws {
|
||||
if w := v.Quaternary(); w == colltab.MaxQuaternary {
|
||||
buf.key = append(buf.key, 0xFF)
|
||||
} else if w > 0 {
|
||||
buf.key = appendPrimary(buf.key, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func processWeights(vw alternateHandling, top uint32, wa []colltab.Elem) {
|
||||
ignore := false
|
||||
vtop := int(top)
|
||||
switch vw {
|
||||
case altShifted, altShiftTrimmed:
|
||||
for i := range wa {
|
||||
if p := wa[i].Primary(); p <= vtop && p != 0 {
|
||||
wa[i] = colltab.MakeQuaternary(p)
|
||||
ignore = true
|
||||
} else if p == 0 {
|
||||
if ignore {
|
||||
wa[i] = colltab.Ignore
|
||||
}
|
||||
} else {
|
||||
ignore = false
|
||||
}
|
||||
}
|
||||
case altBlanked:
|
||||
for i := range wa {
|
||||
if p := wa[i].Primary(); p <= vtop && (ignore || p != 0) {
|
||||
wa[i] = colltab.Ignore
|
||||
ignore = true
|
||||
} else {
|
||||
ignore = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
482
vendor/golang.org/x/text/collate/collate_test.go
generated
vendored
Normal file
482
vendor/golang.org/x/text/collate/collate_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
// Copyright 2012 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 collate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/collate/colltab"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
type weightsTest struct {
|
||||
opt opts
|
||||
in, out ColElems
|
||||
}
|
||||
|
||||
type opts struct {
|
||||
lev int
|
||||
alt alternateHandling
|
||||
top int
|
||||
|
||||
backwards bool
|
||||
caseLevel bool
|
||||
}
|
||||
|
||||
// ignore returns an initialized boolean array based on the given Level.
|
||||
// A negative value means using the default setting of quaternary.
|
||||
func ignore(level colltab.Level) (ignore [colltab.NumLevels]bool) {
|
||||
if level < 0 {
|
||||
level = colltab.Quaternary
|
||||
}
|
||||
for i := range ignore {
|
||||
ignore[i] = level < colltab.Level(i)
|
||||
}
|
||||
return ignore
|
||||
}
|
||||
|
||||
func makeCE(w []int) colltab.Elem {
|
||||
ce, err := colltab.MakeElem(w[0], w[1], w[2], uint8(w[3]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ce
|
||||
}
|
||||
|
||||
func (o opts) collator() *Collator {
|
||||
c := &Collator{
|
||||
options: options{
|
||||
ignore: ignore(colltab.Level(o.lev - 1)),
|
||||
alternate: o.alt,
|
||||
backwards: o.backwards,
|
||||
caseLevel: o.caseLevel,
|
||||
variableTop: uint32(o.top),
|
||||
},
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
const (
|
||||
maxQ = 0x1FFFFF
|
||||
)
|
||||
|
||||
func wpq(p, q int) Weights {
|
||||
return W(p, defaults.Secondary, defaults.Tertiary, q)
|
||||
}
|
||||
|
||||
func wsq(s, q int) Weights {
|
||||
return W(0, s, defaults.Tertiary, q)
|
||||
}
|
||||
|
||||
func wq(q int) Weights {
|
||||
return W(0, 0, 0, q)
|
||||
}
|
||||
|
||||
var zero = W(0, 0, 0, 0)
|
||||
|
||||
var processTests = []weightsTest{
|
||||
// Shifted
|
||||
{ // simple sequence of non-variables
|
||||
opt: opts{alt: altShifted, top: 100},
|
||||
in: ColElems{W(200), W(300), W(400)},
|
||||
out: ColElems{wpq(200, maxQ), wpq(300, maxQ), wpq(400, maxQ)},
|
||||
},
|
||||
{ // first is a variable
|
||||
opt: opts{alt: altShifted, top: 250},
|
||||
in: ColElems{W(200), W(300), W(400)},
|
||||
out: ColElems{wq(200), wpq(300, maxQ), wpq(400, maxQ)},
|
||||
},
|
||||
{ // all but first are variable
|
||||
opt: opts{alt: altShifted, top: 999},
|
||||
in: ColElems{W(1000), W(200), W(300), W(400)},
|
||||
out: ColElems{wpq(1000, maxQ), wq(200), wq(300), wq(400)},
|
||||
},
|
||||
{ // first is a modifier
|
||||
opt: opts{alt: altShifted, top: 999},
|
||||
in: ColElems{W(0, 10), W(1000)},
|
||||
out: ColElems{wsq(10, maxQ), wpq(1000, maxQ)},
|
||||
},
|
||||
{ // primary ignorables
|
||||
opt: opts{alt: altShifted, top: 250},
|
||||
in: ColElems{W(200), W(0, 10), W(300), W(0, 15), W(400)},
|
||||
out: ColElems{wq(200), zero, wpq(300, maxQ), wsq(15, maxQ), wpq(400, maxQ)},
|
||||
},
|
||||
{ // secondary ignorables
|
||||
opt: opts{alt: altShifted, top: 250},
|
||||
in: ColElems{W(200), W(0, 0, 10), W(300), W(0, 0, 15), W(400)},
|
||||
out: ColElems{wq(200), zero, wpq(300, maxQ), W(0, 0, 15, maxQ), wpq(400, maxQ)},
|
||||
},
|
||||
{ // tertiary ignorables, no change
|
||||
opt: opts{alt: altShifted, top: 250},
|
||||
in: ColElems{W(200), zero, W(300), zero, W(400)},
|
||||
out: ColElems{wq(200), zero, wpq(300, maxQ), zero, wpq(400, maxQ)},
|
||||
},
|
||||
|
||||
// ShiftTrimmed (same as Shifted)
|
||||
{ // simple sequence of non-variables
|
||||
opt: opts{alt: altShiftTrimmed, top: 100},
|
||||
in: ColElems{W(200), W(300), W(400)},
|
||||
out: ColElems{wpq(200, maxQ), wpq(300, maxQ), wpq(400, maxQ)},
|
||||
},
|
||||
{ // first is a variable
|
||||
opt: opts{alt: altShiftTrimmed, top: 250},
|
||||
in: ColElems{W(200), W(300), W(400)},
|
||||
out: ColElems{wq(200), wpq(300, maxQ), wpq(400, maxQ)},
|
||||
},
|
||||
{ // all but first are variable
|
||||
opt: opts{alt: altShiftTrimmed, top: 999},
|
||||
in: ColElems{W(1000), W(200), W(300), W(400)},
|
||||
out: ColElems{wpq(1000, maxQ), wq(200), wq(300), wq(400)},
|
||||
},
|
||||
{ // first is a modifier
|
||||
opt: opts{alt: altShiftTrimmed, top: 999},
|
||||
in: ColElems{W(0, 10), W(1000)},
|
||||
out: ColElems{wsq(10, maxQ), wpq(1000, maxQ)},
|
||||
},
|
||||
{ // primary ignorables
|
||||
opt: opts{alt: altShiftTrimmed, top: 250},
|
||||
in: ColElems{W(200), W(0, 10), W(300), W(0, 15), W(400)},
|
||||
out: ColElems{wq(200), zero, wpq(300, maxQ), wsq(15, maxQ), wpq(400, maxQ)},
|
||||
},
|
||||
{ // secondary ignorables
|
||||
opt: opts{alt: altShiftTrimmed, top: 250},
|
||||
in: ColElems{W(200), W(0, 0, 10), W(300), W(0, 0, 15), W(400)},
|
||||
out: ColElems{wq(200), zero, wpq(300, maxQ), W(0, 0, 15, maxQ), wpq(400, maxQ)},
|
||||
},
|
||||
{ // tertiary ignorables, no change
|
||||
opt: opts{alt: altShiftTrimmed, top: 250},
|
||||
in: ColElems{W(200), zero, W(300), zero, W(400)},
|
||||
out: ColElems{wq(200), zero, wpq(300, maxQ), zero, wpq(400, maxQ)},
|
||||
},
|
||||
|
||||
// Blanked
|
||||
{ // simple sequence of non-variables
|
||||
opt: opts{alt: altBlanked, top: 100},
|
||||
in: ColElems{W(200), W(300), W(400)},
|
||||
out: ColElems{W(200), W(300), W(400)},
|
||||
},
|
||||
{ // first is a variable
|
||||
opt: opts{alt: altBlanked, top: 250},
|
||||
in: ColElems{W(200), W(300), W(400)},
|
||||
out: ColElems{zero, W(300), W(400)},
|
||||
},
|
||||
{ // all but first are variable
|
||||
opt: opts{alt: altBlanked, top: 999},
|
||||
in: ColElems{W(1000), W(200), W(300), W(400)},
|
||||
out: ColElems{W(1000), zero, zero, zero},
|
||||
},
|
||||
{ // first is a modifier
|
||||
opt: opts{alt: altBlanked, top: 999},
|
||||
in: ColElems{W(0, 10), W(1000)},
|
||||
out: ColElems{W(0, 10), W(1000)},
|
||||
},
|
||||
{ // primary ignorables
|
||||
opt: opts{alt: altBlanked, top: 250},
|
||||
in: ColElems{W(200), W(0, 10), W(300), W(0, 15), W(400)},
|
||||
out: ColElems{zero, zero, W(300), W(0, 15), W(400)},
|
||||
},
|
||||
{ // secondary ignorables
|
||||
opt: opts{alt: altBlanked, top: 250},
|
||||
in: ColElems{W(200), W(0, 0, 10), W(300), W(0, 0, 15), W(400)},
|
||||
out: ColElems{zero, zero, W(300), W(0, 0, 15), W(400)},
|
||||
},
|
||||
{ // tertiary ignorables, no change
|
||||
opt: opts{alt: altBlanked, top: 250},
|
||||
in: ColElems{W(200), zero, W(300), zero, W(400)},
|
||||
out: ColElems{zero, zero, W(300), zero, W(400)},
|
||||
},
|
||||
|
||||
// Non-ignorable: input is always equal to output.
|
||||
{ // all but first are variable
|
||||
opt: opts{alt: altNonIgnorable, top: 999},
|
||||
in: ColElems{W(1000), W(200), W(300), W(400)},
|
||||
out: ColElems{W(1000), W(200), W(300), W(400)},
|
||||
},
|
||||
{ // primary ignorables
|
||||
opt: opts{alt: altNonIgnorable, top: 250},
|
||||
in: ColElems{W(200), W(0, 10), W(300), W(0, 15), W(400)},
|
||||
out: ColElems{W(200), W(0, 10), W(300), W(0, 15), W(400)},
|
||||
},
|
||||
{ // secondary ignorables
|
||||
opt: opts{alt: altNonIgnorable, top: 250},
|
||||
in: ColElems{W(200), W(0, 0, 10), W(300), W(0, 0, 15), W(400)},
|
||||
out: ColElems{W(200), W(0, 0, 10), W(300), W(0, 0, 15), W(400)},
|
||||
},
|
||||
{ // tertiary ignorables, no change
|
||||
opt: opts{alt: altNonIgnorable, top: 250},
|
||||
in: ColElems{W(200), zero, W(300), zero, W(400)},
|
||||
out: ColElems{W(200), zero, W(300), zero, W(400)},
|
||||
},
|
||||
}
|
||||
|
||||
func TestProcessWeights(t *testing.T) {
|
||||
for i, tt := range processTests {
|
||||
in := convertFromWeights(tt.in)
|
||||
out := convertFromWeights(tt.out)
|
||||
processWeights(tt.opt.alt, uint32(tt.opt.top), in)
|
||||
for j, w := range in {
|
||||
if w != out[j] {
|
||||
t.Errorf("%d: Weights %d was %v; want %v", i, j, w, out[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type keyFromElemTest struct {
|
||||
opt opts
|
||||
in ColElems
|
||||
out []byte
|
||||
}
|
||||
|
||||
var defS = byte(defaults.Secondary)
|
||||
var defT = byte(defaults.Tertiary)
|
||||
|
||||
const sep = 0 // separator byte
|
||||
|
||||
var keyFromElemTests = []keyFromElemTest{
|
||||
{ // simple primary and secondary weights.
|
||||
opts{alt: altShifted},
|
||||
ColElems{W(0x200), W(0x7FFF), W(0, 0x30), W(0x100)},
|
||||
[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
|
||||
sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
|
||||
sep, sep, defT, defT, defT, defT, // tertiary
|
||||
sep, 0xFF, 0xFF, 0xFF, 0xFF, // quaternary
|
||||
},
|
||||
},
|
||||
{ // same as first, but with zero element that need to be removed
|
||||
opts{alt: altShifted},
|
||||
ColElems{W(0x200), zero, W(0x7FFF), W(0, 0x30), zero, W(0x100)},
|
||||
[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
|
||||
sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
|
||||
sep, sep, defT, defT, defT, defT, // tertiary
|
||||
sep, 0xFF, 0xFF, 0xFF, 0xFF, // quaternary
|
||||
},
|
||||
},
|
||||
{ // same as first, with large primary values
|
||||
opts{alt: altShifted},
|
||||
ColElems{W(0x200), W(0x8000), W(0, 0x30), W(0x12345)},
|
||||
[]byte{0x2, 0, 0x80, 0x80, 0x00, 0x81, 0x23, 0x45, // primary
|
||||
sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
|
||||
sep, sep, defT, defT, defT, defT, // tertiary
|
||||
sep, 0xFF, 0xFF, 0xFF, 0xFF, // quaternary
|
||||
},
|
||||
},
|
||||
{ // same as first, but with the secondary level backwards
|
||||
opts{alt: altShifted, backwards: true},
|
||||
ColElems{W(0x200), W(0x7FFF), W(0, 0x30), W(0x100)},
|
||||
[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
|
||||
sep, sep, 0, defS, 0, 0x30, 0, defS, 0, defS, // secondary
|
||||
sep, sep, defT, defT, defT, defT, // tertiary
|
||||
sep, 0xFF, 0xFF, 0xFF, 0xFF, // quaternary
|
||||
},
|
||||
},
|
||||
{ // same as first, ignoring quaternary level
|
||||
opts{alt: altShifted, lev: 3},
|
||||
ColElems{W(0x200), zero, W(0x7FFF), W(0, 0x30), zero, W(0x100)},
|
||||
[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
|
||||
sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
|
||||
sep, sep, defT, defT, defT, defT, // tertiary
|
||||
},
|
||||
},
|
||||
{ // same as first, ignoring tertiary level
|
||||
opts{alt: altShifted, lev: 2},
|
||||
ColElems{W(0x200), zero, W(0x7FFF), W(0, 0x30), zero, W(0x100)},
|
||||
[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
|
||||
sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
|
||||
},
|
||||
},
|
||||
{ // same as first, ignoring secondary level
|
||||
opts{alt: altShifted, lev: 1},
|
||||
ColElems{W(0x200), zero, W(0x7FFF), W(0, 0x30), zero, W(0x100)},
|
||||
[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00},
|
||||
},
|
||||
{ // simple primary and secondary weights.
|
||||
opts{alt: altShiftTrimmed, top: 0x250},
|
||||
ColElems{W(0x300), W(0x200), W(0x7FFF), W(0, 0x30), W(0x800)},
|
||||
[]byte{0x3, 0, 0x7F, 0xFF, 0x8, 0x00, // primary
|
||||
sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
|
||||
sep, sep, defT, defT, defT, defT, // tertiary
|
||||
sep, 0xFF, 0x2, 0, // quaternary
|
||||
},
|
||||
},
|
||||
{ // as first, primary with case level enabled
|
||||
opts{alt: altShifted, lev: 1, caseLevel: true},
|
||||
ColElems{W(0x200), W(0x7FFF), W(0, 0x30), W(0x100)},
|
||||
[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
|
||||
sep, sep, // secondary
|
||||
sep, sep, defT, defT, defT, defT, // tertiary
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestKeyFromElems(t *testing.T) {
|
||||
buf := Buffer{}
|
||||
for i, tt := range keyFromElemTests {
|
||||
buf.Reset()
|
||||
in := convertFromWeights(tt.in)
|
||||
processWeights(tt.opt.alt, uint32(tt.opt.top), in)
|
||||
tt.opt.collator().keyFromElems(&buf, in)
|
||||
res := buf.key
|
||||
if len(res) != len(tt.out) {
|
||||
t.Errorf("%d: len(ws) was %d; want %d (%X should be %X)", i, len(res), len(tt.out), res, tt.out)
|
||||
}
|
||||
n := len(res)
|
||||
if len(tt.out) < n {
|
||||
n = len(tt.out)
|
||||
}
|
||||
for j, c := range res[:n] {
|
||||
if c != tt.out[j] {
|
||||
t.Errorf("%d: byte %d was %X; want %X", i, j, c, tt.out[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetColElems(t *testing.T) {
|
||||
for i, tt := range appendNextTests {
|
||||
c, err := makeTable(tt.in)
|
||||
if err != nil {
|
||||
// error is reported in TestAppendNext
|
||||
continue
|
||||
}
|
||||
// Create one large test per table
|
||||
str := make([]byte, 0, 4000)
|
||||
out := ColElems{}
|
||||
for len(str) < 3000 {
|
||||
for _, chk := range tt.chk {
|
||||
str = append(str, chk.in[:chk.n]...)
|
||||
out = append(out, chk.out...)
|
||||
}
|
||||
}
|
||||
for j, chk := range append(tt.chk, check{string(str), len(str), out}) {
|
||||
out := convertFromWeights(chk.out)
|
||||
ce := c.getColElems([]byte(chk.in)[:chk.n])
|
||||
if len(ce) != len(out) {
|
||||
t.Errorf("%d:%d: len(ws) was %d; want %d", i, j, len(ce), len(out))
|
||||
continue
|
||||
}
|
||||
cnt := 0
|
||||
for k, w := range ce {
|
||||
w, _ = colltab.MakeElem(w.Primary(), w.Secondary(), int(w.Tertiary()), 0)
|
||||
if w != out[k] {
|
||||
t.Errorf("%d:%d: Weights %d was %X; want %X", i, j, k, w, out[k])
|
||||
cnt++
|
||||
}
|
||||
if cnt > 10 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type keyTest struct {
|
||||
in string
|
||||
out []byte
|
||||
}
|
||||
|
||||
var keyTests = []keyTest{
|
||||
{"abc",
|
||||
[]byte{0, 100, 0, 200, 1, 44, 0, 0, 0, 32, 0, 32, 0, 32, 0, 0, 2, 2, 2, 0, 255, 255, 255},
|
||||
},
|
||||
{"a\u0301",
|
||||
[]byte{0, 102, 0, 0, 0, 32, 0, 0, 2, 0, 255},
|
||||
},
|
||||
{"aaaaa",
|
||||
[]byte{0, 100, 0, 100, 0, 100, 0, 100, 0, 100, 0, 0,
|
||||
0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 0,
|
||||
2, 2, 2, 2, 2, 0,
|
||||
255, 255, 255, 255, 255,
|
||||
},
|
||||
},
|
||||
// Issue 16391: incomplete rune at end of UTF-8 sequence.
|
||||
{"\xc2", []byte{133, 255, 253, 0, 0, 0, 32, 0, 0, 2, 0, 255}},
|
||||
{"\xc2a", []byte{133, 255, 253, 0, 100, 0, 0, 0, 32, 0, 32, 0, 0, 2, 2, 0, 255, 255}},
|
||||
}
|
||||
|
||||
func TestKey(t *testing.T) {
|
||||
c, _ := makeTable(appendNextTests[4].in)
|
||||
c.alternate = altShifted
|
||||
c.ignore = ignore(colltab.Quaternary)
|
||||
buf := Buffer{}
|
||||
keys1 := [][]byte{}
|
||||
keys2 := [][]byte{}
|
||||
for _, tt := range keyTests {
|
||||
keys1 = append(keys1, c.Key(&buf, []byte(tt.in)))
|
||||
keys2 = append(keys2, c.KeyFromString(&buf, tt.in))
|
||||
}
|
||||
// Separate generation from testing to ensure buffers are not overwritten.
|
||||
for i, tt := range keyTests {
|
||||
if !bytes.Equal(keys1[i], tt.out) {
|
||||
t.Errorf("%d: Key(%q) = %d; want %d", i, tt.in, keys1[i], tt.out)
|
||||
}
|
||||
if !bytes.Equal(keys2[i], tt.out) {
|
||||
t.Errorf("%d: KeyFromString(%q) = %d; want %d", i, tt.in, keys2[i], tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type compareTest struct {
|
||||
a, b string
|
||||
res int // comparison result
|
||||
}
|
||||
|
||||
var compareTests = []compareTest{
|
||||
{"a\u0301", "a", 1},
|
||||
{"a\u0301b", "ab", 1},
|
||||
{"a", "a\u0301", -1},
|
||||
{"ab", "a\u0301b", -1},
|
||||
{"bc", "a\u0301c", 1},
|
||||
{"ab", "aB", -1},
|
||||
{"a\u0301", "a\u0301", 0},
|
||||
{"a", "a", 0},
|
||||
// Only clip prefixes of whole runes.
|
||||
{"\u302E", "\u302F", 1},
|
||||
// Don't clip prefixes when last rune of prefix may be part of contraction.
|
||||
{"a\u035E", "a\u0301\u035F", -1},
|
||||
{"a\u0301\u035Fb", "a\u0301\u035F", -1},
|
||||
}
|
||||
|
||||
func TestCompare(t *testing.T) {
|
||||
c, _ := makeTable(appendNextTests[4].in)
|
||||
for i, tt := range compareTests {
|
||||
if res := c.Compare([]byte(tt.a), []byte(tt.b)); res != tt.res {
|
||||
t.Errorf("%d: Compare(%q, %q) == %d; want %d", i, tt.a, tt.b, res, tt.res)
|
||||
}
|
||||
if res := c.CompareString(tt.a, tt.b); res != tt.res {
|
||||
t.Errorf("%d: CompareString(%q, %q) == %d; want %d", i, tt.a, tt.b, res, tt.res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumeric(t *testing.T) {
|
||||
c := New(language.English, Loose, Numeric)
|
||||
|
||||
for i, tt := range []struct {
|
||||
a, b string
|
||||
want int
|
||||
}{
|
||||
{"1", "2", -1},
|
||||
{"2", "12", -1},
|
||||
{"2", "12", -1}, // Fullwidth is sorted as usual.
|
||||
{"₂", "₁₂", 1}, // Subscript is not sorted as numbers.
|
||||
{"②", "①②", 1}, // Circled is not sorted as numbers.
|
||||
{ // Imperial Aramaic, is not sorted as number.
|
||||
"\U00010859",
|
||||
"\U00010858\U00010859",
|
||||
1,
|
||||
},
|
||||
{"12", "2", 1},
|
||||
{"A-1", "A-2", -1},
|
||||
{"A-2", "A-12", -1},
|
||||
{"A-12", "A-2", 1},
|
||||
{"A-0001", "A-1", 0},
|
||||
} {
|
||||
if got := c.CompareString(tt.a, tt.b); got != tt.want {
|
||||
t.Errorf("%d: CompareString(%s, %s) = %d; want %d", i, tt.a, tt.b, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
121
vendor/golang.org/x/text/collate/colltab/collate_test.go
generated
vendored
Normal file
121
vendor/golang.org/x/text/collate/colltab/collate_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright 2014 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 colltab_test
|
||||
|
||||
// This file contains tests which need to import package collate, which causes
|
||||
// an import cycle when done within package colltab itself.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/collate"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/unicode/rangetable"
|
||||
)
|
||||
|
||||
// assigned is used to only test runes that are inside the scope of the Unicode
|
||||
// version used to generation the collation table.
|
||||
var assigned = rangetable.Assigned(collate.UnicodeVersion)
|
||||
|
||||
func TestNonDigits(t *testing.T) {
|
||||
c := collate.New(language.English, collate.Loose, collate.Numeric)
|
||||
|
||||
// Verify that all non-digit numbers sort outside of the number range.
|
||||
for r, hi := rune(unicode.N.R16[0].Lo), rune(unicode.N.R32[0].Hi); r <= hi; r++ {
|
||||
if unicode.In(r, unicode.Nd) || !unicode.In(r, assigned) {
|
||||
continue
|
||||
}
|
||||
if a := string(r); c.CompareString(a, "0") != -1 && c.CompareString(a, "999999") != 1 {
|
||||
t.Errorf("%+q non-digit number is collated as digit", a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericCompare(t *testing.T) {
|
||||
c := collate.New(language.English, collate.Loose, collate.Numeric)
|
||||
|
||||
// Iterate over all digits.
|
||||
for _, r16 := range unicode.Nd.R16 {
|
||||
testDigitCompare(t, c, rune(r16.Lo), rune(r16.Hi))
|
||||
}
|
||||
for _, r32 := range unicode.Nd.R32 {
|
||||
testDigitCompare(t, c, rune(r32.Lo), rune(r32.Hi))
|
||||
}
|
||||
}
|
||||
|
||||
func testDigitCompare(t *testing.T, c *collate.Collator, zero, nine rune) {
|
||||
if !unicode.In(zero, assigned) {
|
||||
return
|
||||
}
|
||||
n := int(nine - zero + 1)
|
||||
if n%10 != 0 {
|
||||
t.Fatalf("len([%+q, %+q]) = %d; want a multiple of 10", zero, nine, n)
|
||||
}
|
||||
for _, tt := range []struct {
|
||||
prefix string
|
||||
b [11]string
|
||||
}{
|
||||
{
|
||||
prefix: "",
|
||||
b: [11]string{
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
|
||||
},
|
||||
},
|
||||
{
|
||||
prefix: "1",
|
||||
b: [11]string{
|
||||
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
|
||||
},
|
||||
},
|
||||
{
|
||||
prefix: "0",
|
||||
b: [11]string{
|
||||
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10",
|
||||
},
|
||||
},
|
||||
{
|
||||
prefix: "00",
|
||||
b: [11]string{
|
||||
"000", "001", "002", "003", "004", "005", "006", "007", "008", "009", "010",
|
||||
},
|
||||
},
|
||||
{
|
||||
prefix: "9",
|
||||
b: [11]string{
|
||||
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100",
|
||||
},
|
||||
},
|
||||
} {
|
||||
for k := 0; k <= n; k++ {
|
||||
i := k % 10
|
||||
a := tt.prefix + string(zero+rune(i))
|
||||
for j, b := range tt.b {
|
||||
want := 0
|
||||
switch {
|
||||
case i < j:
|
||||
want = -1
|
||||
case i > j:
|
||||
want = 1
|
||||
}
|
||||
got := c.CompareString(a, b)
|
||||
if got != want {
|
||||
t.Errorf("Compare(%+q, %+q) = %d; want %d", a, b, got, want)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNumericWeighter(b *testing.B) {
|
||||
c := collate.New(language.English, collate.Numeric)
|
||||
input := bytes.Repeat([]byte("Testing, testing 123..."), 100)
|
||||
b.SetBytes(int64(2 * len(input)))
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Compare(input, input)
|
||||
}
|
||||
}
|
||||
371
vendor/golang.org/x/text/collate/colltab/collelem.go
generated
vendored
Normal file
371
vendor/golang.org/x/text/collate/colltab/collelem.go
generated
vendored
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
// Copyright 2012 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 colltab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Level identifies the collation comparison level.
|
||||
// The primary level corresponds to the basic sorting of text.
|
||||
// The secondary level corresponds to accents and related linguistic elements.
|
||||
// The tertiary level corresponds to casing and related concepts.
|
||||
// The quaternary level is derived from the other levels by the
|
||||
// various algorithms for handling variable elements.
|
||||
type Level int
|
||||
|
||||
const (
|
||||
Primary Level = iota
|
||||
Secondary
|
||||
Tertiary
|
||||
Quaternary
|
||||
Identity
|
||||
|
||||
NumLevels
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSecondary = 0x20
|
||||
defaultTertiary = 0x2
|
||||
maxTertiary = 0x1F
|
||||
MaxQuaternary = 0x1FFFFF // 21 bits.
|
||||
)
|
||||
|
||||
// Elem is a representation of a collation element. This API provides ways to encode
|
||||
// and decode Elems. Implementations of collation tables may use values greater
|
||||
// or equal to PrivateUse for their own purposes. However, these should never be
|
||||
// returned by AppendNext.
|
||||
type Elem uint32
|
||||
|
||||
const (
|
||||
maxCE Elem = 0xAFFFFFFF
|
||||
PrivateUse = minContract
|
||||
minContract = 0xC0000000
|
||||
maxContract = 0xDFFFFFFF
|
||||
minExpand = 0xE0000000
|
||||
maxExpand = 0xEFFFFFFF
|
||||
minDecomp = 0xF0000000
|
||||
)
|
||||
|
||||
type ceType int
|
||||
|
||||
const (
|
||||
ceNormal ceType = iota // ceNormal includes implicits (ce == 0)
|
||||
ceContractionIndex // rune can be a start of a contraction
|
||||
ceExpansionIndex // rune expands into a sequence of collation elements
|
||||
ceDecompose // rune expands using NFKC decomposition
|
||||
)
|
||||
|
||||
func (ce Elem) ctype() ceType {
|
||||
if ce <= maxCE {
|
||||
return ceNormal
|
||||
}
|
||||
if ce <= maxContract {
|
||||
return ceContractionIndex
|
||||
} else {
|
||||
if ce <= maxExpand {
|
||||
return ceExpansionIndex
|
||||
}
|
||||
return ceDecompose
|
||||
}
|
||||
panic("should not reach here")
|
||||
return ceType(-1)
|
||||
}
|
||||
|
||||
// For normal collation elements, we assume that a collation element either has
|
||||
// a primary or non-default secondary value, not both.
|
||||
// Collation elements with a primary value are of the form
|
||||
// 01pppppp pppppppp ppppppp0 ssssssss
|
||||
// - p* is primary collation value
|
||||
// - s* is the secondary collation value
|
||||
// 00pppppp pppppppp ppppppps sssttttt, where
|
||||
// - p* is primary collation value
|
||||
// - s* offset of secondary from default value.
|
||||
// - t* is the tertiary collation value
|
||||
// 100ttttt cccccccc pppppppp pppppppp
|
||||
// - t* is the tertiar collation value
|
||||
// - c* is the cannonical combining class
|
||||
// - p* is the primary collation value
|
||||
// Collation elements with a secondary value are of the form
|
||||
// 1010cccc ccccssss ssssssss tttttttt, where
|
||||
// - c* is the canonical combining class
|
||||
// - s* is the secondary collation value
|
||||
// - t* is the tertiary collation value
|
||||
// 11qqqqqq qqqqqqqq qqqqqqq0 00000000
|
||||
// - q* quaternary value
|
||||
const (
|
||||
ceTypeMask = 0xC0000000
|
||||
ceTypeMaskExt = 0xE0000000
|
||||
ceIgnoreMask = 0xF00FFFFF
|
||||
ceType1 = 0x40000000
|
||||
ceType2 = 0x00000000
|
||||
ceType3or4 = 0x80000000
|
||||
ceType4 = 0xA0000000
|
||||
ceTypeQ = 0xC0000000
|
||||
Ignore = ceType4
|
||||
firstNonPrimary = 0x80000000
|
||||
lastSpecialPrimary = 0xA0000000
|
||||
secondaryMask = 0x80000000
|
||||
hasTertiaryMask = 0x40000000
|
||||
primaryValueMask = 0x3FFFFE00
|
||||
maxPrimaryBits = 21
|
||||
compactPrimaryBits = 16
|
||||
maxSecondaryBits = 12
|
||||
maxTertiaryBits = 8
|
||||
maxCCCBits = 8
|
||||
maxSecondaryCompactBits = 8
|
||||
maxSecondaryDiffBits = 4
|
||||
maxTertiaryCompactBits = 5
|
||||
primaryShift = 9
|
||||
compactSecondaryShift = 5
|
||||
minCompactSecondary = defaultSecondary - 4
|
||||
)
|
||||
|
||||
func makeImplicitCE(primary int) Elem {
|
||||
return ceType1 | Elem(primary<<primaryShift) | defaultSecondary
|
||||
}
|
||||
|
||||
// MakeElem returns an Elem for the given values. It will return an error
|
||||
// if the given combination of values is invalid.
|
||||
func MakeElem(primary, secondary, tertiary int, ccc uint8) (Elem, error) {
|
||||
if w := primary; w >= 1<<maxPrimaryBits || w < 0 {
|
||||
return 0, fmt.Errorf("makeCE: primary weight out of bounds: %x >= %x", w, 1<<maxPrimaryBits)
|
||||
}
|
||||
if w := secondary; w >= 1<<maxSecondaryBits || w < 0 {
|
||||
return 0, fmt.Errorf("makeCE: secondary weight out of bounds: %x >= %x", w, 1<<maxSecondaryBits)
|
||||
}
|
||||
if w := tertiary; w >= 1<<maxTertiaryBits || w < 0 {
|
||||
return 0, fmt.Errorf("makeCE: tertiary weight out of bounds: %x >= %x", w, 1<<maxTertiaryBits)
|
||||
}
|
||||
ce := Elem(0)
|
||||
if primary != 0 {
|
||||
if ccc != 0 {
|
||||
if primary >= 1<<compactPrimaryBits {
|
||||
return 0, fmt.Errorf("makeCE: primary weight with non-zero CCC out of bounds: %x >= %x", primary, 1<<compactPrimaryBits)
|
||||
}
|
||||
if secondary != defaultSecondary {
|
||||
return 0, fmt.Errorf("makeCE: cannot combine non-default secondary value (%x) with non-zero CCC (%x)", secondary, ccc)
|
||||
}
|
||||
ce = Elem(tertiary << (compactPrimaryBits + maxCCCBits))
|
||||
ce |= Elem(ccc) << compactPrimaryBits
|
||||
ce |= Elem(primary)
|
||||
ce |= ceType3or4
|
||||
} else if tertiary == defaultTertiary {
|
||||
if secondary >= 1<<maxSecondaryCompactBits {
|
||||
return 0, fmt.Errorf("makeCE: secondary weight with non-zero primary out of bounds: %x >= %x", secondary, 1<<maxSecondaryCompactBits)
|
||||
}
|
||||
ce = Elem(primary<<(maxSecondaryCompactBits+1) + secondary)
|
||||
ce |= ceType1
|
||||
} else {
|
||||
d := secondary - defaultSecondary + maxSecondaryDiffBits
|
||||
if d >= 1<<maxSecondaryDiffBits || d < 0 {
|
||||
return 0, fmt.Errorf("makeCE: secondary weight diff out of bounds: %x < 0 || %x > %x", d, d, 1<<maxSecondaryDiffBits)
|
||||
}
|
||||
if tertiary >= 1<<maxTertiaryCompactBits {
|
||||
return 0, fmt.Errorf("makeCE: tertiary weight with non-zero primary out of bounds: %x > %x", tertiary, 1<<maxTertiaryCompactBits)
|
||||
}
|
||||
ce = Elem(primary<<maxSecondaryDiffBits + d)
|
||||
ce = ce<<maxTertiaryCompactBits + Elem(tertiary)
|
||||
}
|
||||
} else {
|
||||
ce = Elem(secondary<<maxTertiaryBits + tertiary)
|
||||
ce += Elem(ccc) << (maxSecondaryBits + maxTertiaryBits)
|
||||
ce |= ceType4
|
||||
}
|
||||
return ce, nil
|
||||
}
|
||||
|
||||
// MakeQuaternary returns an Elem with the given quaternary value.
|
||||
func MakeQuaternary(v int) Elem {
|
||||
return ceTypeQ | Elem(v<<primaryShift)
|
||||
}
|
||||
|
||||
// Mask sets weights for any level smaller than l to 0.
|
||||
// The resulting Elem can be used to test for equality with
|
||||
// other Elems to which the same mask has been applied.
|
||||
func (ce Elem) Mask(l Level) uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// CCC returns the canonical combining class associated with the underlying character,
|
||||
// if applicable, or 0 otherwise.
|
||||
func (ce Elem) CCC() uint8 {
|
||||
if ce&ceType3or4 != 0 {
|
||||
if ce&ceType4 == ceType3or4 {
|
||||
return uint8(ce >> 16)
|
||||
}
|
||||
return uint8(ce >> 20)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Primary returns the primary collation weight for ce.
|
||||
func (ce Elem) Primary() int {
|
||||
if ce >= firstNonPrimary {
|
||||
if ce > lastSpecialPrimary {
|
||||
return 0
|
||||
}
|
||||
return int(uint16(ce))
|
||||
}
|
||||
return int(ce&primaryValueMask) >> primaryShift
|
||||
}
|
||||
|
||||
// Secondary returns the secondary collation weight for ce.
|
||||
func (ce Elem) Secondary() int {
|
||||
switch ce & ceTypeMask {
|
||||
case ceType1:
|
||||
return int(uint8(ce))
|
||||
case ceType2:
|
||||
return minCompactSecondary + int((ce>>compactSecondaryShift)&0xF)
|
||||
case ceType3or4:
|
||||
if ce < ceType4 {
|
||||
return defaultSecondary
|
||||
}
|
||||
return int(ce>>8) & 0xFFF
|
||||
case ceTypeQ:
|
||||
return 0
|
||||
}
|
||||
panic("should not reach here")
|
||||
}
|
||||
|
||||
// Tertiary returns the tertiary collation weight for ce.
|
||||
func (ce Elem) Tertiary() uint8 {
|
||||
if ce&hasTertiaryMask == 0 {
|
||||
if ce&ceType3or4 == 0 {
|
||||
return uint8(ce & 0x1F)
|
||||
}
|
||||
if ce&ceType4 == ceType4 {
|
||||
return uint8(ce)
|
||||
}
|
||||
return uint8(ce>>24) & 0x1F // type 2
|
||||
} else if ce&ceTypeMask == ceType1 {
|
||||
return defaultTertiary
|
||||
}
|
||||
// ce is a quaternary value.
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ce Elem) updateTertiary(t uint8) Elem {
|
||||
if ce&ceTypeMask == ceType1 {
|
||||
// convert to type 4
|
||||
nce := ce & primaryValueMask
|
||||
nce |= Elem(uint8(ce)-minCompactSecondary) << compactSecondaryShift
|
||||
ce = nce
|
||||
} else if ce&ceTypeMaskExt == ceType3or4 {
|
||||
ce &= ^Elem(maxTertiary << 24)
|
||||
return ce | (Elem(t) << 24)
|
||||
} else {
|
||||
// type 2 or 4
|
||||
ce &= ^Elem(maxTertiary)
|
||||
}
|
||||
return ce | Elem(t)
|
||||
}
|
||||
|
||||
// Quaternary returns the quaternary value if explicitly specified,
|
||||
// 0 if ce == Ignore, or MaxQuaternary otherwise.
|
||||
// Quaternary values are used only for shifted variants.
|
||||
func (ce Elem) Quaternary() int {
|
||||
if ce&ceTypeMask == ceTypeQ {
|
||||
return int(ce&primaryValueMask) >> primaryShift
|
||||
} else if ce&ceIgnoreMask == Ignore {
|
||||
return 0
|
||||
}
|
||||
return MaxQuaternary
|
||||
}
|
||||
|
||||
// Weight returns the collation weight for the given level.
|
||||
func (ce Elem) Weight(l Level) int {
|
||||
switch l {
|
||||
case Primary:
|
||||
return ce.Primary()
|
||||
case Secondary:
|
||||
return ce.Secondary()
|
||||
case Tertiary:
|
||||
return int(ce.Tertiary())
|
||||
case Quaternary:
|
||||
return ce.Quaternary()
|
||||
}
|
||||
return 0 // return 0 (ignore) for undefined levels.
|
||||
}
|
||||
|
||||
// For contractions, collation elements are of the form
|
||||
// 110bbbbb bbbbbbbb iiiiiiii iiiinnnn, where
|
||||
// - n* is the size of the first node in the contraction trie.
|
||||
// - i* is the index of the first node in the contraction trie.
|
||||
// - b* is the offset into the contraction collation element table.
|
||||
// See contract.go for details on the contraction trie.
|
||||
const (
|
||||
maxNBits = 4
|
||||
maxTrieIndexBits = 12
|
||||
maxContractOffsetBits = 13
|
||||
)
|
||||
|
||||
func splitContractIndex(ce Elem) (index, n, offset int) {
|
||||
n = int(ce & (1<<maxNBits - 1))
|
||||
ce >>= maxNBits
|
||||
index = int(ce & (1<<maxTrieIndexBits - 1))
|
||||
ce >>= maxTrieIndexBits
|
||||
offset = int(ce & (1<<maxContractOffsetBits - 1))
|
||||
return
|
||||
}
|
||||
|
||||
// For expansions, Elems are of the form 11100000 00000000 bbbbbbbb bbbbbbbb,
|
||||
// where b* is the index into the expansion sequence table.
|
||||
const maxExpandIndexBits = 16
|
||||
|
||||
func splitExpandIndex(ce Elem) (index int) {
|
||||
return int(uint16(ce))
|
||||
}
|
||||
|
||||
// Some runes can be expanded using NFKD decomposition. Instead of storing the full
|
||||
// sequence of collation elements, we decompose the rune and lookup the collation
|
||||
// elements for each rune in the decomposition and modify the tertiary weights.
|
||||
// The Elem, in this case, is of the form 11110000 00000000 wwwwwwww vvvvvvvv, where
|
||||
// - v* is the replacement tertiary weight for the first rune,
|
||||
// - w* is the replacement tertiary weight for the second rune,
|
||||
// Tertiary weights of subsequent runes should be replaced with maxTertiary.
|
||||
// See http://www.unicode.org/reports/tr10/#Compatibility_Decompositions for more details.
|
||||
func splitDecompose(ce Elem) (t1, t2 uint8) {
|
||||
return uint8(ce), uint8(ce >> 8)
|
||||
}
|
||||
|
||||
const (
|
||||
// These constants were taken from http://www.unicode.org/versions/Unicode6.0.0/ch12.pdf.
|
||||
minUnified rune = 0x4E00
|
||||
maxUnified = 0x9FFF
|
||||
minCompatibility = 0xF900
|
||||
maxCompatibility = 0xFAFF
|
||||
minRare = 0x3400
|
||||
maxRare = 0x4DBF
|
||||
)
|
||||
const (
|
||||
commonUnifiedOffset = 0x10000
|
||||
rareUnifiedOffset = 0x20000 // largest rune in common is U+FAFF
|
||||
otherOffset = 0x50000 // largest rune in rare is U+2FA1D
|
||||
illegalOffset = otherOffset + int(unicode.MaxRune)
|
||||
maxPrimary = illegalOffset + 1
|
||||
)
|
||||
|
||||
// implicitPrimary returns the primary weight for the a rune
|
||||
// for which there is no entry for the rune in the collation table.
|
||||
// We take a different approach from the one specified in
|
||||
// http://unicode.org/reports/tr10/#Implicit_Weights,
|
||||
// but preserve the resulting relative ordering of the runes.
|
||||
func implicitPrimary(r rune) int {
|
||||
if unicode.Is(unicode.Ideographic, r) {
|
||||
if r >= minUnified && r <= maxUnified {
|
||||
// The most common case for CJK.
|
||||
return int(r) + commonUnifiedOffset
|
||||
}
|
||||
if r >= minCompatibility && r <= maxCompatibility {
|
||||
// This will typically not hit. The DUCET explicitly specifies mappings
|
||||
// for all characters that do not decompose.
|
||||
return int(r) + commonUnifiedOffset
|
||||
}
|
||||
return int(r) + rareUnifiedOffset
|
||||
}
|
||||
return int(r) + otherOffset
|
||||
}
|
||||
183
vendor/golang.org/x/text/collate/colltab/collelem_test.go
generated
vendored
Normal file
183
vendor/golang.org/x/text/collate/colltab/collelem_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
// Copyright 2012 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 colltab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func (e Elem) String() string {
|
||||
q := ""
|
||||
if v := e.Quaternary(); v == MaxQuaternary {
|
||||
q = "max"
|
||||
} else {
|
||||
q = fmt.Sprint(v)
|
||||
}
|
||||
return fmt.Sprintf("[%d, %d, %d, %s]",
|
||||
e.Primary(),
|
||||
e.Secondary(),
|
||||
e.Tertiary(),
|
||||
q)
|
||||
}
|
||||
|
||||
type ceTest struct {
|
||||
f func(inout []int) (Elem, ceType)
|
||||
arg []int
|
||||
}
|
||||
|
||||
func makeCE(weights []int) Elem {
|
||||
ce, _ := MakeElem(weights[0], weights[1], weights[2], uint8(weights[3]))
|
||||
return ce
|
||||
}
|
||||
|
||||
var defaultValues = []int{0, defaultSecondary, defaultTertiary, 0}
|
||||
|
||||
func e(w ...int) Elem {
|
||||
return makeCE(append(w, defaultValues[len(w):]...))
|
||||
}
|
||||
|
||||
func makeContractIndex(index, n, offset int) Elem {
|
||||
const (
|
||||
contractID = 0xC0000000
|
||||
maxNBits = 4
|
||||
maxTrieIndexBits = 12
|
||||
maxContractOffsetBits = 13
|
||||
)
|
||||
ce := Elem(contractID)
|
||||
ce += Elem(offset << (maxNBits + maxTrieIndexBits))
|
||||
ce += Elem(index << maxNBits)
|
||||
ce += Elem(n)
|
||||
return ce
|
||||
}
|
||||
|
||||
func makeExpandIndex(index int) Elem {
|
||||
const expandID = 0xE0000000
|
||||
return expandID + Elem(index)
|
||||
}
|
||||
|
||||
func makeDecompose(t1, t2 int) Elem {
|
||||
const decompID = 0xF0000000
|
||||
return Elem(t2<<8+t1) + decompID
|
||||
}
|
||||
|
||||
func normalCE(inout []int) (ce Elem, t ceType) {
|
||||
ce = makeCE(inout)
|
||||
inout[0] = ce.Primary()
|
||||
inout[1] = ce.Secondary()
|
||||
inout[2] = int(ce.Tertiary())
|
||||
inout[3] = int(ce.CCC())
|
||||
return ce, ceNormal
|
||||
}
|
||||
|
||||
func expandCE(inout []int) (ce Elem, t ceType) {
|
||||
ce = makeExpandIndex(inout[0])
|
||||
inout[0] = splitExpandIndex(ce)
|
||||
return ce, ceExpansionIndex
|
||||
}
|
||||
|
||||
func contractCE(inout []int) (ce Elem, t ceType) {
|
||||
ce = makeContractIndex(inout[0], inout[1], inout[2])
|
||||
i, n, o := splitContractIndex(ce)
|
||||
inout[0], inout[1], inout[2] = i, n, o
|
||||
return ce, ceContractionIndex
|
||||
}
|
||||
|
||||
func decompCE(inout []int) (ce Elem, t ceType) {
|
||||
ce = makeDecompose(inout[0], inout[1])
|
||||
t1, t2 := splitDecompose(ce)
|
||||
inout[0], inout[1] = int(t1), int(t2)
|
||||
return ce, ceDecompose
|
||||
}
|
||||
|
||||
var ceTests = []ceTest{
|
||||
{normalCE, []int{0, 0, 0, 0}},
|
||||
{normalCE, []int{0, 30, 3, 0}},
|
||||
{normalCE, []int{0, 30, 3, 0xFF}},
|
||||
{normalCE, []int{100, defaultSecondary, defaultTertiary, 0}},
|
||||
{normalCE, []int{100, defaultSecondary, defaultTertiary, 0xFF}},
|
||||
{normalCE, []int{100, defaultSecondary, 3, 0}},
|
||||
{normalCE, []int{0x123, defaultSecondary, 8, 0xFF}},
|
||||
|
||||
{contractCE, []int{0, 0, 0}},
|
||||
{contractCE, []int{1, 1, 1}},
|
||||
{contractCE, []int{1, (1 << maxNBits) - 1, 1}},
|
||||
{contractCE, []int{(1 << maxTrieIndexBits) - 1, 1, 1}},
|
||||
{contractCE, []int{1, 1, (1 << maxContractOffsetBits) - 1}},
|
||||
|
||||
{expandCE, []int{0}},
|
||||
{expandCE, []int{5}},
|
||||
{expandCE, []int{(1 << maxExpandIndexBits) - 1}},
|
||||
|
||||
{decompCE, []int{0, 0}},
|
||||
{decompCE, []int{1, 1}},
|
||||
{decompCE, []int{0x1F, 0x1F}},
|
||||
}
|
||||
|
||||
func TestColElem(t *testing.T) {
|
||||
for i, tt := range ceTests {
|
||||
inout := make([]int, len(tt.arg))
|
||||
copy(inout, tt.arg)
|
||||
ce, typ := tt.f(inout)
|
||||
if ce.ctype() != typ {
|
||||
t.Errorf("%d: type is %d; want %d (ColElem: %X)", i, ce.ctype(), typ, ce)
|
||||
}
|
||||
for j, a := range tt.arg {
|
||||
if inout[j] != a {
|
||||
t.Errorf("%d: argument %d is %X; want %X (ColElem: %X)", i, j, inout[j], a, ce)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type implicitTest struct {
|
||||
r rune
|
||||
p int
|
||||
}
|
||||
|
||||
var implicitTests = []implicitTest{
|
||||
{0x33FF, 0x533FF},
|
||||
{0x3400, 0x23400},
|
||||
{0x4DC0, 0x54DC0},
|
||||
{0x4DFF, 0x54DFF},
|
||||
{0x4E00, 0x14E00},
|
||||
{0x9FCB, 0x19FCB},
|
||||
{0xA000, 0x5A000},
|
||||
{0xF8FF, 0x5F8FF},
|
||||
{0xF900, 0x1F900},
|
||||
{0xFA23, 0x1FA23},
|
||||
{0xFAD9, 0x1FAD9},
|
||||
{0xFB00, 0x5FB00},
|
||||
{0x20000, 0x40000},
|
||||
{0x2B81C, 0x4B81C},
|
||||
{unicode.MaxRune, 0x15FFFF}, // maximum primary value
|
||||
}
|
||||
|
||||
func TestImplicit(t *testing.T) {
|
||||
for _, tt := range implicitTests {
|
||||
if p := implicitPrimary(tt.r); p != tt.p {
|
||||
t.Errorf("%U: was %X; want %X", tt.r, p, tt.p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateTertiary(t *testing.T) {
|
||||
tests := []struct {
|
||||
in, out Elem
|
||||
t uint8
|
||||
}{
|
||||
{0x4000FE20, 0x0000FE8A, 0x0A},
|
||||
{0x4000FE21, 0x0000FEAA, 0x0A},
|
||||
{0x0000FE8B, 0x0000FE83, 0x03},
|
||||
{0x82FF0188, 0x9BFF0188, 0x1B},
|
||||
{0xAFF0CC02, 0xAFF0CC1B, 0x1B},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
if out := tt.in.updateTertiary(tt.t); out != tt.out {
|
||||
t.Errorf("%d: was %X; want %X", i, out, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
31
vendor/golang.org/x/text/collate/colltab/colltab.go
generated
vendored
Normal file
31
vendor/golang.org/x/text/collate/colltab/colltab.go
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// 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 colltab // import "golang.org/x/text/collate/colltab"
|
||||
|
||||
// A Weighter can be used as a source for Collator and Searcher.
|
||||
type Weighter interface {
|
||||
// Start finds the start of the segment that includes position p.
|
||||
Start(p int, b []byte) int
|
||||
|
||||
// StartString finds the start of the segment that includes position p.
|
||||
StartString(p int, s string) int
|
||||
|
||||
// AppendNext appends Elems to buf corresponding to the longest match
|
||||
// of a single character or contraction from the start of s.
|
||||
// It returns the new buf and the number of bytes consumed.
|
||||
AppendNext(buf []Elem, s []byte) (ce []Elem, n int)
|
||||
|
||||
// AppendNextString appends Elems to buf corresponding to the longest match
|
||||
// of a single character or contraction from the start of s.
|
||||
// It returns the new buf and the number of bytes consumed.
|
||||
AppendNextString(buf []Elem, s string) (ce []Elem, n int)
|
||||
|
||||
// Domain returns a slice of all single characters and contractions for which
|
||||
// collation elements are defined in this table.
|
||||
Domain() []string
|
||||
|
||||
// Top returns the highest variable primary value.
|
||||
Top() uint32
|
||||
}
|
||||
42
vendor/golang.org/x/text/collate/colltab/colltab_test.go
generated
vendored
Normal file
42
vendor/golang.org/x/text/collate/colltab/colltab_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2014 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 colltab
|
||||
|
||||
// testWeighter is a simple Weighter that returns weights from a user-defined map.
|
||||
type testWeighter map[string][]Elem
|
||||
|
||||
func (t testWeighter) Start(int, []byte) int { return 0 }
|
||||
func (t testWeighter) StartString(int, string) int { return 0 }
|
||||
func (t testWeighter) Domain() []string { return nil }
|
||||
func (t testWeighter) Top() uint32 { return 0 }
|
||||
|
||||
// maxContractBytes is the maximum length of any key in the map.
|
||||
const maxContractBytes = 10
|
||||
|
||||
func (t testWeighter) AppendNext(buf []Elem, s []byte) ([]Elem, int) {
|
||||
n := len(s)
|
||||
if n > maxContractBytes {
|
||||
n = maxContractBytes
|
||||
}
|
||||
for i := n; i > 0; i-- {
|
||||
if e, ok := t[string(s[:i])]; ok {
|
||||
return append(buf, e...), i
|
||||
}
|
||||
}
|
||||
panic("incomplete testWeighter: could not find " + string(s))
|
||||
}
|
||||
|
||||
func (t testWeighter) AppendNextString(buf []Elem, s string) ([]Elem, int) {
|
||||
n := len(s)
|
||||
if n > maxContractBytes {
|
||||
n = maxContractBytes
|
||||
}
|
||||
for i := n; i > 0; i-- {
|
||||
if e, ok := t[s[:i]]; ok {
|
||||
return append(buf, e...), i
|
||||
}
|
||||
}
|
||||
panic("incomplete testWeighter: could not find " + s)
|
||||
}
|
||||
145
vendor/golang.org/x/text/collate/colltab/contract.go
generated
vendored
Normal file
145
vendor/golang.org/x/text/collate/colltab/contract.go
generated
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2012 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 colltab
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
// For a description of contractTrieSet, see exp/locale/collate/build/contract.go.
|
||||
|
||||
type contractTrieSet []struct{ l, h, n, i uint8 }
|
||||
|
||||
// ctScanner is used to match a trie to an input sequence.
|
||||
// A contraction may match a non-contiguous sequence of bytes in an input string.
|
||||
// For example, if there is a contraction for <a, combining_ring>, it should match
|
||||
// the sequence <a, combining_cedilla, combining_ring>, as combining_cedilla does
|
||||
// not block combining_ring.
|
||||
// ctScanner does not automatically skip over non-blocking non-starters, but rather
|
||||
// retains the state of the last match and leaves it up to the user to continue
|
||||
// the match at the appropriate points.
|
||||
type ctScanner struct {
|
||||
states contractTrieSet
|
||||
s []byte
|
||||
n int
|
||||
index int
|
||||
pindex int
|
||||
done bool
|
||||
}
|
||||
|
||||
type ctScannerString struct {
|
||||
states contractTrieSet
|
||||
s string
|
||||
n int
|
||||
index int
|
||||
pindex int
|
||||
done bool
|
||||
}
|
||||
|
||||
func (t contractTrieSet) scanner(index, n int, b []byte) ctScanner {
|
||||
return ctScanner{s: b, states: t[index:], n: n}
|
||||
}
|
||||
|
||||
func (t contractTrieSet) scannerString(index, n int, str string) ctScannerString {
|
||||
return ctScannerString{s: str, states: t[index:], n: n}
|
||||
}
|
||||
|
||||
// result returns the offset i and bytes consumed p so far. If no suffix
|
||||
// matched, i and p will be 0.
|
||||
func (s *ctScanner) result() (i, p int) {
|
||||
return s.index, s.pindex
|
||||
}
|
||||
|
||||
func (s *ctScannerString) result() (i, p int) {
|
||||
return s.index, s.pindex
|
||||
}
|
||||
|
||||
const (
|
||||
final = 0
|
||||
noIndex = 0xFF
|
||||
)
|
||||
|
||||
// scan matches the longest suffix at the current location in the input
|
||||
// and returns the number of bytes consumed.
|
||||
func (s *ctScanner) scan(p int) int {
|
||||
pr := p // the p at the rune start
|
||||
str := s.s
|
||||
states, n := s.states, s.n
|
||||
for i := 0; i < n && p < len(str); {
|
||||
e := states[i]
|
||||
c := str[p]
|
||||
// TODO: a significant number of contractions are of a form that
|
||||
// cannot match discontiguous UTF-8 in a normalized string. We could let
|
||||
// a negative value of e.n mean that we can set s.done = true and avoid
|
||||
// the need for additional matches.
|
||||
if c >= e.l {
|
||||
if e.l == c {
|
||||
p++
|
||||
if e.i != noIndex {
|
||||
s.index = int(e.i)
|
||||
s.pindex = p
|
||||
}
|
||||
if e.n != final {
|
||||
i, states, n = 0, states[int(e.h)+n:], int(e.n)
|
||||
if p >= len(str) || utf8.RuneStart(str[p]) {
|
||||
s.states, s.n, pr = states, n, p
|
||||
}
|
||||
} else {
|
||||
s.done = true
|
||||
return p
|
||||
}
|
||||
continue
|
||||
} else if e.n == final && c <= e.h {
|
||||
p++
|
||||
s.done = true
|
||||
s.index = int(c-e.l) + int(e.i)
|
||||
s.pindex = p
|
||||
return p
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return pr
|
||||
}
|
||||
|
||||
// scan is a verbatim copy of ctScanner.scan.
|
||||
func (s *ctScannerString) scan(p int) int {
|
||||
pr := p // the p at the rune start
|
||||
str := s.s
|
||||
states, n := s.states, s.n
|
||||
for i := 0; i < n && p < len(str); {
|
||||
e := states[i]
|
||||
c := str[p]
|
||||
// TODO: a significant number of contractions are of a form that
|
||||
// cannot match discontiguous UTF-8 in a normalized string. We could let
|
||||
// a negative value of e.n mean that we can set s.done = true and avoid
|
||||
// the need for additional matches.
|
||||
if c >= e.l {
|
||||
if e.l == c {
|
||||
p++
|
||||
if e.i != noIndex {
|
||||
s.index = int(e.i)
|
||||
s.pindex = p
|
||||
}
|
||||
if e.n != final {
|
||||
i, states, n = 0, states[int(e.h)+n:], int(e.n)
|
||||
if p >= len(str) || utf8.RuneStart(str[p]) {
|
||||
s.states, s.n, pr = states, n, p
|
||||
}
|
||||
} else {
|
||||
s.done = true
|
||||
return p
|
||||
}
|
||||
continue
|
||||
} else if e.n == final && c <= e.h {
|
||||
p++
|
||||
s.done = true
|
||||
s.index = int(c-e.l) + int(e.i)
|
||||
s.pindex = p
|
||||
return p
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return pr
|
||||
}
|
||||
132
vendor/golang.org/x/text/collate/colltab/contract_test.go
generated
vendored
Normal file
132
vendor/golang.org/x/text/collate/colltab/contract_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// Copyright 2012 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 colltab
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type lookupStrings struct {
|
||||
str string
|
||||
offset int
|
||||
n int // bytes consumed from input
|
||||
}
|
||||
|
||||
type LookupTest struct {
|
||||
lookup []lookupStrings
|
||||
n int
|
||||
tries contractTrieSet
|
||||
}
|
||||
|
||||
var lookupTests = []LookupTest{
|
||||
{[]lookupStrings{
|
||||
{"abc", 1, 3},
|
||||
{"a", 0, 0},
|
||||
{"b", 0, 0},
|
||||
{"c", 0, 0},
|
||||
{"d", 0, 0},
|
||||
},
|
||||
1,
|
||||
contractTrieSet{
|
||||
{'a', 0, 1, 0xFF},
|
||||
{'b', 0, 1, 0xFF},
|
||||
{'c', 'c', 0, 1},
|
||||
},
|
||||
},
|
||||
{[]lookupStrings{
|
||||
{"abc", 1, 3},
|
||||
{"abd", 2, 3},
|
||||
{"abe", 3, 3},
|
||||
{"a", 0, 0},
|
||||
{"ab", 0, 0},
|
||||
{"d", 0, 0},
|
||||
{"f", 0, 0},
|
||||
},
|
||||
1,
|
||||
contractTrieSet{
|
||||
{'a', 0, 1, 0xFF},
|
||||
{'b', 0, 1, 0xFF},
|
||||
{'c', 'e', 0, 1},
|
||||
},
|
||||
},
|
||||
{[]lookupStrings{
|
||||
{"abc", 1, 3},
|
||||
{"ab", 2, 2},
|
||||
{"a", 3, 1},
|
||||
{"abcd", 1, 3},
|
||||
{"abe", 2, 2},
|
||||
},
|
||||
1,
|
||||
contractTrieSet{
|
||||
{'a', 0, 1, 3},
|
||||
{'b', 0, 1, 2},
|
||||
{'c', 'c', 0, 1},
|
||||
},
|
||||
},
|
||||
{[]lookupStrings{
|
||||
{"abc", 1, 3},
|
||||
{"abd", 2, 3},
|
||||
{"ab", 3, 2},
|
||||
{"ac", 4, 2},
|
||||
{"a", 5, 1},
|
||||
{"b", 6, 1},
|
||||
{"ba", 6, 1},
|
||||
},
|
||||
2,
|
||||
contractTrieSet{
|
||||
{'b', 'b', 0, 6},
|
||||
{'a', 0, 2, 5},
|
||||
{'c', 'c', 0, 4},
|
||||
{'b', 0, 1, 3},
|
||||
{'c', 'd', 0, 1},
|
||||
},
|
||||
},
|
||||
{[]lookupStrings{
|
||||
{"bcde", 2, 4},
|
||||
{"bc", 7, 2},
|
||||
{"ab", 6, 2},
|
||||
{"bcd", 5, 3},
|
||||
{"abcd", 1, 4},
|
||||
{"abc", 4, 3},
|
||||
{"bcdf", 3, 4},
|
||||
},
|
||||
2,
|
||||
contractTrieSet{
|
||||
{'b', 3, 1, 0xFF},
|
||||
{'a', 0, 1, 0xFF},
|
||||
{'b', 0, 1, 6},
|
||||
{'c', 0, 1, 4},
|
||||
{'d', 'd', 0, 1},
|
||||
{'c', 0, 1, 7},
|
||||
{'d', 0, 1, 5},
|
||||
{'e', 'f', 0, 2},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func lookup(c *contractTrieSet, nnode int, s []uint8) (i, n int) {
|
||||
scan := c.scanner(0, nnode, s)
|
||||
scan.scan(0)
|
||||
return scan.result()
|
||||
}
|
||||
|
||||
func TestLookupContraction(t *testing.T) {
|
||||
for i, tt := range lookupTests {
|
||||
cts := contractTrieSet(tt.tries)
|
||||
for j, lu := range tt.lookup {
|
||||
str := lu.str
|
||||
for _, s := range []string{str, str + "X"} {
|
||||
const msg = `%d:%d: %s of "%s" %v; want %v`
|
||||
offset, n := lookup(&cts, tt.n, []byte(s))
|
||||
if offset != lu.offset {
|
||||
t.Errorf(msg, i, j, "offset", s, offset, lu.offset)
|
||||
}
|
||||
if n != lu.n {
|
||||
t.Errorf(msg, i, j, "bytes consumed", s, n, len(str))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
vendor/golang.org/x/text/collate/colltab/export.go
generated
vendored
Normal file
36
vendor/golang.org/x/text/collate/colltab/export.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2012 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 colltab
|
||||
|
||||
// Init is for internal use only.
|
||||
func Init(data interface{}) Weighter {
|
||||
init, ok := data.(tableInitializer)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
t := &table{}
|
||||
loff, voff := init.FirstBlockOffsets()
|
||||
t.index.index = init.TrieIndex()
|
||||
t.index.index0 = t.index.index[blockSize*int(loff):]
|
||||
t.index.values = init.TrieValues()
|
||||
t.index.values0 = t.index.values[blockSize*int(voff):]
|
||||
t.expandElem = init.ExpandElems()
|
||||
t.contractTries = init.ContractTries()
|
||||
t.contractElem = init.ContractElems()
|
||||
t.maxContractLen = init.MaxContractLen()
|
||||
t.variableTop = init.VariableTop()
|
||||
return t
|
||||
}
|
||||
|
||||
type tableInitializer interface {
|
||||
TrieIndex() []uint16
|
||||
TrieValues() []uint32
|
||||
FirstBlockOffsets() (lookup, value uint16)
|
||||
ExpandElems() []uint32
|
||||
ContractTries() []struct{ l, h, n, i uint8 }
|
||||
ContractElems() []uint32
|
||||
MaxContractLen() int
|
||||
VariableTop() uint32
|
||||
}
|
||||
236
vendor/golang.org/x/text/collate/colltab/numeric.go
generated
vendored
Normal file
236
vendor/golang.org/x/text/collate/colltab/numeric.go
generated
vendored
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
// Copyright 2014 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 colltab
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// NewNumericWeighter wraps w to replace individual digits to sort based on their
|
||||
// numeric value.
|
||||
//
|
||||
// Weighter w must have a free primary weight after the primary weight for 9.
|
||||
// If this is not the case, numeric value will sort at the same primary level
|
||||
// as the first primary sorting after 9.
|
||||
func NewNumericWeighter(w Weighter) Weighter {
|
||||
getElem := func(s string) Elem {
|
||||
elems, _ := w.AppendNextString(nil, s)
|
||||
return elems[0]
|
||||
}
|
||||
nine := getElem("9")
|
||||
|
||||
// Numbers should order before zero, but the DUCET has no room for this.
|
||||
// TODO: move before zero once we use fractional collation elements.
|
||||
ns, _ := MakeElem(nine.Primary()+1, nine.Secondary(), int(nine.Tertiary()), 0)
|
||||
|
||||
return &numericWeighter{
|
||||
Weighter: w,
|
||||
|
||||
// We assume that w sorts digits of different kinds in order of numeric
|
||||
// value and that the tertiary weight order is preserved.
|
||||
//
|
||||
// TODO: evaluate whether it is worth basing the ranges on the Elem
|
||||
// encoding itself once the move to fractional weights is complete.
|
||||
zero: getElem("0"),
|
||||
zeroSpecialLo: getElem("0"), // U+FF10 FULLWIDTH DIGIT ZERO
|
||||
zeroSpecialHi: getElem("₀"), // U+2080 SUBSCRIPT ZERO
|
||||
nine: nine,
|
||||
nineSpecialHi: getElem("₉"), // U+2089 SUBSCRIPT NINE
|
||||
numberStart: ns,
|
||||
}
|
||||
}
|
||||
|
||||
// A numericWeighter translates a stream of digits into a stream of weights
|
||||
// representing the numeric value.
|
||||
type numericWeighter struct {
|
||||
Weighter
|
||||
|
||||
// The Elems below all demarcate boundaries of specific ranges. With the
|
||||
// current element encoding digits are in two ranges: normal (default
|
||||
// tertiary value) and special. For most languages, digits have collation
|
||||
// elements in the normal range.
|
||||
//
|
||||
// Note: the range tests are very specific for the element encoding used by
|
||||
// this implementation. The tests in collate_test.go are designed to fail
|
||||
// if this code is not updated when an encoding has changed.
|
||||
|
||||
zero Elem // normal digit zero
|
||||
zeroSpecialLo Elem // special digit zero, low tertiary value
|
||||
zeroSpecialHi Elem // special digit zero, high tertiary value
|
||||
nine Elem // normal digit nine
|
||||
nineSpecialHi Elem // special digit nine
|
||||
numberStart Elem
|
||||
}
|
||||
|
||||
// AppendNext calls the namesake of the underlying weigher, but replaces single
|
||||
// digits with weights representing their value.
|
||||
func (nw *numericWeighter) AppendNext(buf []Elem, s []byte) (ce []Elem, n int) {
|
||||
ce, n = nw.Weighter.AppendNext(buf, s)
|
||||
nc := numberConverter{
|
||||
elems: buf,
|
||||
w: nw,
|
||||
b: s,
|
||||
}
|
||||
isZero, ok := nc.checkNextDigit(ce)
|
||||
if !ok {
|
||||
return ce, n
|
||||
}
|
||||
// ce might have been grown already, so take it instead of buf.
|
||||
nc.init(ce, len(buf), isZero)
|
||||
for n < len(s) {
|
||||
ce, sz := nw.Weighter.AppendNext(nc.elems, s[n:])
|
||||
nc.b = s
|
||||
n += sz
|
||||
if !nc.update(ce) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nc.result(), n
|
||||
}
|
||||
|
||||
// AppendNextString calls the namesake of the underlying weigher, but replaces
|
||||
// single digits with weights representing their value.
|
||||
func (nw *numericWeighter) AppendNextString(buf []Elem, s string) (ce []Elem, n int) {
|
||||
ce, n = nw.Weighter.AppendNextString(buf, s)
|
||||
nc := numberConverter{
|
||||
elems: buf,
|
||||
w: nw,
|
||||
s: s,
|
||||
}
|
||||
isZero, ok := nc.checkNextDigit(ce)
|
||||
if !ok {
|
||||
return ce, n
|
||||
}
|
||||
nc.init(ce, len(buf), isZero)
|
||||
for n < len(s) {
|
||||
ce, sz := nw.Weighter.AppendNextString(nc.elems, s[n:])
|
||||
nc.s = s
|
||||
n += sz
|
||||
if !nc.update(ce) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nc.result(), n
|
||||
}
|
||||
|
||||
type numberConverter struct {
|
||||
w *numericWeighter
|
||||
|
||||
elems []Elem
|
||||
nDigits int
|
||||
lenIndex int
|
||||
|
||||
s string // set if the input was of type string
|
||||
b []byte // set if the input was of type []byte
|
||||
}
|
||||
|
||||
// init completes initialization of a numberConverter and prepares it for adding
|
||||
// more digits. elems is assumed to have a digit starting at oldLen.
|
||||
func (nc *numberConverter) init(elems []Elem, oldLen int, isZero bool) {
|
||||
// Insert a marker indicating the start of a number and and a placeholder
|
||||
// for the number of digits.
|
||||
if isZero {
|
||||
elems = append(elems[:oldLen], nc.w.numberStart, 0)
|
||||
} else {
|
||||
elems = append(elems, 0, 0)
|
||||
copy(elems[oldLen+2:], elems[oldLen:])
|
||||
elems[oldLen] = nc.w.numberStart
|
||||
elems[oldLen+1] = 0
|
||||
|
||||
nc.nDigits = 1
|
||||
}
|
||||
nc.elems = elems
|
||||
nc.lenIndex = oldLen + 1
|
||||
}
|
||||
|
||||
// checkNextDigit reports whether bufNew adds a single digit relative to the old
|
||||
// buffer. If it does, it also reports whether this digit is zero.
|
||||
func (nc *numberConverter) checkNextDigit(bufNew []Elem) (isZero, ok bool) {
|
||||
if len(nc.elems) >= len(bufNew) {
|
||||
return false, false
|
||||
}
|
||||
e := bufNew[len(nc.elems)]
|
||||
if e < nc.w.zeroSpecialLo || nc.w.nine < e {
|
||||
// Not a number.
|
||||
return false, false
|
||||
}
|
||||
if e < nc.w.zero {
|
||||
if e > nc.w.nineSpecialHi {
|
||||
// Not a number.
|
||||
return false, false
|
||||
}
|
||||
if !nc.isDigit() {
|
||||
return false, false
|
||||
}
|
||||
isZero = e <= nc.w.zeroSpecialHi
|
||||
} else {
|
||||
// This is the common case if we encounter a digit.
|
||||
isZero = e == nc.w.zero
|
||||
}
|
||||
// Test the remaining added collation elements have a zero primary value.
|
||||
if n := len(bufNew) - len(nc.elems); n > 1 {
|
||||
for i := len(nc.elems) + 1; i < len(bufNew); i++ {
|
||||
if bufNew[i].Primary() != 0 {
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
// In some rare cases, collation elements will encode runes in
|
||||
// unicode.No as a digit. For example Ethiopic digits (U+1369 - U+1371)
|
||||
// are not in Nd. Also some digits that clearly belong in unicode.No,
|
||||
// like U+0C78 TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR, have
|
||||
// collation elements indistinguishable from normal digits.
|
||||
// Unfortunately, this means we need to make this check for nearly all
|
||||
// non-Latin digits.
|
||||
//
|
||||
// TODO: check the performance impact and find something better if it is
|
||||
// an issue.
|
||||
if !nc.isDigit() {
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
return isZero, true
|
||||
}
|
||||
|
||||
func (nc *numberConverter) isDigit() bool {
|
||||
if nc.b != nil {
|
||||
r, _ := utf8.DecodeRune(nc.b)
|
||||
return unicode.In(r, unicode.Nd)
|
||||
}
|
||||
r, _ := utf8.DecodeRuneInString(nc.s)
|
||||
return unicode.In(r, unicode.Nd)
|
||||
}
|
||||
|
||||
// We currently support a maximum of about 2M digits (the number of primary
|
||||
// values). Such numbers will compare correctly against small numbers, but their
|
||||
// comparison against other large numbers is undefined.
|
||||
//
|
||||
// TODO: define a proper fallback, such as comparing large numbers textually or
|
||||
// actually allowing numbers of unlimited length.
|
||||
//
|
||||
// TODO: cap this to a lower number (like 100) and maybe allow a larger number
|
||||
// in an option?
|
||||
const maxDigits = 1<<maxPrimaryBits - 1
|
||||
|
||||
func (nc *numberConverter) update(elems []Elem) bool {
|
||||
isZero, ok := nc.checkNextDigit(elems)
|
||||
if nc.nDigits == 0 && isZero {
|
||||
return true
|
||||
}
|
||||
nc.elems = elems
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
nc.nDigits++
|
||||
return nc.nDigits < maxDigits
|
||||
}
|
||||
|
||||
// result fills in the length element for the digit sequence and returns the
|
||||
// completed collation elements.
|
||||
func (nc *numberConverter) result() []Elem {
|
||||
e, _ := MakeElem(nc.nDigits, defaultSecondary, defaultTertiary, 0)
|
||||
nc.elems[nc.lenIndex] = e
|
||||
return nc.elems
|
||||
}
|
||||
159
vendor/golang.org/x/text/collate/colltab/numeric_test.go
generated
vendored
Normal file
159
vendor/golang.org/x/text/collate/colltab/numeric_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2014 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 colltab
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/internal/testtext"
|
||||
)
|
||||
|
||||
const (
|
||||
digSec = defaultSecondary
|
||||
digTert = defaultTertiary
|
||||
)
|
||||
|
||||
var tPlus3 = e(0, 50, digTert+3)
|
||||
|
||||
// numWeighter is a testWeighter used for testing numericWeighter.
|
||||
var numWeighter = testWeighter{
|
||||
"0": p(100),
|
||||
"0": []Elem{e(100, digSec, digTert+1)}, // U+FF10 FULLWIDTH DIGIT ZERO
|
||||
"₀": []Elem{e(100, digSec, digTert+5)}, // U+2080 SUBSCRIPT ZERO
|
||||
|
||||
"1": p(101),
|
||||
// Allow non-primary collation elements to be inserted.
|
||||
"١": append(p(101), tPlus3), // U+0661 ARABIC-INDIC DIGIT ONE
|
||||
// Allow varying tertiary weight if the number is Nd.
|
||||
"1": []Elem{e(101, digSec, digTert+1)}, // U+FF11 FULLWIDTH DIGIT ONE
|
||||
"2": p(102),
|
||||
// Allow non-primary collation elements to be inserted.
|
||||
"٢": append(p(102), tPlus3), // U+0662 ARABIC-INDIC DIGIT TWO
|
||||
// Varying tertiary weights should be ignored.
|
||||
"2": []Elem{e(102, digSec, digTert+3)}, // U+FF12 FULLWIDTH DIGIT TWO
|
||||
"3": p(103),
|
||||
"4": p(104),
|
||||
"5": p(105),
|
||||
"6": p(106),
|
||||
"7": p(107),
|
||||
// Weights must be strictly monotonically increasing, but do not need to be
|
||||
// consecutive.
|
||||
"8": p(118),
|
||||
"9": p(119),
|
||||
// Allow non-primary collation elements to be inserted.
|
||||
"٩": append(p(119), tPlus3), // U+0669 ARABIC-INDIC DIGIT NINE
|
||||
// Varying tertiary weights should be ignored.
|
||||
"9": []Elem{e(119, digSec, digTert+1)}, // U+FF19 FULLWIDTH DIGIT NINE
|
||||
"₉": []Elem{e(119, digSec, digTert+5)}, // U+2089 SUBSCRIPT NINE
|
||||
|
||||
"a": p(5),
|
||||
"b": p(6),
|
||||
"c": p(8, 2),
|
||||
|
||||
"klm": p(99),
|
||||
|
||||
"nop": p(121),
|
||||
|
||||
"x": p(200),
|
||||
"y": p(201),
|
||||
}
|
||||
|
||||
func p(w ...int) (elems []Elem) {
|
||||
for _, x := range w {
|
||||
e, _ := MakeElem(x, digSec, digTert, 0)
|
||||
elems = append(elems, e)
|
||||
}
|
||||
return elems
|
||||
}
|
||||
|
||||
func TestNumericAppendNext(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
in string
|
||||
w []Elem
|
||||
}{
|
||||
{"a", p(5)},
|
||||
{"klm", p(99)},
|
||||
{"aa", p(5, 5)},
|
||||
{"1", p(120, 1, 101)},
|
||||
{"0", p(120, 0)},
|
||||
{"01", p(120, 1, 101)},
|
||||
{"0001", p(120, 1, 101)},
|
||||
{"10", p(120, 2, 101, 100)},
|
||||
{"99", p(120, 2, 119, 119)},
|
||||
{"9999", p(120, 4, 119, 119, 119, 119)},
|
||||
{"1a", p(120, 1, 101, 5)},
|
||||
{"0b", p(120, 0, 6)},
|
||||
{"01c", p(120, 1, 101, 8, 2)},
|
||||
{"10x", p(120, 2, 101, 100, 200)},
|
||||
{"99y", p(120, 2, 119, 119, 201)},
|
||||
{"9999nop", p(120, 4, 119, 119, 119, 119, 121)},
|
||||
|
||||
// Allow follow-up collation elements if they have a zero non-primary.
|
||||
{"١٢٩", []Elem{e(120), e(3), e(101), tPlus3, e(102), tPlus3, e(119), tPlus3}},
|
||||
{
|
||||
"129",
|
||||
[]Elem{
|
||||
e(120), e(3),
|
||||
e(101, digSec, digTert+1),
|
||||
e(102, digSec, digTert+3),
|
||||
e(119, digSec, digTert+1),
|
||||
},
|
||||
},
|
||||
|
||||
// Ensure AppendNext* adds to the given buffer.
|
||||
{"a10", p(5, 120, 2, 101, 100)},
|
||||
} {
|
||||
nw := NewNumericWeighter(numWeighter)
|
||||
|
||||
b := []byte(tt.in)
|
||||
got := []Elem(nil)
|
||||
for n, sz := 0, 0; n < len(b); {
|
||||
got, sz = nw.AppendNext(got, b[n:])
|
||||
n += sz
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.w) {
|
||||
t.Errorf("AppendNext(%q) =\n%v; want\n%v", tt.in, got, tt.w)
|
||||
}
|
||||
|
||||
got = nil
|
||||
for n, sz := 0, 0; n < len(tt.in); {
|
||||
got, sz = nw.AppendNextString(got, tt.in[n:])
|
||||
n += sz
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.w) {
|
||||
t.Errorf("AppendNextString(%q) =\n%v; want\n%v", tt.in, got, tt.w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericOverflow(t *testing.T) {
|
||||
manyDigits := strings.Repeat("9", maxDigits+1) + "a"
|
||||
|
||||
nw := NewNumericWeighter(numWeighter)
|
||||
|
||||
got, n := nw.AppendNextString(nil, manyDigits)
|
||||
|
||||
if n != maxDigits {
|
||||
t.Errorf("n: got %d; want %d", n, maxDigits)
|
||||
}
|
||||
|
||||
if got[1].Primary() != maxDigits {
|
||||
t.Errorf("primary(e[1]): got %d; want %d", n, maxDigits)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericWeighterAlloc(t *testing.T) {
|
||||
buf := make([]Elem, 100)
|
||||
w := NewNumericWeighter(numWeighter)
|
||||
s := "1234567890a"
|
||||
|
||||
nNormal := testtext.AllocsPerRun(3, func() { numWeighter.AppendNextString(buf, s) })
|
||||
nNumeric := testtext.AllocsPerRun(3, func() { w.AppendNextString(buf, s) })
|
||||
if n := nNumeric - nNormal; n > 0 {
|
||||
t.Errorf("got %f; want 0", n)
|
||||
}
|
||||
}
|
||||
275
vendor/golang.org/x/text/collate/colltab/table.go
generated
vendored
Normal file
275
vendor/golang.org/x/text/collate/colltab/table.go
generated
vendored
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
// Copyright 2012 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 colltab
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// table holds all collation data for a given collation ordering.
|
||||
type table struct {
|
||||
index trie // main trie
|
||||
|
||||
// expansion info
|
||||
expandElem []uint32
|
||||
|
||||
// contraction info
|
||||
contractTries contractTrieSet
|
||||
contractElem []uint32
|
||||
maxContractLen int
|
||||
variableTop uint32
|
||||
}
|
||||
|
||||
func (t *table) AppendNext(w []Elem, b []byte) (res []Elem, n int) {
|
||||
return t.appendNext(w, source{bytes: b})
|
||||
}
|
||||
|
||||
func (t *table) AppendNextString(w []Elem, s string) (res []Elem, n int) {
|
||||
return t.appendNext(w, source{str: s})
|
||||
}
|
||||
|
||||
func (t *table) Start(p int, b []byte) int {
|
||||
// TODO: implement
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (t *table) StartString(p int, s string) int {
|
||||
// TODO: implement
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (t *table) Domain() []string {
|
||||
// TODO: implement
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (t *table) Top() uint32 {
|
||||
return t.variableTop
|
||||
}
|
||||
|
||||
type source struct {
|
||||
str string
|
||||
bytes []byte
|
||||
}
|
||||
|
||||
func (src *source) lookup(t *table) (ce Elem, sz int) {
|
||||
if src.bytes == nil {
|
||||
return t.index.lookupString(src.str)
|
||||
}
|
||||
return t.index.lookup(src.bytes)
|
||||
}
|
||||
|
||||
func (src *source) tail(sz int) {
|
||||
if src.bytes == nil {
|
||||
src.str = src.str[sz:]
|
||||
} else {
|
||||
src.bytes = src.bytes[sz:]
|
||||
}
|
||||
}
|
||||
|
||||
func (src *source) nfd(buf []byte, end int) []byte {
|
||||
if src.bytes == nil {
|
||||
return norm.NFD.AppendString(buf[:0], src.str[:end])
|
||||
}
|
||||
return norm.NFD.Append(buf[:0], src.bytes[:end]...)
|
||||
}
|
||||
|
||||
func (src *source) rune() (r rune, sz int) {
|
||||
if src.bytes == nil {
|
||||
return utf8.DecodeRuneInString(src.str)
|
||||
}
|
||||
return utf8.DecodeRune(src.bytes)
|
||||
}
|
||||
|
||||
func (src *source) properties(f norm.Form) norm.Properties {
|
||||
if src.bytes == nil {
|
||||
return f.PropertiesString(src.str)
|
||||
}
|
||||
return f.Properties(src.bytes)
|
||||
}
|
||||
|
||||
// appendNext appends the weights corresponding to the next rune or
|
||||
// contraction in s. If a contraction is matched to a discontinuous
|
||||
// sequence of runes, the weights for the interstitial runes are
|
||||
// appended as well. It returns a new slice that includes the appended
|
||||
// weights and the number of bytes consumed from s.
|
||||
func (t *table) appendNext(w []Elem, src source) (res []Elem, n int) {
|
||||
ce, sz := src.lookup(t)
|
||||
tp := ce.ctype()
|
||||
if tp == ceNormal {
|
||||
if ce == 0 {
|
||||
r, _ := src.rune()
|
||||
const (
|
||||
hangulSize = 3
|
||||
firstHangul = 0xAC00
|
||||
lastHangul = 0xD7A3
|
||||
)
|
||||
if r >= firstHangul && r <= lastHangul {
|
||||
// TODO: performance can be considerably improved here.
|
||||
n = sz
|
||||
var buf [16]byte // Used for decomposing Hangul.
|
||||
for b := src.nfd(buf[:0], hangulSize); len(b) > 0; b = b[sz:] {
|
||||
ce, sz = t.index.lookup(b)
|
||||
w = append(w, ce)
|
||||
}
|
||||
return w, n
|
||||
}
|
||||
ce = makeImplicitCE(implicitPrimary(r))
|
||||
}
|
||||
w = append(w, ce)
|
||||
} else if tp == ceExpansionIndex {
|
||||
w = t.appendExpansion(w, ce)
|
||||
} else if tp == ceContractionIndex {
|
||||
n := 0
|
||||
src.tail(sz)
|
||||
if src.bytes == nil {
|
||||
w, n = t.matchContractionString(w, ce, src.str)
|
||||
} else {
|
||||
w, n = t.matchContraction(w, ce, src.bytes)
|
||||
}
|
||||
sz += n
|
||||
} else if tp == ceDecompose {
|
||||
// Decompose using NFKD and replace tertiary weights.
|
||||
t1, t2 := splitDecompose(ce)
|
||||
i := len(w)
|
||||
nfkd := src.properties(norm.NFKD).Decomposition()
|
||||
for p := 0; len(nfkd) > 0; nfkd = nfkd[p:] {
|
||||
w, p = t.appendNext(w, source{bytes: nfkd})
|
||||
}
|
||||
w[i] = w[i].updateTertiary(t1)
|
||||
if i++; i < len(w) {
|
||||
w[i] = w[i].updateTertiary(t2)
|
||||
for i++; i < len(w); i++ {
|
||||
w[i] = w[i].updateTertiary(maxTertiary)
|
||||
}
|
||||
}
|
||||
}
|
||||
return w, sz
|
||||
}
|
||||
|
||||
func (t *table) appendExpansion(w []Elem, ce Elem) []Elem {
|
||||
i := splitExpandIndex(ce)
|
||||
n := int(t.expandElem[i])
|
||||
i++
|
||||
for _, ce := range t.expandElem[i : i+n] {
|
||||
w = append(w, Elem(ce))
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func (t *table) matchContraction(w []Elem, ce Elem, suffix []byte) ([]Elem, int) {
|
||||
index, n, offset := splitContractIndex(ce)
|
||||
|
||||
scan := t.contractTries.scanner(index, n, suffix)
|
||||
buf := [norm.MaxSegmentSize]byte{}
|
||||
bufp := 0
|
||||
p := scan.scan(0)
|
||||
|
||||
if !scan.done && p < len(suffix) && suffix[p] >= utf8.RuneSelf {
|
||||
// By now we should have filtered most cases.
|
||||
p0 := p
|
||||
bufn := 0
|
||||
rune := norm.NFD.Properties(suffix[p:])
|
||||
p += rune.Size()
|
||||
if rune.LeadCCC() != 0 {
|
||||
prevCC := rune.TrailCCC()
|
||||
// A gap may only occur in the last normalization segment.
|
||||
// This also ensures that len(scan.s) < norm.MaxSegmentSize.
|
||||
if end := norm.NFD.FirstBoundary(suffix[p:]); end != -1 {
|
||||
scan.s = suffix[:p+end]
|
||||
}
|
||||
for p < len(suffix) && !scan.done && suffix[p] >= utf8.RuneSelf {
|
||||
rune = norm.NFD.Properties(suffix[p:])
|
||||
if ccc := rune.LeadCCC(); ccc == 0 || prevCC >= ccc {
|
||||
break
|
||||
}
|
||||
prevCC = rune.TrailCCC()
|
||||
if pp := scan.scan(p); pp != p {
|
||||
// Copy the interstitial runes for later processing.
|
||||
bufn += copy(buf[bufn:], suffix[p0:p])
|
||||
if scan.pindex == pp {
|
||||
bufp = bufn
|
||||
}
|
||||
p, p0 = pp, pp
|
||||
} else {
|
||||
p += rune.Size()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Append weights for the matched contraction, which may be an expansion.
|
||||
i, n := scan.result()
|
||||
ce = Elem(t.contractElem[i+offset])
|
||||
if ce.ctype() == ceNormal {
|
||||
w = append(w, ce)
|
||||
} else {
|
||||
w = t.appendExpansion(w, ce)
|
||||
}
|
||||
// Append weights for the runes in the segment not part of the contraction.
|
||||
for b, p := buf[:bufp], 0; len(b) > 0; b = b[p:] {
|
||||
w, p = t.appendNext(w, source{bytes: b})
|
||||
}
|
||||
return w, n
|
||||
}
|
||||
|
||||
// TODO: unify the two implementations. This is best done after first simplifying
|
||||
// the algorithm taking into account the inclusion of both NFC and NFD forms
|
||||
// in the table.
|
||||
func (t *table) matchContractionString(w []Elem, ce Elem, suffix string) ([]Elem, int) {
|
||||
index, n, offset := splitContractIndex(ce)
|
||||
|
||||
scan := t.contractTries.scannerString(index, n, suffix)
|
||||
buf := [norm.MaxSegmentSize]byte{}
|
||||
bufp := 0
|
||||
p := scan.scan(0)
|
||||
|
||||
if !scan.done && p < len(suffix) && suffix[p] >= utf8.RuneSelf {
|
||||
// By now we should have filtered most cases.
|
||||
p0 := p
|
||||
bufn := 0
|
||||
rune := norm.NFD.PropertiesString(suffix[p:])
|
||||
p += rune.Size()
|
||||
if rune.LeadCCC() != 0 {
|
||||
prevCC := rune.TrailCCC()
|
||||
// A gap may only occur in the last normalization segment.
|
||||
// This also ensures that len(scan.s) < norm.MaxSegmentSize.
|
||||
if end := norm.NFD.FirstBoundaryInString(suffix[p:]); end != -1 {
|
||||
scan.s = suffix[:p+end]
|
||||
}
|
||||
for p < len(suffix) && !scan.done && suffix[p] >= utf8.RuneSelf {
|
||||
rune = norm.NFD.PropertiesString(suffix[p:])
|
||||
if ccc := rune.LeadCCC(); ccc == 0 || prevCC >= ccc {
|
||||
break
|
||||
}
|
||||
prevCC = rune.TrailCCC()
|
||||
if pp := scan.scan(p); pp != p {
|
||||
// Copy the interstitial runes for later processing.
|
||||
bufn += copy(buf[bufn:], suffix[p0:p])
|
||||
if scan.pindex == pp {
|
||||
bufp = bufn
|
||||
}
|
||||
p, p0 = pp, pp
|
||||
} else {
|
||||
p += rune.Size()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Append weights for the matched contraction, which may be an expansion.
|
||||
i, n := scan.result()
|
||||
ce = Elem(t.contractElem[i+offset])
|
||||
if ce.ctype() == ceNormal {
|
||||
w = append(w, ce)
|
||||
} else {
|
||||
w = t.appendExpansion(w, ce)
|
||||
}
|
||||
// Append weights for the runes in the segment not part of the contraction.
|
||||
for b, p := buf[:bufp], 0; len(b) > 0; b = b[p:] {
|
||||
w, p = t.appendNext(w, source{bytes: b})
|
||||
}
|
||||
return w, n
|
||||
}
|
||||
159
vendor/golang.org/x/text/collate/colltab/trie.go
generated
vendored
Normal file
159
vendor/golang.org/x/text/collate/colltab/trie.go
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// The trie in this file is used to associate the first full character in an
|
||||
// UTF-8 string to a collation element. All but the last byte in a UTF-8 byte
|
||||
// sequence are used to lookup offsets in the index table to be used for the
|
||||
// next byte. The last byte is used to index into a table of collation elements.
|
||||
// For a full description, see go.text/collate/build/trie.go.
|
||||
|
||||
package colltab
|
||||
|
||||
const blockSize = 64
|
||||
|
||||
type trie struct {
|
||||
index0 []uint16 // index for first byte (0xC0-0xFF)
|
||||
values0 []uint32 // index for first byte (0x00-0x7F)
|
||||
index []uint16
|
||||
values []uint32
|
||||
}
|
||||
|
||||
const (
|
||||
t1 = 0x00 // 0000 0000
|
||||
tx = 0x80 // 1000 0000
|
||||
t2 = 0xC0 // 1100 0000
|
||||
t3 = 0xE0 // 1110 0000
|
||||
t4 = 0xF0 // 1111 0000
|
||||
t5 = 0xF8 // 1111 1000
|
||||
t6 = 0xFC // 1111 1100
|
||||
te = 0xFE // 1111 1110
|
||||
)
|
||||
|
||||
func (t *trie) lookupValue(n uint16, b byte) Elem {
|
||||
return Elem(t.values[int(n)<<6+int(b)])
|
||||
}
|
||||
|
||||
// lookup returns the trie value for the first UTF-8 encoding in s and
|
||||
// the width in bytes of this encoding. The size will be 0 if s does not
|
||||
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
|
||||
func (t *trie) lookup(s []byte) (v Elem, sz int) {
|
||||
c0 := s[0]
|
||||
switch {
|
||||
case c0 < tx:
|
||||
return Elem(t.values0[c0]), 1
|
||||
case c0 < t2:
|
||||
return 0, 1
|
||||
case c0 < t3:
|
||||
if len(s) < 2 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index0[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
return t.lookupValue(i, c1), 2
|
||||
case c0 < t4:
|
||||
if len(s) < 3 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index0[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
o := int(i)<<6 + int(c1)
|
||||
i = t.index[o]
|
||||
c2 := s[2]
|
||||
if c2 < tx || t2 <= c2 {
|
||||
return 0, 2
|
||||
}
|
||||
return t.lookupValue(i, c2), 3
|
||||
case c0 < t5:
|
||||
if len(s) < 4 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index0[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
o := int(i)<<6 + int(c1)
|
||||
i = t.index[o]
|
||||
c2 := s[2]
|
||||
if c2 < tx || t2 <= c2 {
|
||||
return 0, 2
|
||||
}
|
||||
o = int(i)<<6 + int(c2)
|
||||
i = t.index[o]
|
||||
c3 := s[3]
|
||||
if c3 < tx || t2 <= c3 {
|
||||
return 0, 3
|
||||
}
|
||||
return t.lookupValue(i, c3), 4
|
||||
}
|
||||
// Illegal rune
|
||||
return 0, 1
|
||||
}
|
||||
|
||||
// The body of lookupString is a verbatim copy of that of lookup.
|
||||
func (t *trie) lookupString(s string) (v Elem, sz int) {
|
||||
c0 := s[0]
|
||||
switch {
|
||||
case c0 < tx:
|
||||
return Elem(t.values0[c0]), 1
|
||||
case c0 < t2:
|
||||
return 0, 1
|
||||
case c0 < t3:
|
||||
if len(s) < 2 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index0[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
return t.lookupValue(i, c1), 2
|
||||
case c0 < t4:
|
||||
if len(s) < 3 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index0[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
o := int(i)<<6 + int(c1)
|
||||
i = t.index[o]
|
||||
c2 := s[2]
|
||||
if c2 < tx || t2 <= c2 {
|
||||
return 0, 2
|
||||
}
|
||||
return t.lookupValue(i, c2), 3
|
||||
case c0 < t5:
|
||||
if len(s) < 4 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index0[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
o := int(i)<<6 + int(c1)
|
||||
i = t.index[o]
|
||||
c2 := s[2]
|
||||
if c2 < tx || t2 <= c2 {
|
||||
return 0, 2
|
||||
}
|
||||
o = int(i)<<6 + int(c2)
|
||||
i = t.index[o]
|
||||
c3 := s[3]
|
||||
if c3 < tx || t2 <= c3 {
|
||||
return 0, 3
|
||||
}
|
||||
return t.lookupValue(i, c3), 4
|
||||
}
|
||||
// Illegal rune
|
||||
return 0, 1
|
||||
}
|
||||
106
vendor/golang.org/x/text/collate/colltab/trie_test.go
generated
vendored
Normal file
106
vendor/golang.org/x/text/collate/colltab/trie_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2012 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 colltab
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// We take the smallest, largest and an arbitrary value for each
|
||||
// of the UTF-8 sequence lengths.
|
||||
var testRunes = []rune{
|
||||
0x01, 0x0C, 0x7F, // 1-byte sequences
|
||||
0x80, 0x100, 0x7FF, // 2-byte sequences
|
||||
0x800, 0x999, 0xFFFF, // 3-byte sequences
|
||||
0x10000, 0x10101, 0x10FFFF, // 4-byte sequences
|
||||
0x200, 0x201, 0x202, 0x210, 0x215, // five entries in one sparse block
|
||||
}
|
||||
|
||||
// Test cases for illegal runes.
|
||||
type trietest struct {
|
||||
size int
|
||||
bytes []byte
|
||||
}
|
||||
|
||||
var tests = []trietest{
|
||||
// illegal runes
|
||||
{1, []byte{0x80}},
|
||||
{1, []byte{0xFF}},
|
||||
{1, []byte{t2, tx - 1}},
|
||||
{1, []byte{t2, t2}},
|
||||
{2, []byte{t3, tx, tx - 1}},
|
||||
{2, []byte{t3, tx, t2}},
|
||||
{1, []byte{t3, tx - 1, tx}},
|
||||
{3, []byte{t4, tx, tx, tx - 1}},
|
||||
{3, []byte{t4, tx, tx, t2}},
|
||||
{1, []byte{t4, t2, tx, tx - 1}},
|
||||
{2, []byte{t4, tx, t2, tx - 1}},
|
||||
|
||||
// short runes
|
||||
{0, []byte{t2}},
|
||||
{0, []byte{t3, tx}},
|
||||
{0, []byte{t4, tx, tx}},
|
||||
|
||||
// we only support UTF-8 up to utf8.UTFMax bytes (4 bytes)
|
||||
{1, []byte{t5, tx, tx, tx, tx}},
|
||||
{1, []byte{t6, tx, tx, tx, tx, tx}},
|
||||
}
|
||||
|
||||
func TestLookupTrie(t *testing.T) {
|
||||
for i, r := range testRunes {
|
||||
b := []byte(string(r))
|
||||
v, sz := testTrie.lookup(b)
|
||||
if int(v) != i {
|
||||
t.Errorf("lookup(%U): found value %#x, expected %#x", r, v, i)
|
||||
}
|
||||
if sz != len(b) {
|
||||
t.Errorf("lookup(%U): found size %d, expected %d", r, sz, len(b))
|
||||
}
|
||||
}
|
||||
for i, tt := range tests {
|
||||
v, sz := testTrie.lookup(tt.bytes)
|
||||
if int(v) != 0 {
|
||||
t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v)
|
||||
}
|
||||
if sz != tt.size {
|
||||
t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test data is taken from exp/collate/locale/build/trie_test.go
|
||||
var testValues = [832]uint32{
|
||||
0x000c: 0x00000001,
|
||||
0x007f: 0x00000002,
|
||||
0x00c0: 0x00000003,
|
||||
0x0100: 0x00000004,
|
||||
0x0140: 0x0000000c, 0x0141: 0x0000000d, 0x0142: 0x0000000e,
|
||||
0x0150: 0x0000000f,
|
||||
0x0155: 0x00000010,
|
||||
0x01bf: 0x00000005,
|
||||
0x01c0: 0x00000006,
|
||||
0x0219: 0x00000007,
|
||||
0x027f: 0x00000008,
|
||||
0x0280: 0x00000009,
|
||||
0x02c1: 0x0000000a,
|
||||
0x033f: 0x0000000b,
|
||||
}
|
||||
|
||||
var testLookup = [640]uint16{
|
||||
0x0e0: 0x05, 0x0e6: 0x06,
|
||||
0x13f: 0x07,
|
||||
0x140: 0x08, 0x144: 0x09,
|
||||
0x190: 0x03,
|
||||
0x1ff: 0x0a,
|
||||
0x20f: 0x05,
|
||||
0x242: 0x01, 0x244: 0x02,
|
||||
0x248: 0x03,
|
||||
0x25f: 0x04,
|
||||
0x260: 0x01,
|
||||
0x26f: 0x02,
|
||||
0x270: 0x04, 0x274: 0x06,
|
||||
}
|
||||
|
||||
var testTrie = trie{testLookup[6*blockSize:], testValues[:], testLookup[:], testValues[:]}
|
||||
51
vendor/golang.org/x/text/collate/export_test.go
generated
vendored
Normal file
51
vendor/golang.org/x/text/collate/export_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2012 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 collate
|
||||
|
||||
// Export for testing.
|
||||
// TODO: no longer necessary. Remove at some point.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/text/collate/colltab"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSecondary = 0x20
|
||||
defaultTertiary = 0x2
|
||||
)
|
||||
|
||||
type Weights struct {
|
||||
Primary, Secondary, Tertiary, Quaternary int
|
||||
}
|
||||
|
||||
func W(ce ...int) Weights {
|
||||
w := Weights{ce[0], defaultSecondary, defaultTertiary, 0}
|
||||
if len(ce) > 1 {
|
||||
w.Secondary = ce[1]
|
||||
}
|
||||
if len(ce) > 2 {
|
||||
w.Tertiary = ce[2]
|
||||
}
|
||||
if len(ce) > 3 {
|
||||
w.Quaternary = ce[3]
|
||||
}
|
||||
return w
|
||||
}
|
||||
func (w Weights) String() string {
|
||||
return fmt.Sprintf("[%X.%X.%X.%X]", w.Primary, w.Secondary, w.Tertiary, w.Quaternary)
|
||||
}
|
||||
|
||||
func convertFromWeights(ws []Weights) []colltab.Elem {
|
||||
out := make([]colltab.Elem, len(ws))
|
||||
for i, w := range ws {
|
||||
out[i], _ = colltab.MakeElem(w.Primary, w.Secondary, w.Tertiary, 0)
|
||||
if out[i] == colltab.Ignore && w.Quaternary > 0 {
|
||||
out[i] = colltab.MakeQuaternary(w.Quaternary)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
44
vendor/golang.org/x/text/collate/index.go
generated
vendored
Normal file
44
vendor/golang.org/x/text/collate/index.go
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// 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 collate
|
||||
|
||||
// tableIndex holds information for constructing a table
|
||||
// for a certain locale based on the main table.
|
||||
type tableIndex struct {
|
||||
lookupOffset uint32
|
||||
valuesOffset uint32
|
||||
}
|
||||
|
||||
func (t tableIndex) TrieIndex() []uint16 {
|
||||
return mainLookup[:]
|
||||
}
|
||||
|
||||
func (t tableIndex) TrieValues() []uint32 {
|
||||
return mainValues[:]
|
||||
}
|
||||
|
||||
func (t tableIndex) FirstBlockOffsets() (lookup, value uint16) {
|
||||
return uint16(t.lookupOffset), uint16(t.valuesOffset)
|
||||
}
|
||||
|
||||
func (t tableIndex) ExpandElems() []uint32 {
|
||||
return mainExpandElem[:]
|
||||
}
|
||||
|
||||
func (t tableIndex) ContractTries() []struct{ l, h, n, i uint8 } {
|
||||
return mainCTEntries[:]
|
||||
}
|
||||
|
||||
func (t tableIndex) ContractElems() []uint32 {
|
||||
return mainContractElem[:]
|
||||
}
|
||||
|
||||
func (t tableIndex) MaxContractLen() int {
|
||||
return 18 // TODO: generate
|
||||
}
|
||||
|
||||
func (t tableIndex) VariableTop() uint32 {
|
||||
return varTop
|
||||
}
|
||||
553
vendor/golang.org/x/text/collate/maketables.go
generated
vendored
Normal file
553
vendor/golang.org/x/text/collate/maketables.go
generated
vendored
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// Collation table generator.
|
||||
// Data read from the web.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/collate"
|
||||
"golang.org/x/text/collate/build"
|
||||
"golang.org/x/text/collate/colltab"
|
||||
"golang.org/x/text/internal/gen"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/unicode/cldr"
|
||||
)
|
||||
|
||||
var (
|
||||
test = flag.Bool("test", false,
|
||||
"test existing tables; can be used to compare web data with package data.")
|
||||
short = flag.Bool("short", false, `Use "short" alternatives, when available.`)
|
||||
draft = flag.Bool("draft", false, `Use draft versions, when available.`)
|
||||
tags = flag.String("tags", "", "build tags to be included after +build directive")
|
||||
pkg = flag.String("package", "collate",
|
||||
"the name of the package in which the generated file is to be included")
|
||||
|
||||
tables = flagStringSetAllowAll("tables", "collate", "collate,chars",
|
||||
"comma-spearated list of tables to generate.")
|
||||
exclude = flagStringSet("exclude", "zh2", "",
|
||||
"comma-separated list of languages to exclude.")
|
||||
include = flagStringSet("include", "", "",
|
||||
"comma-separated list of languages to include. Include trumps exclude.")
|
||||
// TODO: Not included: unihan gb2312han zhuyin big5han (for size reasons)
|
||||
// TODO: Not included: traditional (buggy for Bengali)
|
||||
types = flagStringSetAllowAll("types", "standard,phonebook,phonetic,reformed,pinyin,stroke", "",
|
||||
"comma-separated list of types that should be included.")
|
||||
)
|
||||
|
||||
// stringSet implements an ordered set based on a list. It implements flag.Value
|
||||
// to allow a set to be specified as a comma-separated list.
|
||||
type stringSet struct {
|
||||
s []string
|
||||
allowed *stringSet
|
||||
dirty bool // needs compaction if true
|
||||
all bool
|
||||
allowAll bool
|
||||
}
|
||||
|
||||
func flagStringSet(name, def, allowed, usage string) *stringSet {
|
||||
ss := &stringSet{}
|
||||
if allowed != "" {
|
||||
usage += fmt.Sprintf(" (allowed values: any of %s)", allowed)
|
||||
ss.allowed = &stringSet{}
|
||||
failOnError(ss.allowed.Set(allowed))
|
||||
}
|
||||
ss.Set(def)
|
||||
flag.Var(ss, name, usage)
|
||||
return ss
|
||||
}
|
||||
|
||||
func flagStringSetAllowAll(name, def, allowed, usage string) *stringSet {
|
||||
ss := &stringSet{allowAll: true}
|
||||
if allowed == "" {
|
||||
flag.Var(ss, name, usage+fmt.Sprintf(` Use "all" to select all.`))
|
||||
} else {
|
||||
ss.allowed = &stringSet{}
|
||||
failOnError(ss.allowed.Set(allowed))
|
||||
flag.Var(ss, name, usage+fmt.Sprintf(` (allowed values: "all" or any of %s)`, allowed))
|
||||
}
|
||||
ss.Set(def)
|
||||
return ss
|
||||
}
|
||||
|
||||
func (ss stringSet) Len() int {
|
||||
return len(ss.s)
|
||||
}
|
||||
|
||||
func (ss stringSet) String() string {
|
||||
return strings.Join(ss.s, ",")
|
||||
}
|
||||
|
||||
func (ss *stringSet) Set(s string) error {
|
||||
if ss.allowAll && s == "all" {
|
||||
ss.s = nil
|
||||
ss.all = true
|
||||
return nil
|
||||
}
|
||||
ss.s = ss.s[:0]
|
||||
for _, s := range strings.Split(s, ",") {
|
||||
if s := strings.TrimSpace(s); s != "" {
|
||||
if ss.allowed != nil && !ss.allowed.contains(s) {
|
||||
return fmt.Errorf("unsupported value %q; must be one of %s", s, ss.allowed)
|
||||
}
|
||||
ss.add(s)
|
||||
}
|
||||
}
|
||||
ss.compact()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *stringSet) add(s string) {
|
||||
ss.s = append(ss.s, s)
|
||||
ss.dirty = true
|
||||
}
|
||||
|
||||
func (ss *stringSet) values() []string {
|
||||
ss.compact()
|
||||
return ss.s
|
||||
}
|
||||
|
||||
func (ss *stringSet) contains(s string) bool {
|
||||
if ss.all {
|
||||
return true
|
||||
}
|
||||
for _, v := range ss.s {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ss *stringSet) compact() {
|
||||
if !ss.dirty {
|
||||
return
|
||||
}
|
||||
a := ss.s
|
||||
sort.Strings(a)
|
||||
k := 0
|
||||
for i := 1; i < len(a); i++ {
|
||||
if a[k] != a[i] {
|
||||
a[k+1] = a[i]
|
||||
k++
|
||||
}
|
||||
}
|
||||
ss.s = a[:k+1]
|
||||
ss.dirty = false
|
||||
}
|
||||
|
||||
func skipLang(l string) bool {
|
||||
if include.Len() > 0 {
|
||||
return !include.contains(l)
|
||||
}
|
||||
return exclude.contains(l)
|
||||
}
|
||||
|
||||
// altInclude returns a list of alternatives (for the LDML alt attribute)
|
||||
// in order of preference. An empty string in this list indicates the
|
||||
// default entry.
|
||||
func altInclude() []string {
|
||||
l := []string{}
|
||||
if *short {
|
||||
l = append(l, "short")
|
||||
}
|
||||
l = append(l, "")
|
||||
// TODO: handle draft using cldr.SetDraftLevel
|
||||
if *draft {
|
||||
l = append(l, "proposed")
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func failOnError(e error) {
|
||||
if e != nil {
|
||||
log.Panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
func openArchive() *zip.Reader {
|
||||
f := gen.OpenCLDRCoreZip()
|
||||
buffer, err := ioutil.ReadAll(f)
|
||||
f.Close()
|
||||
failOnError(err)
|
||||
archive, err := zip.NewReader(bytes.NewReader(buffer), int64(len(buffer)))
|
||||
failOnError(err)
|
||||
return archive
|
||||
}
|
||||
|
||||
// parseUCA parses a Default Unicode Collation Element Table of the format
|
||||
// specified in http://www.unicode.org/reports/tr10/#File_Format.
|
||||
// It returns the variable top.
|
||||
func parseUCA(builder *build.Builder) {
|
||||
var r io.ReadCloser
|
||||
var err error
|
||||
for _, f := range openArchive().File {
|
||||
if strings.HasSuffix(f.Name, "allkeys_CLDR.txt") {
|
||||
r, err = f.Open()
|
||||
}
|
||||
}
|
||||
if r == nil {
|
||||
log.Fatal("File allkeys_CLDR.txt not found in archive.")
|
||||
}
|
||||
failOnError(err)
|
||||
defer r.Close()
|
||||
scanner := bufio.NewScanner(r)
|
||||
colelem := regexp.MustCompile(`\[([.*])([0-9A-F.]+)\]`)
|
||||
for i := 1; scanner.Scan(); i++ {
|
||||
line := scanner.Text()
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
if line[0] == '@' {
|
||||
// parse properties
|
||||
switch {
|
||||
case strings.HasPrefix(line[1:], "version "):
|
||||
a := strings.Split(line[1:], " ")
|
||||
if a[1] != gen.UnicodeVersion() {
|
||||
log.Fatalf("incompatible version %s; want %s", a[1], gen.UnicodeVersion())
|
||||
}
|
||||
case strings.HasPrefix(line[1:], "backwards "):
|
||||
log.Fatalf("%d: unsupported option backwards", i)
|
||||
default:
|
||||
log.Printf("%d: unknown option %s", i, line[1:])
|
||||
}
|
||||
} else {
|
||||
// parse entries
|
||||
part := strings.Split(line, " ; ")
|
||||
if len(part) != 2 {
|
||||
log.Fatalf("%d: production rule without ';': %v", i, line)
|
||||
}
|
||||
lhs := []rune{}
|
||||
for _, v := range strings.Split(part[0], " ") {
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
lhs = append(lhs, rune(convHex(i, v)))
|
||||
}
|
||||
var n int
|
||||
var vars []int
|
||||
rhs := [][]int{}
|
||||
for i, m := range colelem.FindAllStringSubmatch(part[1], -1) {
|
||||
n += len(m[0])
|
||||
elem := []int{}
|
||||
for _, h := range strings.Split(m[2], ".") {
|
||||
elem = append(elem, convHex(i, h))
|
||||
}
|
||||
if m[1] == "*" {
|
||||
vars = append(vars, i)
|
||||
}
|
||||
rhs = append(rhs, elem)
|
||||
}
|
||||
if len(part[1]) < n+3 || part[1][n+1] != '#' {
|
||||
log.Fatalf("%d: expected comment; found %s", i, part[1][n:])
|
||||
}
|
||||
if *test {
|
||||
testInput.add(string(lhs))
|
||||
}
|
||||
failOnError(builder.Add(lhs, rhs, vars))
|
||||
}
|
||||
}
|
||||
if scanner.Err() != nil {
|
||||
log.Fatal(scanner.Err())
|
||||
}
|
||||
}
|
||||
|
||||
func convHex(line int, s string) int {
|
||||
r, e := strconv.ParseInt(s, 16, 32)
|
||||
if e != nil {
|
||||
log.Fatalf("%d: %v", line, e)
|
||||
}
|
||||
return int(r)
|
||||
}
|
||||
|
||||
var testInput = stringSet{}
|
||||
|
||||
var charRe = regexp.MustCompile(`&#x([0-9A-F]*);`)
|
||||
var tagRe = regexp.MustCompile(`<([a-z_]*) */>`)
|
||||
|
||||
var mainLocales = []string{}
|
||||
|
||||
// charsets holds a list of exemplar characters per category.
|
||||
type charSets map[string][]string
|
||||
|
||||
func (p charSets) fprint(w io.Writer) {
|
||||
fmt.Fprintln(w, "[exN]string{")
|
||||
for i, k := range []string{"", "contractions", "punctuation", "auxiliary", "currencySymbol", "index"} {
|
||||
if set := p[k]; len(set) != 0 {
|
||||
fmt.Fprintf(w, "\t\t%d: %q,\n", i, strings.Join(set, " "))
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(w, "\t},")
|
||||
}
|
||||
|
||||
var localeChars = make(map[string]charSets)
|
||||
|
||||
const exemplarHeader = `
|
||||
type exemplarType int
|
||||
const (
|
||||
exCharacters exemplarType = iota
|
||||
exContractions
|
||||
exPunctuation
|
||||
exAuxiliary
|
||||
exCurrency
|
||||
exIndex
|
||||
exN
|
||||
)
|
||||
`
|
||||
|
||||
func printExemplarCharacters(w io.Writer) {
|
||||
fmt.Fprintln(w, exemplarHeader)
|
||||
fmt.Fprintln(w, "var exemplarCharacters = map[string][exN]string{")
|
||||
for _, loc := range mainLocales {
|
||||
fmt.Fprintf(w, "\t%q: ", loc)
|
||||
localeChars[loc].fprint(w)
|
||||
}
|
||||
fmt.Fprintln(w, "}")
|
||||
}
|
||||
|
||||
func decodeCLDR(d *cldr.Decoder) *cldr.CLDR {
|
||||
r := gen.OpenCLDRCoreZip()
|
||||
data, err := d.DecodeZip(r)
|
||||
failOnError(err)
|
||||
return data
|
||||
}
|
||||
|
||||
// parseMain parses XML files in the main directory of the CLDR core.zip file.
|
||||
func parseMain() {
|
||||
d := &cldr.Decoder{}
|
||||
d.SetDirFilter("main")
|
||||
d.SetSectionFilter("characters")
|
||||
data := decodeCLDR(d)
|
||||
for _, loc := range data.Locales() {
|
||||
x := data.RawLDML(loc)
|
||||
if skipLang(x.Identity.Language.Type) {
|
||||
continue
|
||||
}
|
||||
if x.Characters != nil {
|
||||
x, _ = data.LDML(loc)
|
||||
loc = language.Make(loc).String()
|
||||
for _, ec := range x.Characters.ExemplarCharacters {
|
||||
if ec.Draft != "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := localeChars[loc]; !ok {
|
||||
mainLocales = append(mainLocales, loc)
|
||||
localeChars[loc] = make(charSets)
|
||||
}
|
||||
localeChars[loc][ec.Type] = parseCharacters(ec.Data())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseCharacters(chars string) []string {
|
||||
parseSingle := func(s string) (r rune, tail string, escaped bool) {
|
||||
if s[0] == '\\' {
|
||||
return rune(s[1]), s[2:], true
|
||||
}
|
||||
r, sz := utf8.DecodeRuneInString(s)
|
||||
return r, s[sz:], false
|
||||
}
|
||||
chars = strings.TrimSpace(chars)
|
||||
if n := len(chars) - 1; chars[n] == ']' && chars[0] == '[' {
|
||||
chars = chars[1:n]
|
||||
}
|
||||
list := []string{}
|
||||
var r, last, end rune
|
||||
for len(chars) > 0 {
|
||||
if chars[0] == '{' { // character sequence
|
||||
buf := []rune{}
|
||||
for chars = chars[1:]; len(chars) > 0; {
|
||||
r, chars, _ = parseSingle(chars)
|
||||
if r == '}' {
|
||||
break
|
||||
}
|
||||
if r == ' ' {
|
||||
log.Fatalf("space not supported in sequence %q", chars)
|
||||
}
|
||||
buf = append(buf, r)
|
||||
}
|
||||
list = append(list, string(buf))
|
||||
last = 0
|
||||
} else { // single character
|
||||
escaped := false
|
||||
r, chars, escaped = parseSingle(chars)
|
||||
if r != ' ' {
|
||||
if r == '-' && !escaped {
|
||||
if last == 0 {
|
||||
log.Fatal("'-' should be preceded by a character")
|
||||
}
|
||||
end, chars, _ = parseSingle(chars)
|
||||
for ; last <= end; last++ {
|
||||
list = append(list, string(last))
|
||||
}
|
||||
last = 0
|
||||
} else {
|
||||
list = append(list, string(r))
|
||||
last = r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
var fileRe = regexp.MustCompile(`.*/collation/(.*)\.xml`)
|
||||
|
||||
// typeMap translates legacy type keys to their BCP47 equivalent.
|
||||
var typeMap = map[string]string{
|
||||
"phonebook": "phonebk",
|
||||
"traditional": "trad",
|
||||
}
|
||||
|
||||
// parseCollation parses XML files in the collation directory of the CLDR core.zip file.
|
||||
func parseCollation(b *build.Builder) {
|
||||
d := &cldr.Decoder{}
|
||||
d.SetDirFilter("collation")
|
||||
data := decodeCLDR(d)
|
||||
for _, loc := range data.Locales() {
|
||||
x, err := data.LDML(loc)
|
||||
failOnError(err)
|
||||
if skipLang(x.Identity.Language.Type) {
|
||||
continue
|
||||
}
|
||||
cs := x.Collations.Collation
|
||||
sl := cldr.MakeSlice(&cs)
|
||||
if len(types.s) == 0 {
|
||||
sl.SelectAnyOf("type", x.Collations.Default())
|
||||
} else if !types.all {
|
||||
sl.SelectAnyOf("type", types.s...)
|
||||
}
|
||||
sl.SelectOnePerGroup("alt", altInclude())
|
||||
|
||||
for _, c := range cs {
|
||||
id, err := language.Parse(loc)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "invalid locale: %q", err)
|
||||
continue
|
||||
}
|
||||
// Support both old- and new-style defaults.
|
||||
d := c.Type
|
||||
if x.Collations.DefaultCollation == nil {
|
||||
d = x.Collations.Default()
|
||||
} else {
|
||||
d = x.Collations.DefaultCollation.Data()
|
||||
}
|
||||
// We assume tables are being built either for search or collation,
|
||||
// but not both. For search the default is always "search".
|
||||
if d != c.Type && c.Type != "search" {
|
||||
typ := c.Type
|
||||
if len(c.Type) > 8 {
|
||||
typ = typeMap[c.Type]
|
||||
}
|
||||
id, err = id.SetTypeForKey("co", typ)
|
||||
failOnError(err)
|
||||
}
|
||||
t := b.Tailoring(id)
|
||||
c.Process(processor{t})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type processor struct {
|
||||
t *build.Tailoring
|
||||
}
|
||||
|
||||
func (p processor) Reset(anchor string, before int) (err error) {
|
||||
if before != 0 {
|
||||
err = p.t.SetAnchorBefore(anchor)
|
||||
} else {
|
||||
err = p.t.SetAnchor(anchor)
|
||||
}
|
||||
failOnError(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p processor) Insert(level int, str, context, extend string) error {
|
||||
str = context + str
|
||||
if *test {
|
||||
testInput.add(str)
|
||||
}
|
||||
// TODO: mimic bug in old maketables: remove.
|
||||
err := p.t.Insert(colltab.Level(level-1), str, context+extend)
|
||||
failOnError(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p processor) Index(id string) {
|
||||
}
|
||||
|
||||
func testCollator(c *collate.Collator) {
|
||||
c0 := collate.New(language.Und)
|
||||
|
||||
// iterator over all characters for all locales and check
|
||||
// whether Key is equal.
|
||||
buf := collate.Buffer{}
|
||||
|
||||
// Add all common and not too uncommon runes to the test set.
|
||||
for i := rune(0); i < 0x30000; i++ {
|
||||
testInput.add(string(i))
|
||||
}
|
||||
for i := rune(0xE0000); i < 0xF0000; i++ {
|
||||
testInput.add(string(i))
|
||||
}
|
||||
for _, str := range testInput.values() {
|
||||
k0 := c0.KeyFromString(&buf, str)
|
||||
k := c.KeyFromString(&buf, str)
|
||||
if !bytes.Equal(k0, k) {
|
||||
failOnError(fmt.Errorf("test:%U: keys differ (%x vs %x)", []rune(str), k0, k))
|
||||
}
|
||||
buf.Reset()
|
||||
}
|
||||
fmt.Println("PASS")
|
||||
}
|
||||
|
||||
func main() {
|
||||
gen.Init()
|
||||
b := build.NewBuilder()
|
||||
parseUCA(b)
|
||||
if tables.contains("chars") {
|
||||
parseMain()
|
||||
}
|
||||
parseCollation(b)
|
||||
|
||||
c, err := b.Build()
|
||||
failOnError(err)
|
||||
|
||||
if *test {
|
||||
testCollator(collate.NewFromTable(c))
|
||||
} else {
|
||||
w := &bytes.Buffer{}
|
||||
|
||||
gen.WriteUnicodeVersion(w)
|
||||
gen.WriteCLDRVersion(w)
|
||||
|
||||
if tables.contains("collate") {
|
||||
_, err = b.Print(w)
|
||||
failOnError(err)
|
||||
}
|
||||
if tables.contains("chars") {
|
||||
printExemplarCharacters(w)
|
||||
}
|
||||
gen.WriteGoFile("tables.go", *pkg, w.Bytes())
|
||||
}
|
||||
}
|
||||
239
vendor/golang.org/x/text/collate/option.go
generated
vendored
Normal file
239
vendor/golang.org/x/text/collate/option.go
generated
vendored
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
// Copyright 2014 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 collate
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"golang.org/x/text/collate/colltab"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// newCollator creates a new collator with default options configured.
|
||||
func newCollator(t colltab.Weighter) *Collator {
|
||||
// Initialize a collator with default options.
|
||||
c := &Collator{
|
||||
options: options{
|
||||
ignore: [colltab.NumLevels]bool{
|
||||
colltab.Quaternary: true,
|
||||
colltab.Identity: true,
|
||||
},
|
||||
f: norm.NFD,
|
||||
t: t,
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: store vt in tags or remove.
|
||||
c.variableTop = t.Top()
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// An Option is used to change the behavior of a Collator. Options override the
|
||||
// settings passed through the locale identifier.
|
||||
type Option struct {
|
||||
priority int
|
||||
f func(o *options)
|
||||
}
|
||||
|
||||
type prioritizedOptions []Option
|
||||
|
||||
func (p prioritizedOptions) Len() int {
|
||||
return len(p)
|
||||
}
|
||||
|
||||
func (p prioritizedOptions) Swap(i, j int) {
|
||||
p[i], p[j] = p[j], p[i]
|
||||
}
|
||||
|
||||
func (p prioritizedOptions) Less(i, j int) bool {
|
||||
return p[i].priority < p[j].priority
|
||||
}
|
||||
|
||||
type options struct {
|
||||
// ignore specifies which levels to ignore.
|
||||
ignore [colltab.NumLevels]bool
|
||||
|
||||
// caseLevel is true if there is an additional level of case matching
|
||||
// between the secondary and tertiary levels.
|
||||
caseLevel bool
|
||||
|
||||
// backwards specifies the order of sorting at the secondary level.
|
||||
// This option exists predominantly to support reverse sorting of accents in French.
|
||||
backwards bool
|
||||
|
||||
// numeric specifies whether any sequence of decimal digits (category is Nd)
|
||||
// is sorted at a primary level with its numeric value.
|
||||
// For example, "A-21" < "A-123".
|
||||
// This option is set by wrapping the main Weighter with NewNumericWeighter.
|
||||
numeric bool
|
||||
|
||||
// alternate specifies an alternative handling of variables.
|
||||
alternate alternateHandling
|
||||
|
||||
// variableTop is the largest primary value that is considered to be
|
||||
// variable.
|
||||
variableTop uint32
|
||||
|
||||
t colltab.Weighter
|
||||
|
||||
f norm.Form
|
||||
}
|
||||
|
||||
func (o *options) setOptions(opts []Option) {
|
||||
sort.Sort(prioritizedOptions(opts))
|
||||
for _, x := range opts {
|
||||
x.f(o)
|
||||
}
|
||||
}
|
||||
|
||||
// OptionsFromTag extracts the BCP47 collation options from the tag and
|
||||
// configures a collator accordingly. These options are set before any other
|
||||
// option.
|
||||
func OptionsFromTag(t language.Tag) Option {
|
||||
return Option{0, func(o *options) {
|
||||
o.setFromTag(t)
|
||||
}}
|
||||
}
|
||||
|
||||
func (o *options) setFromTag(t language.Tag) {
|
||||
o.caseLevel = ldmlBool(t, o.caseLevel, "kc")
|
||||
o.backwards = ldmlBool(t, o.backwards, "kb")
|
||||
o.numeric = ldmlBool(t, o.numeric, "kn")
|
||||
|
||||
// Extract settings from the BCP47 u extension.
|
||||
switch t.TypeForKey("ks") { // strength
|
||||
case "level1":
|
||||
o.ignore[colltab.Secondary] = true
|
||||
o.ignore[colltab.Tertiary] = true
|
||||
case "level2":
|
||||
o.ignore[colltab.Tertiary] = true
|
||||
case "level3", "":
|
||||
// The default.
|
||||
case "level4":
|
||||
o.ignore[colltab.Quaternary] = false
|
||||
case "identic":
|
||||
o.ignore[colltab.Quaternary] = false
|
||||
o.ignore[colltab.Identity] = false
|
||||
}
|
||||
|
||||
switch t.TypeForKey("ka") {
|
||||
case "shifted":
|
||||
o.alternate = altShifted
|
||||
// The following two types are not official BCP47, but we support them to
|
||||
// give access to this otherwise hidden functionality. The name blanked is
|
||||
// derived from the LDML name blanked and posix reflects the main use of
|
||||
// the shift-trimmed option.
|
||||
case "blanked":
|
||||
o.alternate = altBlanked
|
||||
case "posix":
|
||||
o.alternate = altShiftTrimmed
|
||||
}
|
||||
|
||||
// TODO: caseFirst ("kf"), reorder ("kr"), and maybe variableTop ("vt").
|
||||
|
||||
// Not used:
|
||||
// - normalization ("kk", not necessary for this implementation)
|
||||
// - hiraganaQuatenary ("kh", obsolete)
|
||||
}
|
||||
|
||||
func ldmlBool(t language.Tag, old bool, key string) bool {
|
||||
switch t.TypeForKey(key) {
|
||||
case "true":
|
||||
return true
|
||||
case "false":
|
||||
return false
|
||||
default:
|
||||
return old
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// IgnoreCase sets case-insensitive comparison.
|
||||
IgnoreCase Option = ignoreCase
|
||||
ignoreCase = Option{3, ignoreCaseF}
|
||||
|
||||
// IgnoreDiacritics causes diacritical marks to be ignored. ("o" == "ö").
|
||||
IgnoreDiacritics Option = ignoreDiacritics
|
||||
ignoreDiacritics = Option{3, ignoreDiacriticsF}
|
||||
|
||||
// IgnoreWidth causes full-width characters to match their half-width
|
||||
// equivalents.
|
||||
IgnoreWidth Option = ignoreWidth
|
||||
ignoreWidth = Option{2, ignoreWidthF}
|
||||
|
||||
// Loose sets the collator to ignore diacritics, case and weight.
|
||||
Loose Option = loose
|
||||
loose = Option{4, looseF}
|
||||
|
||||
// Force ordering if strings are equivalent but not equal.
|
||||
Force Option = force
|
||||
force = Option{5, forceF}
|
||||
|
||||
// Numeric specifies that numbers should sort numerically ("2" < "12").
|
||||
Numeric Option = numeric
|
||||
numeric = Option{5, numericF}
|
||||
)
|
||||
|
||||
func ignoreWidthF(o *options) {
|
||||
o.ignore[colltab.Tertiary] = true
|
||||
o.caseLevel = true
|
||||
}
|
||||
|
||||
func ignoreDiacriticsF(o *options) {
|
||||
o.ignore[colltab.Secondary] = true
|
||||
}
|
||||
|
||||
func ignoreCaseF(o *options) {
|
||||
o.ignore[colltab.Tertiary] = true
|
||||
o.caseLevel = false
|
||||
}
|
||||
|
||||
func looseF(o *options) {
|
||||
ignoreWidthF(o)
|
||||
ignoreDiacriticsF(o)
|
||||
ignoreCaseF(o)
|
||||
}
|
||||
|
||||
func forceF(o *options) {
|
||||
o.ignore[colltab.Identity] = false
|
||||
}
|
||||
|
||||
func numericF(o *options) { o.numeric = true }
|
||||
|
||||
// Reorder overrides the pre-defined ordering of scripts and character sets.
|
||||
func Reorder(s ...string) Option {
|
||||
// TODO: need fractional weights to implement this.
|
||||
panic("TODO: implement")
|
||||
}
|
||||
|
||||
// TODO: consider making these public again. These options cannot be fully
|
||||
// specified in BCP47, so an API interface seems warranted. Still a higher-level
|
||||
// interface would be nice (e.g. a POSIX option for enabling altShiftTrimmed)
|
||||
|
||||
// alternateHandling identifies the various ways in which variables are handled.
|
||||
// A rune with a primary weight lower than the variable top is considered a
|
||||
// variable.
|
||||
// See http://www.unicode.org/reports/tr10/#Variable_Weighting for details.
|
||||
type alternateHandling int
|
||||
|
||||
const (
|
||||
// altNonIgnorable turns off special handling of variables.
|
||||
altNonIgnorable alternateHandling = iota
|
||||
|
||||
// altBlanked sets variables and all subsequent primary ignorables to be
|
||||
// ignorable at all levels. This is identical to removing all variables
|
||||
// and subsequent primary ignorables from the input.
|
||||
altBlanked
|
||||
|
||||
// altShifted sets variables to be ignorable for levels one through three and
|
||||
// adds a fourth level based on the values of the ignored levels.
|
||||
altShifted
|
||||
|
||||
// altShiftTrimmed is a slight variant of altShifted that is used to
|
||||
// emulate POSIX.
|
||||
altShiftTrimmed
|
||||
)
|
||||
209
vendor/golang.org/x/text/collate/option_test.go
generated
vendored
Normal file
209
vendor/golang.org/x/text/collate/option_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
// Copyright 2014 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 collate
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/collate/colltab"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultIgnore = ignore(colltab.Tertiary)
|
||||
defaultTable = colltab.Init(locales[0])
|
||||
)
|
||||
|
||||
func TestOptions(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
in []Option
|
||||
out options
|
||||
}{
|
||||
0: {
|
||||
out: options{
|
||||
ignore: defaultIgnore,
|
||||
},
|
||||
},
|
||||
1: {
|
||||
in: []Option{IgnoreDiacritics},
|
||||
out: options{
|
||||
ignore: [colltab.NumLevels]bool{false, true, false, true, true},
|
||||
},
|
||||
},
|
||||
2: {
|
||||
in: []Option{IgnoreCase, IgnoreDiacritics},
|
||||
out: options{
|
||||
ignore: ignore(colltab.Primary),
|
||||
},
|
||||
},
|
||||
3: {
|
||||
in: []Option{ignoreDiacritics, IgnoreWidth},
|
||||
out: options{
|
||||
ignore: ignore(colltab.Primary),
|
||||
caseLevel: true,
|
||||
},
|
||||
},
|
||||
4: {
|
||||
in: []Option{IgnoreWidth, ignoreDiacritics},
|
||||
out: options{
|
||||
ignore: ignore(colltab.Primary),
|
||||
caseLevel: true,
|
||||
},
|
||||
},
|
||||
5: {
|
||||
in: []Option{IgnoreCase, IgnoreWidth},
|
||||
out: options{
|
||||
ignore: ignore(colltab.Secondary),
|
||||
},
|
||||
},
|
||||
6: {
|
||||
in: []Option{IgnoreCase, IgnoreWidth, Loose},
|
||||
out: options{
|
||||
ignore: ignore(colltab.Primary),
|
||||
},
|
||||
},
|
||||
7: {
|
||||
in: []Option{Force, IgnoreCase, IgnoreWidth, Loose},
|
||||
out: options{
|
||||
ignore: [colltab.NumLevels]bool{false, true, true, true, false},
|
||||
},
|
||||
},
|
||||
8: {
|
||||
in: []Option{IgnoreDiacritics, IgnoreCase},
|
||||
out: options{
|
||||
ignore: ignore(colltab.Primary),
|
||||
},
|
||||
},
|
||||
9: {
|
||||
in: []Option{Numeric},
|
||||
out: options{
|
||||
ignore: defaultIgnore,
|
||||
numeric: true,
|
||||
},
|
||||
},
|
||||
10: {
|
||||
in: []Option{OptionsFromTag(language.MustParse("und-u-ks-level1"))},
|
||||
out: options{
|
||||
ignore: ignore(colltab.Primary),
|
||||
},
|
||||
},
|
||||
11: {
|
||||
in: []Option{OptionsFromTag(language.MustParse("und-u-ks-level4"))},
|
||||
out: options{
|
||||
ignore: ignore(colltab.Quaternary),
|
||||
},
|
||||
},
|
||||
12: {
|
||||
in: []Option{OptionsFromTag(language.MustParse("und-u-ks-identic"))},
|
||||
out: options{},
|
||||
},
|
||||
13: {
|
||||
in: []Option{
|
||||
OptionsFromTag(language.MustParse("und-u-kn-true-kb-true-kc-true")),
|
||||
},
|
||||
out: options{
|
||||
ignore: defaultIgnore,
|
||||
caseLevel: true,
|
||||
backwards: true,
|
||||
numeric: true,
|
||||
},
|
||||
},
|
||||
14: {
|
||||
in: []Option{
|
||||
OptionsFromTag(language.MustParse("und-u-kn-true-kb-true-kc-true")),
|
||||
OptionsFromTag(language.MustParse("und-u-kn-false-kb-false-kc-false")),
|
||||
},
|
||||
out: options{
|
||||
ignore: defaultIgnore,
|
||||
},
|
||||
},
|
||||
15: {
|
||||
in: []Option{
|
||||
OptionsFromTag(language.MustParse("und-u-kn-true-kb-true-kc-true")),
|
||||
OptionsFromTag(language.MustParse("und-u-kn-foo-kb-foo-kc-foo")),
|
||||
},
|
||||
out: options{
|
||||
ignore: defaultIgnore,
|
||||
caseLevel: true,
|
||||
backwards: true,
|
||||
numeric: true,
|
||||
},
|
||||
},
|
||||
16: { // Normal options take precedence over tag options.
|
||||
in: []Option{
|
||||
Numeric, IgnoreCase,
|
||||
OptionsFromTag(language.MustParse("und-u-kn-false-kc-true")),
|
||||
},
|
||||
out: options{
|
||||
ignore: ignore(colltab.Secondary),
|
||||
caseLevel: false,
|
||||
numeric: true,
|
||||
},
|
||||
},
|
||||
17: {
|
||||
in: []Option{
|
||||
OptionsFromTag(language.MustParse("und-u-ka-shifted")),
|
||||
},
|
||||
out: options{
|
||||
ignore: defaultIgnore,
|
||||
alternate: altShifted,
|
||||
},
|
||||
},
|
||||
18: {
|
||||
in: []Option{
|
||||
OptionsFromTag(language.MustParse("und-u-ka-blanked")),
|
||||
},
|
||||
out: options{
|
||||
ignore: defaultIgnore,
|
||||
alternate: altBlanked,
|
||||
},
|
||||
},
|
||||
19: {
|
||||
in: []Option{
|
||||
OptionsFromTag(language.MustParse("und-u-ka-posix")),
|
||||
},
|
||||
out: options{
|
||||
ignore: defaultIgnore,
|
||||
alternate: altShiftTrimmed,
|
||||
},
|
||||
},
|
||||
} {
|
||||
c := newCollator(defaultTable)
|
||||
c.t = nil
|
||||
c.variableTop = 0
|
||||
c.f = 0
|
||||
|
||||
c.setOptions(tt.in)
|
||||
if !reflect.DeepEqual(c.options, tt.out) {
|
||||
t.Errorf("%d: got %v; want %v", i, c.options, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlternateSortTypes(t *testing.T) {
|
||||
testCases := []struct {
|
||||
lang string
|
||||
in []string
|
||||
want []string
|
||||
}{{
|
||||
lang: "zh,cmn,zh-Hant-u-co-pinyin,zh-HK-u-co-pinyin,zh-pinyin",
|
||||
in: []string{"爸爸", "妈妈", "儿子", "女儿"},
|
||||
want: []string{"爸爸", "儿子", "妈妈", "女儿"},
|
||||
}, {
|
||||
lang: "zh-Hant,zh-u-co-stroke,zh-Hant-u-co-stroke",
|
||||
in: []string{"爸爸", "妈妈", "儿子", "女儿"},
|
||||
want: []string{"儿子", "女儿", "妈妈", "爸爸"},
|
||||
}}
|
||||
for _, tc := range testCases {
|
||||
for _, tag := range strings.Split(tc.lang, ",") {
|
||||
got := append([]string{}, tc.in...)
|
||||
New(language.MustParse(tag)).SortStrings(got)
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("New(%s).SortStrings(%v) = %v; want %v", tag, tc.in, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
230
vendor/golang.org/x/text/collate/reg_test.go
generated
vendored
Normal file
230
vendor/golang.org/x/text/collate/reg_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
// Copyright 2012 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 collate
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/collate/build"
|
||||
"golang.org/x/text/internal/gen"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
var long = flag.Bool("long", false,
|
||||
"run time-consuming tests, such as tests that fetch data online")
|
||||
|
||||
// This regression test runs tests for the test files in CollationTest.zip
|
||||
// (taken from http://www.unicode.org/Public/UCA/<gen.UnicodeVersion()>/).
|
||||
//
|
||||
// The test files have the following form:
|
||||
// # header
|
||||
// 0009 0021; # ('\u0009') <CHARACTER TABULATION> [| | | 0201 025E]
|
||||
// 0009 003F; # ('\u0009') <CHARACTER TABULATION> [| | | 0201 0263]
|
||||
// 000A 0021; # ('\u000A') <LINE FEED (LF)> [| | | 0202 025E]
|
||||
// 000A 003F; # ('\u000A') <LINE FEED (LF)> [| | | 0202 0263]
|
||||
//
|
||||
// The part before the semicolon is the hex representation of a sequence
|
||||
// of runes. After the hash mark is a comment. The strings
|
||||
// represented by rune sequence are in the file in sorted order, as
|
||||
// defined by the DUCET.
|
||||
|
||||
type Test struct {
|
||||
name string
|
||||
str [][]byte
|
||||
comment []string
|
||||
}
|
||||
|
||||
var versionRe = regexp.MustCompile(`# UCA Version: (.*)\n?$`)
|
||||
var testRe = regexp.MustCompile(`^([\dA-F ]+);.*# (.*)\n?$`)
|
||||
|
||||
func TestCollation(t *testing.T) {
|
||||
if !gen.IsLocal() && !*long {
|
||||
t.Skip("skipping test to prevent downloading; to run use -long or use -local to specify a local source")
|
||||
}
|
||||
t.Skip("must first update to new file format to support test")
|
||||
for _, test := range loadTestData() {
|
||||
doTest(t, test)
|
||||
}
|
||||
}
|
||||
|
||||
func Error(e error) {
|
||||
if e != nil {
|
||||
log.Fatal(e)
|
||||
}
|
||||
}
|
||||
|
||||
// parseUCA parses a Default Unicode Collation Element Table of the format
|
||||
// specified in http://www.unicode.org/reports/tr10/#File_Format.
|
||||
// It returns the variable top.
|
||||
func parseUCA(builder *build.Builder) {
|
||||
r := gen.OpenUnicodeFile("UCA", "", "allkeys.txt")
|
||||
defer r.Close()
|
||||
input := bufio.NewReader(r)
|
||||
colelem := regexp.MustCompile(`\[([.*])([0-9A-F.]+)\]`)
|
||||
for i := 1; true; i++ {
|
||||
l, prefix, err := input.ReadLine()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
Error(err)
|
||||
line := string(l)
|
||||
if prefix {
|
||||
log.Fatalf("%d: buffer overflow", i)
|
||||
}
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
if line[0] == '@' {
|
||||
if strings.HasPrefix(line[1:], "version ") {
|
||||
if v := strings.Split(line[1:], " ")[1]; v != gen.UnicodeVersion() {
|
||||
log.Fatalf("incompatible version %s; want %s", v, gen.UnicodeVersion())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// parse entries
|
||||
part := strings.Split(line, " ; ")
|
||||
if len(part) != 2 {
|
||||
log.Fatalf("%d: production rule without ';': %v", i, line)
|
||||
}
|
||||
lhs := []rune{}
|
||||
for _, v := range strings.Split(part[0], " ") {
|
||||
if v != "" {
|
||||
lhs = append(lhs, rune(convHex(i, v)))
|
||||
}
|
||||
}
|
||||
vars := []int{}
|
||||
rhs := [][]int{}
|
||||
for i, m := range colelem.FindAllStringSubmatch(part[1], -1) {
|
||||
if m[1] == "*" {
|
||||
vars = append(vars, i)
|
||||
}
|
||||
elem := []int{}
|
||||
for _, h := range strings.Split(m[2], ".") {
|
||||
elem = append(elem, convHex(i, h))
|
||||
}
|
||||
rhs = append(rhs, elem)
|
||||
}
|
||||
builder.Add(lhs, rhs, vars)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func convHex(line int, s string) int {
|
||||
r, e := strconv.ParseInt(s, 16, 32)
|
||||
if e != nil {
|
||||
log.Fatalf("%d: %v", line, e)
|
||||
}
|
||||
return int(r)
|
||||
}
|
||||
|
||||
func loadTestData() []Test {
|
||||
f := gen.OpenUnicodeFile("UCA", "", "CollationTest.zip")
|
||||
buffer, err := ioutil.ReadAll(f)
|
||||
f.Close()
|
||||
Error(err)
|
||||
archive, err := zip.NewReader(bytes.NewReader(buffer), int64(len(buffer)))
|
||||
Error(err)
|
||||
tests := []Test{}
|
||||
for _, f := range archive.File {
|
||||
// Skip the short versions, which are simply duplicates of the long versions.
|
||||
if strings.Contains(f.Name, "SHORT") || f.FileInfo().IsDir() {
|
||||
continue
|
||||
}
|
||||
ff, err := f.Open()
|
||||
Error(err)
|
||||
defer ff.Close()
|
||||
scanner := bufio.NewScanner(ff)
|
||||
test := Test{name: path.Base(f.Name)}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if len(line) <= 1 || line[0] == '#' {
|
||||
if m := versionRe.FindStringSubmatch(line); m != nil {
|
||||
if m[1] != gen.UnicodeVersion() {
|
||||
log.Printf("warning:%s: version is %s; want %s", f.Name, m[1], gen.UnicodeVersion())
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
m := testRe.FindStringSubmatch(line)
|
||||
if m == nil || len(m) < 3 {
|
||||
log.Fatalf(`Failed to parse: "%s" result: %#v`, line, m)
|
||||
}
|
||||
str := []byte{}
|
||||
// In the regression test data (unpaired) surrogates are assigned a weight
|
||||
// corresponding to their code point value. However, utf8.DecodeRune,
|
||||
// which is used to compute the implicit weight, assigns FFFD to surrogates.
|
||||
// We therefore skip tests with surrogates. This skips about 35 entries
|
||||
// per test.
|
||||
valid := true
|
||||
for _, split := range strings.Split(m[1], " ") {
|
||||
r, err := strconv.ParseUint(split, 16, 64)
|
||||
Error(err)
|
||||
valid = valid && utf8.ValidRune(rune(r))
|
||||
str = append(str, string(rune(r))...)
|
||||
}
|
||||
if valid {
|
||||
test.str = append(test.str, str)
|
||||
test.comment = append(test.comment, m[2])
|
||||
}
|
||||
}
|
||||
if scanner.Err() != nil {
|
||||
log.Fatal(scanner.Err())
|
||||
}
|
||||
tests = append(tests, test)
|
||||
}
|
||||
return tests
|
||||
}
|
||||
|
||||
var errorCount int
|
||||
|
||||
func runes(b []byte) []rune {
|
||||
return []rune(string(b))
|
||||
}
|
||||
|
||||
var shifted = language.MustParse("und-u-ka-shifted-ks-level4")
|
||||
|
||||
func doTest(t *testing.T, tc Test) {
|
||||
bld := build.NewBuilder()
|
||||
parseUCA(bld)
|
||||
w, err := bld.Build()
|
||||
Error(err)
|
||||
var tag language.Tag
|
||||
if !strings.Contains(tc.name, "NON_IGNOR") {
|
||||
tag = shifted
|
||||
}
|
||||
c := NewFromTable(w, OptionsFromTag(tag))
|
||||
b := &Buffer{}
|
||||
prev := tc.str[0]
|
||||
for i := 1; i < len(tc.str); i++ {
|
||||
b.Reset()
|
||||
s := tc.str[i]
|
||||
ka := c.Key(b, prev)
|
||||
kb := c.Key(b, s)
|
||||
if r := bytes.Compare(ka, kb); r == 1 {
|
||||
t.Errorf("%s:%d: Key(%.4X) < Key(%.4X) (%X < %X) == %d; want -1 or 0", tc.name, i, []rune(string(prev)), []rune(string(s)), ka, kb, r)
|
||||
prev = s
|
||||
continue
|
||||
}
|
||||
if r := c.Compare(prev, s); r == 1 {
|
||||
t.Errorf("%s:%d: Compare(%.4X, %.4X) == %d; want -1 or 0", tc.name, i, runes(prev), runes(s), r)
|
||||
}
|
||||
if r := c.Compare(s, prev); r == -1 {
|
||||
t.Errorf("%s:%d: Compare(%.4X, %.4X) == %d; want 1 or 0", tc.name, i, runes(s), runes(prev), r)
|
||||
}
|
||||
prev = s
|
||||
}
|
||||
}
|
||||
81
vendor/golang.org/x/text/collate/sort.go
generated
vendored
Normal file
81
vendor/golang.org/x/text/collate/sort.go
generated
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// 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 collate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
maxSortBuffer = 40960
|
||||
maxSortEntries = 4096
|
||||
)
|
||||
|
||||
type swapper interface {
|
||||
Swap(i, j int)
|
||||
}
|
||||
|
||||
type sorter struct {
|
||||
buf *Buffer
|
||||
keys [][]byte
|
||||
src swapper
|
||||
}
|
||||
|
||||
func (s *sorter) init(n int) {
|
||||
if s.buf == nil {
|
||||
s.buf = &Buffer{}
|
||||
s.buf.init()
|
||||
}
|
||||
if cap(s.keys) < n {
|
||||
s.keys = make([][]byte, n)
|
||||
}
|
||||
s.keys = s.keys[0:n]
|
||||
}
|
||||
|
||||
func (s *sorter) sort(src swapper) {
|
||||
s.src = src
|
||||
sort.Sort(s)
|
||||
}
|
||||
|
||||
func (s sorter) Len() int {
|
||||
return len(s.keys)
|
||||
}
|
||||
|
||||
func (s sorter) Less(i, j int) bool {
|
||||
return bytes.Compare(s.keys[i], s.keys[j]) == -1
|
||||
}
|
||||
|
||||
func (s sorter) Swap(i, j int) {
|
||||
s.keys[i], s.keys[j] = s.keys[j], s.keys[i]
|
||||
s.src.Swap(i, j)
|
||||
}
|
||||
|
||||
// A Lister can be sorted by Collator's Sort method.
|
||||
type Lister interface {
|
||||
Len() int
|
||||
Swap(i, j int)
|
||||
// Bytes returns the bytes of the text at index i.
|
||||
Bytes(i int) []byte
|
||||
}
|
||||
|
||||
// Sort uses sort.Sort to sort the strings represented by x using the rules of c.
|
||||
func (c *Collator) Sort(x Lister) {
|
||||
n := x.Len()
|
||||
c.sorter.init(n)
|
||||
for i := 0; i < n; i++ {
|
||||
c.sorter.keys[i] = c.Key(c.sorter.buf, x.Bytes(i))
|
||||
}
|
||||
c.sorter.sort(x)
|
||||
}
|
||||
|
||||
// SortStrings uses sort.Sort to sort the strings in x using the rules of c.
|
||||
func (c *Collator) SortStrings(x []string) {
|
||||
c.sorter.init(len(x))
|
||||
for i, s := range x {
|
||||
c.sorter.keys[i] = c.KeyFromString(c.sorter.buf, s)
|
||||
}
|
||||
c.sorter.sort(sort.StringSlice(x))
|
||||
}
|
||||
55
vendor/golang.org/x/text/collate/sort_test.go
generated
vendored
Normal file
55
vendor/golang.org/x/text/collate/sort_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// 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 collate_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/collate"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func ExampleCollator_Strings() {
|
||||
c := collate.New(language.Und)
|
||||
strings := []string{
|
||||
"ad",
|
||||
"ab",
|
||||
"äb",
|
||||
"ac",
|
||||
}
|
||||
c.SortStrings(strings)
|
||||
fmt.Println(strings)
|
||||
// Output: [ab äb ac ad]
|
||||
}
|
||||
|
||||
type sorter []string
|
||||
|
||||
func (s sorter) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s sorter) Swap(i, j int) {
|
||||
s[j], s[i] = s[i], s[j]
|
||||
}
|
||||
|
||||
func (s sorter) Bytes(i int) []byte {
|
||||
return []byte(s[i])
|
||||
}
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
c := collate.New(language.English)
|
||||
strings := []string{
|
||||
"bcd",
|
||||
"abc",
|
||||
"ddd",
|
||||
}
|
||||
c.Sort(sorter(strings))
|
||||
res := fmt.Sprint(strings)
|
||||
want := "[abc bcd ddd]"
|
||||
if res != want {
|
||||
t.Errorf("found %s; want %s", res, want)
|
||||
}
|
||||
}
|
||||
291
vendor/golang.org/x/text/collate/table_test.go
generated
vendored
Normal file
291
vendor/golang.org/x/text/collate/table_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
// Copyright 2012 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 collate
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/collate/build"
|
||||
"golang.org/x/text/collate/colltab"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
type ColElems []Weights
|
||||
|
||||
type input struct {
|
||||
str string
|
||||
ces [][]int
|
||||
}
|
||||
|
||||
type check struct {
|
||||
in string
|
||||
n int
|
||||
out ColElems
|
||||
}
|
||||
|
||||
type tableTest struct {
|
||||
in []input
|
||||
chk []check
|
||||
}
|
||||
|
||||
func w(ce ...int) Weights {
|
||||
return W(ce...)
|
||||
}
|
||||
|
||||
var defaults = w(0)
|
||||
|
||||
func pt(p, t int) []int {
|
||||
return []int{p, defaults.Secondary, t}
|
||||
}
|
||||
|
||||
func makeTable(in []input) (*Collator, error) {
|
||||
b := build.NewBuilder()
|
||||
for _, r := range in {
|
||||
if e := b.Add([]rune(r.str), r.ces, nil); e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
t, err := b.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewFromTable(t), nil
|
||||
}
|
||||
|
||||
// modSeq holds a seqeunce of modifiers in increasing order of CCC long enough
|
||||
// to cause a segment overflow if not handled correctly. The last rune in this
|
||||
// list has a CCC of 214.
|
||||
var modSeq = []rune{
|
||||
0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BB,
|
||||
0x05BC, 0x05BD, 0x05BF, 0x05C1, 0x05C2, 0xFB1E, 0x064B, 0x064C, 0x064D, 0x064E,
|
||||
0x064F, 0x0650, 0x0651, 0x0652, 0x0670, 0x0711, 0x0C55, 0x0C56, 0x0E38, 0x0E48,
|
||||
0x0EB8, 0x0EC8, 0x0F71, 0x0F72, 0x0F74, 0x0321, 0x1DCE,
|
||||
}
|
||||
|
||||
var mods []input
|
||||
var modW = func() ColElems {
|
||||
ws := ColElems{}
|
||||
for _, r := range modSeq {
|
||||
rune := norm.NFC.PropertiesString(string(r))
|
||||
ws = append(ws, w(0, int(rune.CCC())))
|
||||
mods = append(mods, input{string(r), [][]int{{0, int(rune.CCC())}}})
|
||||
}
|
||||
return ws
|
||||
}()
|
||||
|
||||
var appendNextTests = []tableTest{
|
||||
{ // test getWeights
|
||||
[]input{
|
||||
{"a", [][]int{{100}}},
|
||||
{"b", [][]int{{105}}},
|
||||
{"c", [][]int{{110}}},
|
||||
{"ß", [][]int{{120}}},
|
||||
},
|
||||
[]check{
|
||||
{"a", 1, ColElems{w(100)}},
|
||||
{"b", 1, ColElems{w(105)}},
|
||||
{"c", 1, ColElems{w(110)}},
|
||||
{"d", 1, ColElems{w(0x50064)}},
|
||||
{"ab", 1, ColElems{w(100)}},
|
||||
{"bc", 1, ColElems{w(105)}},
|
||||
{"dd", 1, ColElems{w(0x50064)}},
|
||||
{"ß", 2, ColElems{w(120)}},
|
||||
},
|
||||
},
|
||||
{ // test expansion
|
||||
[]input{
|
||||
{"u", [][]int{{100}}},
|
||||
{"U", [][]int{{100}, {0, 25}}},
|
||||
{"w", [][]int{{100}, {100}}},
|
||||
{"W", [][]int{{100}, {0, 25}, {100}, {0, 25}}},
|
||||
},
|
||||
[]check{
|
||||
{"u", 1, ColElems{w(100)}},
|
||||
{"U", 1, ColElems{w(100), w(0, 25)}},
|
||||
{"w", 1, ColElems{w(100), w(100)}},
|
||||
{"W", 1, ColElems{w(100), w(0, 25), w(100), w(0, 25)}},
|
||||
},
|
||||
},
|
||||
{ // test decompose
|
||||
[]input{
|
||||
{"D", [][]int{pt(104, 8)}},
|
||||
{"z", [][]int{pt(130, 8)}},
|
||||
{"\u030C", [][]int{{0, 40}}}, // Caron
|
||||
{"\u01C5", [][]int{pt(104, 9), pt(130, 4), {0, 40, 0x1F}}}, // Dž = D+z+caron
|
||||
},
|
||||
[]check{
|
||||
{"\u01C5", 2, ColElems{w(pt(104, 9)...), w(pt(130, 4)...), w(0, 40, 0x1F)}},
|
||||
},
|
||||
},
|
||||
{ // test basic contraction
|
||||
[]input{
|
||||
{"a", [][]int{{100}}},
|
||||
{"ab", [][]int{{101}}},
|
||||
{"aab", [][]int{{101}, {101}}},
|
||||
{"abc", [][]int{{102}}},
|
||||
{"b", [][]int{{200}}},
|
||||
{"c", [][]int{{300}}},
|
||||
{"d", [][]int{{400}}},
|
||||
},
|
||||
[]check{
|
||||
{"a", 1, ColElems{w(100)}},
|
||||
{"aa", 1, ColElems{w(100)}},
|
||||
{"aac", 1, ColElems{w(100)}},
|
||||
{"d", 1, ColElems{w(400)}},
|
||||
{"ab", 2, ColElems{w(101)}},
|
||||
{"abb", 2, ColElems{w(101)}},
|
||||
{"aab", 3, ColElems{w(101), w(101)}},
|
||||
{"aaba", 3, ColElems{w(101), w(101)}},
|
||||
{"abc", 3, ColElems{w(102)}},
|
||||
{"abcd", 3, ColElems{w(102)}},
|
||||
},
|
||||
},
|
||||
{ // test discontinuous contraction
|
||||
append(mods, []input{
|
||||
// modifiers; secondary weight equals ccc
|
||||
{"\u0316", [][]int{{0, 220}}},
|
||||
{"\u0317", [][]int{{0, 220}, {0, 220}}},
|
||||
{"\u302D", [][]int{{0, 222}}},
|
||||
{"\u302E", [][]int{{0, 225}}}, // used as starter
|
||||
{"\u302F", [][]int{{0, 224}}}, // used as starter
|
||||
{"\u18A9", [][]int{{0, 228}}},
|
||||
{"\u0300", [][]int{{0, 230}}},
|
||||
{"\u0301", [][]int{{0, 230}}},
|
||||
{"\u0315", [][]int{{0, 232}}},
|
||||
{"\u031A", [][]int{{0, 232}}},
|
||||
{"\u035C", [][]int{{0, 233}}},
|
||||
{"\u035F", [][]int{{0, 233}}},
|
||||
{"\u035D", [][]int{{0, 234}}},
|
||||
{"\u035E", [][]int{{0, 234}}},
|
||||
{"\u0345", [][]int{{0, 240}}},
|
||||
|
||||
// starters
|
||||
{"a", [][]int{{100}}},
|
||||
{"b", [][]int{{200}}},
|
||||
{"c", [][]int{{300}}},
|
||||
{"\u03B1", [][]int{{900}}},
|
||||
{"\x01", [][]int{{0, 0, 0, 0}}},
|
||||
|
||||
// contractions
|
||||
{"a\u0300", [][]int{{101}}},
|
||||
{"a\u0301", [][]int{{102}}},
|
||||
{"a\u035E", [][]int{{110}}},
|
||||
{"a\u035Eb\u035E", [][]int{{115}}},
|
||||
{"ac\u035Eaca\u035E", [][]int{{116}}},
|
||||
{"a\u035Db\u035D", [][]int{{117}}},
|
||||
{"a\u0301\u035Db", [][]int{{120}}},
|
||||
{"a\u0301\u035F", [][]int{{121}}},
|
||||
{"a\u0301\u035Fb", [][]int{{119}}},
|
||||
{"\u03B1\u0345", [][]int{{901}, {902}}},
|
||||
{"\u302E\u302F", [][]int{{0, 131}, {0, 131}}},
|
||||
{"\u302F\u18A9", [][]int{{0, 130}}},
|
||||
}...),
|
||||
[]check{
|
||||
{"a\x01\u0300", 1, ColElems{w(100)}},
|
||||
{"ab", 1, ColElems{w(100)}}, // closing segment
|
||||
{"a\u0316\u0300b", 5, ColElems{w(101), w(0, 220)}}, // closing segment
|
||||
{"a\u0316\u0300", 5, ColElems{w(101), w(0, 220)}}, // no closing segment
|
||||
{"a\u0316\u0300\u035Cb", 5, ColElems{w(101), w(0, 220)}}, // completes before segment end
|
||||
{"a\u0316\u0300\u035C", 5, ColElems{w(101), w(0, 220)}}, // completes before segment end
|
||||
|
||||
{"a\u0316\u0301b", 5, ColElems{w(102), w(0, 220)}}, // closing segment
|
||||
{"a\u0316\u0301", 5, ColElems{w(102), w(0, 220)}}, // no closing segment
|
||||
{"a\u0316\u0301\u035Cb", 5, ColElems{w(102), w(0, 220)}}, // completes before segment end
|
||||
{"a\u0316\u0301\u035C", 5, ColElems{w(102), w(0, 220)}}, // completes before segment end
|
||||
|
||||
// match blocked by modifier with same ccc
|
||||
{"a\u0301\u0315\u031A\u035Fb", 3, ColElems{w(102)}},
|
||||
|
||||
// multiple gaps
|
||||
{"a\u0301\u035Db", 6, ColElems{w(120)}},
|
||||
{"a\u0301\u035F", 5, ColElems{w(121)}},
|
||||
{"a\u0301\u035Fb", 6, ColElems{w(119)}},
|
||||
{"a\u0316\u0301\u035F", 7, ColElems{w(121), w(0, 220)}},
|
||||
{"a\u0301\u0315\u035Fb", 7, ColElems{w(121), w(0, 232)}},
|
||||
{"a\u0316\u0301\u0315\u035Db", 5, ColElems{w(102), w(0, 220)}},
|
||||
{"a\u0316\u0301\u0315\u035F", 9, ColElems{w(121), w(0, 220), w(0, 232)}},
|
||||
{"a\u0316\u0301\u0315\u035Fb", 9, ColElems{w(121), w(0, 220), w(0, 232)}},
|
||||
{"a\u0316\u0301\u0315\u035F\u035D", 9, ColElems{w(121), w(0, 220), w(0, 232)}},
|
||||
{"a\u0316\u0301\u0315\u035F\u035Db", 9, ColElems{w(121), w(0, 220), w(0, 232)}},
|
||||
|
||||
// handling of segment overflow
|
||||
{ // just fits within segment
|
||||
"a" + string(modSeq[:30]) + "\u0301",
|
||||
3 + len(string(modSeq[:30])),
|
||||
append(ColElems{w(102)}, modW[:30]...),
|
||||
},
|
||||
{"a" + string(modSeq[:31]) + "\u0301", 1, ColElems{w(100)}}, // overflow
|
||||
{"a" + string(modSeq) + "\u0301", 1, ColElems{w(100)}},
|
||||
{ // just fits within segment with two interstitial runes
|
||||
"a" + string(modSeq[:28]) + "\u0301\u0315\u035F",
|
||||
7 + len(string(modSeq[:28])),
|
||||
append(append(ColElems{w(121)}, modW[:28]...), w(0, 232)),
|
||||
},
|
||||
{ // second half does not fit within segment
|
||||
"a" + string(modSeq[:29]) + "\u0301\u0315\u035F",
|
||||
3 + len(string(modSeq[:29])),
|
||||
append(ColElems{w(102)}, modW[:29]...),
|
||||
},
|
||||
|
||||
// discontinuity can only occur in last normalization segment
|
||||
{"a\u035Eb\u035E", 6, ColElems{w(115)}},
|
||||
{"a\u0316\u035Eb\u035E", 5, ColElems{w(110), w(0, 220)}},
|
||||
{"a\u035Db\u035D", 6, ColElems{w(117)}},
|
||||
{"a\u0316\u035Db\u035D", 1, ColElems{w(100)}},
|
||||
{"a\u035Eb\u0316\u035E", 8, ColElems{w(115), w(0, 220)}},
|
||||
{"a\u035Db\u0316\u035D", 8, ColElems{w(117), w(0, 220)}},
|
||||
{"ac\u035Eaca\u035E", 9, ColElems{w(116)}},
|
||||
{"a\u0316c\u035Eaca\u035E", 1, ColElems{w(100)}},
|
||||
{"ac\u035Eac\u0316a\u035E", 1, ColElems{w(100)}},
|
||||
|
||||
// expanding contraction
|
||||
{"\u03B1\u0345", 4, ColElems{w(901), w(902)}},
|
||||
|
||||
// Theoretical possibilities
|
||||
// contraction within a gap
|
||||
{"a\u302F\u18A9\u0301", 9, ColElems{w(102), w(0, 130)}},
|
||||
// expansion within a gap
|
||||
{"a\u0317\u0301", 5, ColElems{w(102), w(0, 220), w(0, 220)}},
|
||||
// repeating CCC blocks last modifier
|
||||
{"a\u302E\u302F\u0301", 1, ColElems{w(100)}},
|
||||
// The trailing combining characters (with lower CCC) should block the first one.
|
||||
// TODO: make the following pass.
|
||||
// {"a\u035E\u0316\u0316", 1, ColElems{w(100)}},
|
||||
{"a\u035F\u035Eb", 5, ColElems{w(110), w(0, 233)}},
|
||||
// Last combiner should match after normalization.
|
||||
// TODO: make the following pass.
|
||||
// {"a\u035D\u0301", 3, ColElems{w(102), w(0, 234)}},
|
||||
// The first combiner is blocking the second one as they have the same CCC.
|
||||
{"a\u035D\u035Eb", 1, ColElems{w(100)}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestAppendNext(t *testing.T) {
|
||||
for i, tt := range appendNextTests {
|
||||
c, err := makeTable(tt.in)
|
||||
if err != nil {
|
||||
t.Errorf("%d: error creating table: %v", i, err)
|
||||
continue
|
||||
}
|
||||
for j, chk := range tt.chk {
|
||||
ws, n := c.t.AppendNext(nil, []byte(chk.in))
|
||||
if n != chk.n {
|
||||
t.Errorf("%d:%d: bytes consumed was %d; want %d", i, j, n, chk.n)
|
||||
}
|
||||
out := convertFromWeights(chk.out)
|
||||
if len(ws) != len(out) {
|
||||
t.Errorf("%d:%d: len(ws) was %d; want %d (%X vs %X)\n%X", i, j, len(ws), len(out), ws, out, chk.in)
|
||||
continue
|
||||
}
|
||||
for k, w := range ws {
|
||||
w, _ = colltab.MakeElem(w.Primary(), w.Secondary(), int(w.Tertiary()), 0)
|
||||
if w != out[k] {
|
||||
t.Errorf("%d:%d: Weights %d was %X; want %X", i, j, k, w, out[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73789
vendor/golang.org/x/text/collate/tables.go
generated
vendored
Normal file
73789
vendor/golang.org/x/text/collate/tables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
7
vendor/golang.org/x/text/collate/tools/colcmp/Makefile
generated
vendored
Normal file
7
vendor/golang.org/x/text/collate/tools/colcmp/Makefile
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Copyright 2012 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.
|
||||
|
||||
chars:
|
||||
go run ../../maketables.go -tables=chars -package=main > chars.go
|
||||
gofmt -w -s chars.go
|
||||
1156
vendor/golang.org/x/text/collate/tools/colcmp/chars.go
generated
vendored
Normal file
1156
vendor/golang.org/x/text/collate/tools/colcmp/chars.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
97
vendor/golang.org/x/text/collate/tools/colcmp/col.go
generated
vendored
Normal file
97
vendor/golang.org/x/text/collate/tools/colcmp/col.go
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2012 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 main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"unicode/utf16"
|
||||
|
||||
"golang.org/x/text/collate"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Input holds an input string in both UTF-8 and UTF-16 format.
|
||||
type Input struct {
|
||||
index int // used for restoring to original random order
|
||||
UTF8 []byte
|
||||
UTF16 []uint16
|
||||
key []byte // used for sorting
|
||||
}
|
||||
|
||||
func (i Input) String() string {
|
||||
return string(i.UTF8)
|
||||
}
|
||||
|
||||
func makeInput(s8 []byte, s16 []uint16) Input {
|
||||
return Input{UTF8: s8, UTF16: s16}
|
||||
}
|
||||
|
||||
func makeInputString(s string) Input {
|
||||
return Input{
|
||||
UTF8: []byte(s),
|
||||
UTF16: utf16.Encode([]rune(s)),
|
||||
}
|
||||
}
|
||||
|
||||
// Collator is an interface for architecture-specific implementations of collation.
|
||||
type Collator interface {
|
||||
// Key generates a sort key for the given input. Implemenations
|
||||
// may return nil if a collator does not support sort keys.
|
||||
Key(s Input) []byte
|
||||
|
||||
// Compare returns -1 if a < b, 1 if a > b and 0 if a == b.
|
||||
Compare(a, b Input) int
|
||||
}
|
||||
|
||||
// CollatorFactory creates a Collator for a given language tag.
|
||||
type CollatorFactory struct {
|
||||
name string
|
||||
makeFn func(tag string) (Collator, error)
|
||||
description string
|
||||
}
|
||||
|
||||
var collators = []CollatorFactory{}
|
||||
|
||||
// AddFactory registers f as a factory for an implementation of Collator.
|
||||
func AddFactory(f CollatorFactory) {
|
||||
collators = append(collators, f)
|
||||
}
|
||||
|
||||
func getCollator(name, locale string) Collator {
|
||||
for _, f := range collators {
|
||||
if f.name == name {
|
||||
col, err := f.makeFn(locale)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return col
|
||||
}
|
||||
}
|
||||
log.Fatalf("collator of type %q not found", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// goCollator is an implemention of Collator using go's own collator.
|
||||
type goCollator struct {
|
||||
c *collate.Collator
|
||||
buf collate.Buffer
|
||||
}
|
||||
|
||||
func init() {
|
||||
AddFactory(CollatorFactory{"go", newGoCollator, "Go's native collator implementation."})
|
||||
}
|
||||
|
||||
func newGoCollator(loc string) (Collator, error) {
|
||||
c := &goCollator{c: collate.New(language.Make(loc))}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *goCollator) Key(b Input) []byte {
|
||||
return c.c.Key(&c.buf, b.UTF8)
|
||||
}
|
||||
|
||||
func (c *goCollator) Compare(a, b Input) int {
|
||||
return c.c.Compare(a.UTF8, b.UTF8)
|
||||
}
|
||||
529
vendor/golang.org/x/text/collate/tools/colcmp/colcmp.go
generated
vendored
Normal file
529
vendor/golang.org/x/text/collate/tools/colcmp/colcmp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,529 @@
|
|||
// Copyright 2012 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 main // import "golang.org/x/text/collate/tools/colcmp"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
var (
|
||||
doNorm = flag.Bool("norm", false, "normalize input strings")
|
||||
cases = flag.Bool("case", false, "generate case variants")
|
||||
verbose = flag.Bool("verbose", false, "print results")
|
||||
debug = flag.Bool("debug", false, "output debug information")
|
||||
locales = flag.String("locale", "en_US", "the locale to use. May be a comma-separated list for some commands.")
|
||||
col = flag.String("col", "go", "collator to test")
|
||||
gold = flag.String("gold", "go", "collator used as the gold standard")
|
||||
usecmp = flag.Bool("usecmp", false,
|
||||
`use comparison instead of sort keys when sorting. Must be "test", "gold" or "both"`)
|
||||
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
|
||||
exclude = flag.String("exclude", "", "exclude errors that contain any of the characters")
|
||||
limit = flag.Int("limit", 5000000, "maximum number of samples to generate for one run")
|
||||
)
|
||||
|
||||
func failOnError(err error) {
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test holds test data for testing a locale-collator pair.
|
||||
// Test also provides functionality that is commonly used by the various commands.
|
||||
type Test struct {
|
||||
ctxt *Context
|
||||
Name string
|
||||
Locale string
|
||||
ColName string
|
||||
|
||||
Col Collator
|
||||
UseCompare bool
|
||||
|
||||
Input []Input
|
||||
Duration time.Duration
|
||||
|
||||
start time.Time
|
||||
msg string
|
||||
count int
|
||||
}
|
||||
|
||||
func (t *Test) clear() {
|
||||
t.Col = nil
|
||||
t.Input = nil
|
||||
}
|
||||
|
||||
const (
|
||||
msgGeneratingInput = "generating input"
|
||||
msgGeneratingKeys = "generating keys"
|
||||
msgSorting = "sorting"
|
||||
)
|
||||
|
||||
var lastLen = 0
|
||||
|
||||
func (t *Test) SetStatus(msg string) {
|
||||
if *debug || *verbose {
|
||||
fmt.Printf("%s: %s...\n", t.Name, msg)
|
||||
} else if t.ctxt.out != nil {
|
||||
fmt.Fprint(t.ctxt.out, strings.Repeat(" ", lastLen))
|
||||
fmt.Fprint(t.ctxt.out, strings.Repeat("\b", lastLen))
|
||||
fmt.Fprint(t.ctxt.out, msg, "...")
|
||||
lastLen = len(msg) + 3
|
||||
fmt.Fprint(t.ctxt.out, strings.Repeat("\b", lastLen))
|
||||
}
|
||||
}
|
||||
|
||||
// Start is used by commands to signal the start of an operation.
|
||||
func (t *Test) Start(msg string) {
|
||||
t.SetStatus(msg)
|
||||
t.count = 0
|
||||
t.msg = msg
|
||||
t.start = time.Now()
|
||||
}
|
||||
|
||||
// Stop is used by commands to signal the end of an operation.
|
||||
func (t *Test) Stop() (time.Duration, int) {
|
||||
d := time.Now().Sub(t.start)
|
||||
t.Duration += d
|
||||
if *debug || *verbose {
|
||||
fmt.Printf("%s: %s done. (%.3fs /%dK ops)\n", t.Name, t.msg, d.Seconds(), t.count/1000)
|
||||
}
|
||||
return d, t.count
|
||||
}
|
||||
|
||||
// generateKeys generates sort keys for all the inputs.
|
||||
func (t *Test) generateKeys() {
|
||||
for i, s := range t.Input {
|
||||
b := t.Col.Key(s)
|
||||
t.Input[i].key = b
|
||||
if *debug {
|
||||
fmt.Printf("%s (%X): %X\n", string(s.UTF8), s.UTF16, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort sorts the inputs. It generates sort keys if this is required by the
|
||||
// chosen sort method.
|
||||
func (t *Test) Sort() (tkey, tsort time.Duration, nkey, nsort int) {
|
||||
if *cpuprofile != "" {
|
||||
f, err := os.Create(*cpuprofile)
|
||||
failOnError(err)
|
||||
pprof.StartCPUProfile(f)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
if t.UseCompare || t.Col.Key(t.Input[0]) == nil {
|
||||
t.Start(msgSorting)
|
||||
sort.Sort(&testCompare{*t})
|
||||
tsort, nsort = t.Stop()
|
||||
} else {
|
||||
t.Start(msgGeneratingKeys)
|
||||
t.generateKeys()
|
||||
t.count = len(t.Input)
|
||||
tkey, nkey = t.Stop()
|
||||
t.Start(msgSorting)
|
||||
sort.Sort(t)
|
||||
tsort, nsort = t.Stop()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Test) Swap(a, b int) {
|
||||
t.Input[a], t.Input[b] = t.Input[b], t.Input[a]
|
||||
}
|
||||
|
||||
func (t *Test) Less(a, b int) bool {
|
||||
t.count++
|
||||
return bytes.Compare(t.Input[a].key, t.Input[b].key) == -1
|
||||
}
|
||||
|
||||
func (t Test) Len() int {
|
||||
return len(t.Input)
|
||||
}
|
||||
|
||||
type testCompare struct {
|
||||
Test
|
||||
}
|
||||
|
||||
func (t *testCompare) Less(a, b int) bool {
|
||||
t.count++
|
||||
return t.Col.Compare(t.Input[a], t.Input[b]) == -1
|
||||
}
|
||||
|
||||
type testRestore struct {
|
||||
Test
|
||||
}
|
||||
|
||||
func (t *testRestore) Less(a, b int) bool {
|
||||
return t.Input[a].index < t.Input[b].index
|
||||
}
|
||||
|
||||
// GenerateInput generates input phrases for the locale tested by t.
|
||||
func (t *Test) GenerateInput() {
|
||||
t.Input = nil
|
||||
if t.ctxt.lastLocale != t.Locale {
|
||||
gen := phraseGenerator{}
|
||||
gen.init(t.Locale)
|
||||
t.SetStatus(msgGeneratingInput)
|
||||
t.ctxt.lastInput = nil // allow the previous value to be garbage collected.
|
||||
t.Input = gen.generate(*doNorm)
|
||||
t.ctxt.lastInput = t.Input
|
||||
t.ctxt.lastLocale = t.Locale
|
||||
} else {
|
||||
t.Input = t.ctxt.lastInput
|
||||
for i := range t.Input {
|
||||
t.Input[i].key = nil
|
||||
}
|
||||
sort.Sort(&testRestore{*t})
|
||||
}
|
||||
}
|
||||
|
||||
// Context holds all tests and settings translated from command line options.
|
||||
type Context struct {
|
||||
test []*Test
|
||||
last *Test
|
||||
|
||||
lastLocale string
|
||||
lastInput []Input
|
||||
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (ts *Context) Printf(format string, a ...interface{}) {
|
||||
ts.assertBuf()
|
||||
fmt.Fprintf(ts.out, format, a...)
|
||||
}
|
||||
|
||||
func (ts *Context) Print(a ...interface{}) {
|
||||
ts.assertBuf()
|
||||
fmt.Fprint(ts.out, a...)
|
||||
}
|
||||
|
||||
// assertBuf sets up an io.Writer for ouput, if it doesn't already exist.
|
||||
// In debug and verbose mode, output is buffered so that the regular output
|
||||
// will not interfere with the additional output. Otherwise, output is
|
||||
// written directly to stdout for a more responsive feel.
|
||||
func (ts *Context) assertBuf() {
|
||||
if ts.out != nil {
|
||||
return
|
||||
}
|
||||
if *debug || *verbose {
|
||||
ts.out = &bytes.Buffer{}
|
||||
} else {
|
||||
ts.out = os.Stdout
|
||||
}
|
||||
}
|
||||
|
||||
// flush flushes the contents of ts.out to stdout, if it is not stdout already.
|
||||
func (ts *Context) flush() {
|
||||
if ts.out != nil {
|
||||
if _, ok := ts.out.(io.ReadCloser); !ok {
|
||||
io.Copy(os.Stdout, ts.out.(io.Reader))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseTests creates all tests from command lines and returns
|
||||
// a Context to hold them.
|
||||
func parseTests() *Context {
|
||||
ctxt := &Context{}
|
||||
colls := strings.Split(*col, ",")
|
||||
for _, loc := range strings.Split(*locales, ",") {
|
||||
loc = strings.TrimSpace(loc)
|
||||
for _, name := range colls {
|
||||
name = strings.TrimSpace(name)
|
||||
col := getCollator(name, loc)
|
||||
ctxt.test = append(ctxt.test, &Test{
|
||||
ctxt: ctxt,
|
||||
Locale: loc,
|
||||
ColName: name,
|
||||
UseCompare: *usecmp,
|
||||
Col: col,
|
||||
})
|
||||
}
|
||||
}
|
||||
return ctxt
|
||||
}
|
||||
|
||||
func (c *Context) Len() int {
|
||||
return len(c.test)
|
||||
}
|
||||
|
||||
func (c *Context) Test(i int) *Test {
|
||||
if c.last != nil {
|
||||
c.last.clear()
|
||||
}
|
||||
c.last = c.test[i]
|
||||
return c.last
|
||||
}
|
||||
|
||||
func parseInput(args []string) []Input {
|
||||
input := []Input{}
|
||||
for _, s := range args {
|
||||
rs := []rune{}
|
||||
for len(s) > 0 {
|
||||
var r rune
|
||||
r, _, s, _ = strconv.UnquoteChar(s, '\'')
|
||||
rs = append(rs, r)
|
||||
}
|
||||
s = string(rs)
|
||||
if *doNorm {
|
||||
s = norm.NFD.String(s)
|
||||
}
|
||||
input = append(input, makeInputString(s))
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
// A Command is an implementation of a colcmp command.
|
||||
type Command struct {
|
||||
Run func(cmd *Context, args []string)
|
||||
Usage string
|
||||
Short string
|
||||
Long string
|
||||
}
|
||||
|
||||
func (cmd Command) Name() string {
|
||||
return strings.SplitN(cmd.Usage, " ", 2)[0]
|
||||
}
|
||||
|
||||
var commands = []*Command{
|
||||
cmdSort,
|
||||
cmdBench,
|
||||
cmdRegress,
|
||||
}
|
||||
|
||||
const sortHelp = `
|
||||
Sort sorts a given list of strings. Strings are separated by whitespace.
|
||||
`
|
||||
|
||||
var cmdSort = &Command{
|
||||
Run: runSort,
|
||||
Usage: "sort <string>*",
|
||||
Short: "sort a given list of strings",
|
||||
Long: sortHelp,
|
||||
}
|
||||
|
||||
func runSort(ctxt *Context, args []string) {
|
||||
input := parseInput(args)
|
||||
if len(input) == 0 {
|
||||
log.Fatalf("Nothing to sort.")
|
||||
}
|
||||
if ctxt.Len() > 1 {
|
||||
ctxt.Print("COLL LOCALE RESULT\n")
|
||||
}
|
||||
for i := 0; i < ctxt.Len(); i++ {
|
||||
t := ctxt.Test(i)
|
||||
t.Input = append(t.Input, input...)
|
||||
t.Sort()
|
||||
if ctxt.Len() > 1 {
|
||||
ctxt.Printf("%-5s %-5s ", t.ColName, t.Locale)
|
||||
}
|
||||
for _, s := range t.Input {
|
||||
ctxt.Print(string(s.UTF8), " ")
|
||||
}
|
||||
ctxt.Print("\n")
|
||||
}
|
||||
}
|
||||
|
||||
const benchHelp = `
|
||||
Bench runs a benchmark for the given list of collator implementations.
|
||||
If no collator implementations are given, the go collator will be used.
|
||||
`
|
||||
|
||||
var cmdBench = &Command{
|
||||
Run: runBench,
|
||||
Usage: "bench",
|
||||
Short: "benchmark a given list of collator implementations",
|
||||
Long: benchHelp,
|
||||
}
|
||||
|
||||
func runBench(ctxt *Context, args []string) {
|
||||
ctxt.Printf("%-7s %-5s %-6s %-24s %-24s %-5s %s\n", "LOCALE", "COLL", "N", "KEYS", "SORT", "AVGLN", "TOTAL")
|
||||
for i := 0; i < ctxt.Len(); i++ {
|
||||
t := ctxt.Test(i)
|
||||
ctxt.Printf("%-7s %-5s ", t.Locale, t.ColName)
|
||||
t.GenerateInput()
|
||||
ctxt.Printf("%-6s ", fmt.Sprintf("%dK", t.Len()/1000))
|
||||
tkey, tsort, nkey, nsort := t.Sort()
|
||||
p := func(dur time.Duration, n int) {
|
||||
s := ""
|
||||
if dur > 0 {
|
||||
s = fmt.Sprintf("%6.3fs ", dur.Seconds())
|
||||
if n > 0 {
|
||||
s += fmt.Sprintf("%15s", fmt.Sprintf("(%4.2f ns/op)", float64(dur)/float64(n)))
|
||||
}
|
||||
}
|
||||
ctxt.Printf("%-24s ", s)
|
||||
}
|
||||
p(tkey, nkey)
|
||||
p(tsort, nsort)
|
||||
|
||||
total := 0
|
||||
for _, s := range t.Input {
|
||||
total += len(s.key)
|
||||
}
|
||||
ctxt.Printf("%-5d ", total/t.Len())
|
||||
ctxt.Printf("%6.3fs\n", t.Duration.Seconds())
|
||||
if *debug {
|
||||
for _, s := range t.Input {
|
||||
fmt.Print(string(s.UTF8), " ")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const regressHelp = `
|
||||
Regress runs a monkey test by comparing the results of randomly generated tests
|
||||
between two implementations of a collator. The user may optionally pass a list
|
||||
of strings to regress against instead of the default test set.
|
||||
`
|
||||
|
||||
var cmdRegress = &Command{
|
||||
Run: runRegress,
|
||||
Usage: "regress -gold=<col> -test=<col> [string]*",
|
||||
Short: "run a monkey test between two collators",
|
||||
Long: regressHelp,
|
||||
}
|
||||
|
||||
const failedKeyCompare = `
|
||||
%s:%d: incorrect comparison result for input:
|
||||
a: %q (%.4X)
|
||||
key: %s
|
||||
b: %q (%.4X)
|
||||
key: %s
|
||||
Compare(a, b) = %d; want %d.
|
||||
|
||||
gold keys:
|
||||
a: %s
|
||||
b: %s
|
||||
`
|
||||
|
||||
const failedCompare = `
|
||||
%s:%d: incorrect comparison result for input:
|
||||
a: %q (%.4X)
|
||||
b: %q (%.4X)
|
||||
Compare(a, b) = %d; want %d.
|
||||
`
|
||||
|
||||
func keyStr(b []byte) string {
|
||||
buf := &bytes.Buffer{}
|
||||
for _, v := range b {
|
||||
fmt.Fprintf(buf, "%.2X ", v)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func runRegress(ctxt *Context, args []string) {
|
||||
input := parseInput(args)
|
||||
for i := 0; i < ctxt.Len(); i++ {
|
||||
t := ctxt.Test(i)
|
||||
if len(input) > 0 {
|
||||
t.Input = append(t.Input, input...)
|
||||
} else {
|
||||
t.GenerateInput()
|
||||
}
|
||||
t.Sort()
|
||||
count := 0
|
||||
gold := getCollator(*gold, t.Locale)
|
||||
for i := 1; i < len(t.Input); i++ {
|
||||
ia := t.Input[i-1]
|
||||
ib := t.Input[i]
|
||||
if bytes.IndexAny(ib.UTF8, *exclude) != -1 {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if bytes.IndexAny(ia.UTF8, *exclude) != -1 {
|
||||
continue
|
||||
}
|
||||
goldCmp := gold.Compare(ia, ib)
|
||||
if cmp := bytes.Compare(ia.key, ib.key); cmp != goldCmp {
|
||||
count++
|
||||
a := string(ia.UTF8)
|
||||
b := string(ib.UTF8)
|
||||
fmt.Printf(failedKeyCompare, t.Locale, i-1, a, []rune(a), keyStr(ia.key), b, []rune(b), keyStr(ib.key), cmp, goldCmp, keyStr(gold.Key(ia)), keyStr(gold.Key(ib)))
|
||||
} else if cmp := t.Col.Compare(ia, ib); cmp != goldCmp {
|
||||
count++
|
||||
a := string(ia.UTF8)
|
||||
b := string(ib.UTF8)
|
||||
fmt.Printf(failedCompare, t.Locale, i-1, a, []rune(a), b, []rune(b), cmp, goldCmp)
|
||||
}
|
||||
}
|
||||
if count > 0 {
|
||||
ctxt.Printf("Found %d inconsistencies in %d entries.\n", count, t.Len()-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const helpTemplate = `
|
||||
colcmp is a tool for testing and benchmarking collation
|
||||
|
||||
Usage: colcmp command [arguments]
|
||||
|
||||
The commands are:
|
||||
{{range .}}
|
||||
{{.Name | printf "%-11s"}} {{.Short}}{{end}}
|
||||
|
||||
Use "col help [topic]" for more information about that topic.
|
||||
`
|
||||
|
||||
const detailedHelpTemplate = `
|
||||
Usage: colcmp {{.Usage}}
|
||||
|
||||
{{.Long | trim}}
|
||||
`
|
||||
|
||||
func runHelp(args []string) {
|
||||
t := template.New("help")
|
||||
t.Funcs(template.FuncMap{"trim": strings.TrimSpace})
|
||||
if len(args) < 1 {
|
||||
template.Must(t.Parse(helpTemplate))
|
||||
failOnError(t.Execute(os.Stderr, &commands))
|
||||
} else {
|
||||
for _, cmd := range commands {
|
||||
if cmd.Name() == args[0] {
|
||||
template.Must(t.Parse(detailedHelpTemplate))
|
||||
failOnError(t.Execute(os.Stderr, cmd))
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
log.Fatalf("Unknown command %q. Run 'colcmp help'.", args[0])
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
log.SetFlags(0)
|
||||
|
||||
ctxt := parseTests()
|
||||
|
||||
if flag.NArg() < 1 {
|
||||
runHelp(nil)
|
||||
}
|
||||
args := flag.Args()[1:]
|
||||
if flag.Arg(0) == "help" {
|
||||
runHelp(args)
|
||||
}
|
||||
for _, cmd := range commands {
|
||||
if cmd.Name() == flag.Arg(0) {
|
||||
cmd.Run(ctxt, args)
|
||||
ctxt.flush()
|
||||
return
|
||||
}
|
||||
}
|
||||
runHelp(flag.Args())
|
||||
}
|
||||
111
vendor/golang.org/x/text/collate/tools/colcmp/darwin.go
generated
vendored
Normal file
111
vendor/golang.org/x/text/collate/tools/colcmp/darwin.go
generated
vendored
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build darwin
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -framework CoreFoundation
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func init() {
|
||||
AddFactory(CollatorFactory{"osx", newOSX16Collator,
|
||||
"OS X/Darwin collator, using native strings."})
|
||||
AddFactory(CollatorFactory{"osx8", newOSX8Collator,
|
||||
"OS X/Darwin collator for UTF-8."})
|
||||
}
|
||||
|
||||
func osxUInt8P(s []byte) *C.UInt8 {
|
||||
return (*C.UInt8)(unsafe.Pointer(&s[0]))
|
||||
}
|
||||
|
||||
func osxCharP(s []uint16) *C.UniChar {
|
||||
return (*C.UniChar)(unsafe.Pointer(&s[0]))
|
||||
}
|
||||
|
||||
// osxCollator implements an Collator based on OS X's CoreFoundation.
|
||||
type osxCollator struct {
|
||||
loc C.CFLocaleRef
|
||||
opt C.CFStringCompareFlags
|
||||
}
|
||||
|
||||
func (c *osxCollator) init(locale string) {
|
||||
l := C.CFStringCreateWithBytes(
|
||||
nil,
|
||||
osxUInt8P([]byte(locale)),
|
||||
C.CFIndex(len(locale)),
|
||||
C.kCFStringEncodingUTF8,
|
||||
C.Boolean(0),
|
||||
)
|
||||
c.loc = C.CFLocaleCreate(nil, l)
|
||||
}
|
||||
|
||||
func newOSX8Collator(locale string) (Collator, error) {
|
||||
c := &osx8Collator{}
|
||||
c.init(locale)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func newOSX16Collator(locale string) (Collator, error) {
|
||||
c := &osx16Collator{}
|
||||
c.init(locale)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c osxCollator) Key(s Input) []byte {
|
||||
return nil // sort keys not supported by OS X CoreFoundation
|
||||
}
|
||||
|
||||
type osx8Collator struct {
|
||||
osxCollator
|
||||
}
|
||||
|
||||
type osx16Collator struct {
|
||||
osxCollator
|
||||
}
|
||||
|
||||
func (c osx16Collator) Compare(a, b Input) int {
|
||||
sa := C.CFStringCreateWithCharactersNoCopy(
|
||||
nil,
|
||||
osxCharP(a.UTF16),
|
||||
C.CFIndex(len(a.UTF16)),
|
||||
nil,
|
||||
)
|
||||
sb := C.CFStringCreateWithCharactersNoCopy(
|
||||
nil,
|
||||
osxCharP(b.UTF16),
|
||||
C.CFIndex(len(b.UTF16)),
|
||||
nil,
|
||||
)
|
||||
_range := C.CFRangeMake(0, C.CFStringGetLength(sa))
|
||||
return int(C.CFStringCompareWithOptionsAndLocale(sa, sb, _range, c.opt, c.loc))
|
||||
}
|
||||
|
||||
func (c osx8Collator) Compare(a, b Input) int {
|
||||
sa := C.CFStringCreateWithBytesNoCopy(
|
||||
nil,
|
||||
osxUInt8P(a.UTF8),
|
||||
C.CFIndex(len(a.UTF8)),
|
||||
C.kCFStringEncodingUTF8,
|
||||
C.Boolean(0),
|
||||
nil,
|
||||
)
|
||||
sb := C.CFStringCreateWithBytesNoCopy(
|
||||
nil,
|
||||
osxUInt8P(b.UTF8),
|
||||
C.CFIndex(len(b.UTF8)),
|
||||
C.kCFStringEncodingUTF8,
|
||||
C.Boolean(0),
|
||||
nil,
|
||||
)
|
||||
_range := C.CFRangeMake(0, C.CFStringGetLength(sa))
|
||||
return int(C.CFStringCompareWithOptionsAndLocale(sa, sb, _range, c.opt, c.loc))
|
||||
}
|
||||
183
vendor/golang.org/x/text/collate/tools/colcmp/gen.go
generated
vendored
Normal file
183
vendor/golang.org/x/text/collate/tools/colcmp/gen.go
generated
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
// Copyright 2012 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 main
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// TODO: replace with functionality in language package.
|
||||
// parent computes the parent language for the given language.
|
||||
// It returns false if the parent is already root.
|
||||
func parent(locale string) (parent string, ok bool) {
|
||||
if locale == "und" {
|
||||
return "", false
|
||||
}
|
||||
if i := strings.LastIndex(locale, "-"); i != -1 {
|
||||
return locale[:i], true
|
||||
}
|
||||
return "und", true
|
||||
}
|
||||
|
||||
// rewriter is used to both unique strings and create variants of strings
|
||||
// to add to the test set.
|
||||
type rewriter struct {
|
||||
seen map[string]bool
|
||||
addCases bool
|
||||
}
|
||||
|
||||
func newRewriter() *rewriter {
|
||||
return &rewriter{
|
||||
seen: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rewriter) insert(a []string, s string) []string {
|
||||
if !r.seen[s] {
|
||||
r.seen[s] = true
|
||||
a = append(a, s)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// rewrite takes a sequence of strings in, adds variants of the these strings
|
||||
// based on options and removes duplicates.
|
||||
func (r *rewriter) rewrite(ss []string) []string {
|
||||
ns := []string{}
|
||||
for _, s := range ss {
|
||||
ns = r.insert(ns, s)
|
||||
if r.addCases {
|
||||
rs := []rune(s)
|
||||
rn := rs[0]
|
||||
for c := unicode.SimpleFold(rn); c != rn; c = unicode.SimpleFold(c) {
|
||||
rs[0] = c
|
||||
ns = r.insert(ns, string(rs))
|
||||
}
|
||||
}
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
// exemplarySet holds a parsed set of characters from the exemplarCharacters table.
|
||||
type exemplarySet struct {
|
||||
typ exemplarType
|
||||
set []string
|
||||
charIndex int // cumulative total of phrases, including this set
|
||||
}
|
||||
|
||||
type phraseGenerator struct {
|
||||
sets [exN]exemplarySet
|
||||
n int
|
||||
}
|
||||
|
||||
func (g *phraseGenerator) init(id string) {
|
||||
ec := exemplarCharacters
|
||||
loc := language.Make(id).String()
|
||||
// get sets for locale or parent locale if the set is not defined.
|
||||
for i := range g.sets {
|
||||
for p, ok := loc, true; ok; p, ok = parent(p) {
|
||||
if set, ok := ec[p]; ok && set[i] != "" {
|
||||
g.sets[i].set = strings.Split(set[i], " ")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
r := newRewriter()
|
||||
r.addCases = *cases
|
||||
for i := range g.sets {
|
||||
g.sets[i].set = r.rewrite(g.sets[i].set)
|
||||
}
|
||||
// compute indexes
|
||||
for i, set := range g.sets {
|
||||
g.n += len(set.set)
|
||||
g.sets[i].charIndex = g.n
|
||||
}
|
||||
}
|
||||
|
||||
// phrase returns the ith phrase, where i < g.n.
|
||||
func (g *phraseGenerator) phrase(i int) string {
|
||||
for _, set := range g.sets {
|
||||
if i < set.charIndex {
|
||||
return set.set[i-(set.charIndex-len(set.set))]
|
||||
}
|
||||
}
|
||||
panic("index out of range")
|
||||
}
|
||||
|
||||
// generate generates inputs by combining all pairs of examplar strings.
|
||||
// If doNorm is true, all input strings are normalized to NFC.
|
||||
// TODO: allow other variations, statistical models, and random
|
||||
// trailing sequences.
|
||||
func (g *phraseGenerator) generate(doNorm bool) []Input {
|
||||
const (
|
||||
M = 1024 * 1024
|
||||
buf8Size = 30 * M
|
||||
buf16Size = 10 * M
|
||||
)
|
||||
// TODO: use a better way to limit the input size.
|
||||
if sq := int(math.Sqrt(float64(*limit))); g.n > sq {
|
||||
g.n = sq
|
||||
}
|
||||
size := g.n * g.n
|
||||
a := make([]Input, 0, size)
|
||||
buf8 := make([]byte, 0, buf8Size)
|
||||
buf16 := make([]uint16, 0, buf16Size)
|
||||
|
||||
addInput := func(str string) {
|
||||
buf8 = buf8[len(buf8):]
|
||||
buf16 = buf16[len(buf16):]
|
||||
if len(str) > cap(buf8) {
|
||||
buf8 = make([]byte, 0, buf8Size)
|
||||
}
|
||||
if len(str) > cap(buf16) {
|
||||
buf16 = make([]uint16, 0, buf16Size)
|
||||
}
|
||||
if doNorm {
|
||||
buf8 = norm.NFD.AppendString(buf8, str)
|
||||
} else {
|
||||
buf8 = append(buf8, str...)
|
||||
}
|
||||
buf16 = appendUTF16(buf16, buf8)
|
||||
a = append(a, makeInput(buf8, buf16))
|
||||
}
|
||||
for i := 0; i < g.n; i++ {
|
||||
p1 := g.phrase(i)
|
||||
addInput(p1)
|
||||
for j := 0; j < g.n; j++ {
|
||||
p2 := g.phrase(j)
|
||||
addInput(p1 + p2)
|
||||
}
|
||||
}
|
||||
// permutate
|
||||
rnd := rand.New(rand.NewSource(int64(rand.Int())))
|
||||
for i := range a {
|
||||
j := i + rnd.Intn(len(a)-i)
|
||||
a[i], a[j] = a[j], a[i]
|
||||
a[i].index = i // allow restoring this order if input is used multiple times.
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func appendUTF16(buf []uint16, s []byte) []uint16 {
|
||||
for len(s) > 0 {
|
||||
r, sz := utf8.DecodeRune(s)
|
||||
s = s[sz:]
|
||||
r1, r2 := utf16.EncodeRune(r)
|
||||
if r1 != 0xFFFD {
|
||||
buf = append(buf, uint16(r1), uint16(r2))
|
||||
} else {
|
||||
buf = append(buf, uint16(r))
|
||||
}
|
||||
}
|
||||
return buf
|
||||
}
|
||||
209
vendor/golang.org/x/text/collate/tools/colcmp/icu.go
generated
vendored
Normal file
209
vendor/golang.org/x/text/collate/tools/colcmp/icu.go
generated
vendored
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build icu
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -licui18n -licuuc
|
||||
#include <stdlib.h>
|
||||
#include <unicode/ucol.h>
|
||||
#include <unicode/uiter.h>
|
||||
#include <unicode/utypes.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func init() {
|
||||
AddFactory(CollatorFactory{"icu", newUTF16,
|
||||
"Main ICU collator, using native strings."})
|
||||
AddFactory(CollatorFactory{"icu8", newUTF8iter,
|
||||
"ICU collator using ICU iterators to process UTF8."})
|
||||
AddFactory(CollatorFactory{"icu16", newUTF8conv,
|
||||
"ICU collation by first converting UTF8 to UTF16."})
|
||||
}
|
||||
|
||||
func icuCharP(s []byte) *C.char {
|
||||
return (*C.char)(unsafe.Pointer(&s[0]))
|
||||
}
|
||||
|
||||
func icuUInt8P(s []byte) *C.uint8_t {
|
||||
return (*C.uint8_t)(unsafe.Pointer(&s[0]))
|
||||
}
|
||||
|
||||
func icuUCharP(s []uint16) *C.UChar {
|
||||
return (*C.UChar)(unsafe.Pointer(&s[0]))
|
||||
}
|
||||
func icuULen(s []uint16) C.int32_t {
|
||||
return C.int32_t(len(s))
|
||||
}
|
||||
func icuSLen(s []byte) C.int32_t {
|
||||
return C.int32_t(len(s))
|
||||
}
|
||||
|
||||
// icuCollator implements a Collator based on ICU.
|
||||
type icuCollator struct {
|
||||
loc *C.char
|
||||
col *C.UCollator
|
||||
keyBuf []byte
|
||||
}
|
||||
|
||||
const growBufSize = 10 * 1024 * 1024
|
||||
|
||||
func (c *icuCollator) init(locale string) error {
|
||||
err := C.UErrorCode(0)
|
||||
c.loc = C.CString(locale)
|
||||
c.col = C.ucol_open(c.loc, &err)
|
||||
if err > 0 {
|
||||
return fmt.Errorf("failed opening collator for %q", locale)
|
||||
} else if err < 0 {
|
||||
loc := C.ucol_getLocaleByType(c.col, 0, &err)
|
||||
fmt, ok := map[int]string{
|
||||
-127: "warning: using default collator: %s",
|
||||
-128: "warning: using fallback collator: %s",
|
||||
}[int(err)]
|
||||
if ok {
|
||||
log.Printf(fmt, C.GoString(loc))
|
||||
}
|
||||
}
|
||||
c.keyBuf = make([]byte, 0, growBufSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *icuCollator) buf() (*C.uint8_t, C.int32_t) {
|
||||
if len(c.keyBuf) == cap(c.keyBuf) {
|
||||
c.keyBuf = make([]byte, 0, growBufSize)
|
||||
}
|
||||
b := c.keyBuf[len(c.keyBuf):cap(c.keyBuf)]
|
||||
return icuUInt8P(b), icuSLen(b)
|
||||
}
|
||||
|
||||
func (c *icuCollator) extendBuf(n C.int32_t) []byte {
|
||||
end := len(c.keyBuf) + int(n)
|
||||
if end > cap(c.keyBuf) {
|
||||
if len(c.keyBuf) == 0 {
|
||||
log.Fatalf("icuCollator: max string size exceeded: %v > %v", n, growBufSize)
|
||||
}
|
||||
c.keyBuf = make([]byte, 0, growBufSize)
|
||||
return nil
|
||||
}
|
||||
b := c.keyBuf[len(c.keyBuf):end]
|
||||
c.keyBuf = c.keyBuf[:end]
|
||||
return b
|
||||
}
|
||||
|
||||
func (c *icuCollator) Close() error {
|
||||
C.ucol_close(c.col)
|
||||
C.free(unsafe.Pointer(c.loc))
|
||||
return nil
|
||||
}
|
||||
|
||||
// icuUTF16 implements the Collator interface.
|
||||
type icuUTF16 struct {
|
||||
icuCollator
|
||||
}
|
||||
|
||||
func newUTF16(locale string) (Collator, error) {
|
||||
c := &icuUTF16{}
|
||||
return c, c.init(locale)
|
||||
}
|
||||
|
||||
func (c *icuUTF16) Compare(a, b Input) int {
|
||||
return int(C.ucol_strcoll(c.col, icuUCharP(a.UTF16), icuULen(a.UTF16), icuUCharP(b.UTF16), icuULen(b.UTF16)))
|
||||
}
|
||||
|
||||
func (c *icuUTF16) Key(s Input) []byte {
|
||||
bp, bn := c.buf()
|
||||
n := C.ucol_getSortKey(c.col, icuUCharP(s.UTF16), icuULen(s.UTF16), bp, bn)
|
||||
if b := c.extendBuf(n); b != nil {
|
||||
return b
|
||||
}
|
||||
return c.Key(s)
|
||||
}
|
||||
|
||||
// icuUTF8iter implements the Collator interface
|
||||
// This implementation wraps the UTF8 string in an iterator
|
||||
// which is passed to the collator.
|
||||
type icuUTF8iter struct {
|
||||
icuCollator
|
||||
a, b C.UCharIterator
|
||||
}
|
||||
|
||||
func newUTF8iter(locale string) (Collator, error) {
|
||||
c := &icuUTF8iter{}
|
||||
return c, c.init(locale)
|
||||
}
|
||||
|
||||
func (c *icuUTF8iter) Compare(a, b Input) int {
|
||||
err := C.UErrorCode(0)
|
||||
C.uiter_setUTF8(&c.a, icuCharP(a.UTF8), icuSLen(a.UTF8))
|
||||
C.uiter_setUTF8(&c.b, icuCharP(b.UTF8), icuSLen(b.UTF8))
|
||||
return int(C.ucol_strcollIter(c.col, &c.a, &c.b, &err))
|
||||
}
|
||||
|
||||
func (c *icuUTF8iter) Key(s Input) []byte {
|
||||
err := C.UErrorCode(0)
|
||||
state := [2]C.uint32_t{}
|
||||
C.uiter_setUTF8(&c.a, icuCharP(s.UTF8), icuSLen(s.UTF8))
|
||||
bp, bn := c.buf()
|
||||
n := C.ucol_nextSortKeyPart(c.col, &c.a, &(state[0]), bp, bn, &err)
|
||||
if n >= bn {
|
||||
// Force failure.
|
||||
if c.extendBuf(n+1) != nil {
|
||||
log.Fatal("expected extension to fail")
|
||||
}
|
||||
return c.Key(s)
|
||||
}
|
||||
return c.extendBuf(n)
|
||||
}
|
||||
|
||||
// icuUTF8conv implementes the Collator interface.
|
||||
// This implentation first converts the give UTF8 string
|
||||
// to UTF16 and then calls the main ICU collation function.
|
||||
type icuUTF8conv struct {
|
||||
icuCollator
|
||||
}
|
||||
|
||||
func newUTF8conv(locale string) (Collator, error) {
|
||||
c := &icuUTF8conv{}
|
||||
return c, c.init(locale)
|
||||
}
|
||||
|
||||
func (c *icuUTF8conv) Compare(sa, sb Input) int {
|
||||
a := encodeUTF16(sa.UTF8)
|
||||
b := encodeUTF16(sb.UTF8)
|
||||
return int(C.ucol_strcoll(c.col, icuUCharP(a), icuULen(a), icuUCharP(b), icuULen(b)))
|
||||
}
|
||||
|
||||
func (c *icuUTF8conv) Key(s Input) []byte {
|
||||
a := encodeUTF16(s.UTF8)
|
||||
bp, bn := c.buf()
|
||||
n := C.ucol_getSortKey(c.col, icuUCharP(a), icuULen(a), bp, bn)
|
||||
if b := c.extendBuf(n); b != nil {
|
||||
return b
|
||||
}
|
||||
return c.Key(s)
|
||||
}
|
||||
|
||||
func encodeUTF16(b []byte) []uint16 {
|
||||
a := []uint16{}
|
||||
for len(b) > 0 {
|
||||
r, sz := utf8.DecodeRune(b)
|
||||
b = b[sz:]
|
||||
r1, r2 := utf16.EncodeRune(r)
|
||||
if r1 != 0xFFFD {
|
||||
a = append(a, uint16(r1), uint16(r2))
|
||||
} else {
|
||||
a = append(a, uint16(r))
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
66
vendor/golang.org/x/text/currency/common.go
generated
vendored
Normal file
66
vendor/golang.org/x/text/currency/common.go
generated
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// This file was generated by go generate; DO NOT EDIT
|
||||
|
||||
package currency
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// This file contains code common to gen.go and the package code.
|
||||
|
||||
const (
|
||||
cashShift = 3
|
||||
roundMask = 0x7
|
||||
|
||||
nonTenderBit = 0x8000
|
||||
)
|
||||
|
||||
// currencyInfo contains information about a currency.
|
||||
// bits 0..2: index into roundings for standard rounding
|
||||
// bits 3..5: index into roundings for cash rounding
|
||||
type currencyInfo byte
|
||||
|
||||
// roundingType defines the scale (number of fractional decimals) and increments
|
||||
// in terms of units of size 10^-scale. For example, for scale == 2 and
|
||||
// increment == 1, the currency is rounded to units of 0.01.
|
||||
type roundingType struct {
|
||||
scale, increment uint8
|
||||
}
|
||||
|
||||
// roundings contains rounding data for currencies. This struct is
|
||||
// created by hand as it is very unlikely to change much.
|
||||
var roundings = [...]roundingType{
|
||||
{2, 1}, // default
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{2, 5}, // cash rounding alternative
|
||||
}
|
||||
|
||||
// regionToCode returns a 16-bit region code. Only two-letter codes are
|
||||
// supported. (Three-letter codes are not needed.)
|
||||
func regionToCode(r language.Region) uint16 {
|
||||
if s := r.String(); len(s) == 2 {
|
||||
return uint16(s[0])<<8 | uint16(s[1])
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func toDate(t time.Time) uint32 {
|
||||
y := t.Year()
|
||||
if y == 1 {
|
||||
return 0
|
||||
}
|
||||
date := uint32(y) << 4
|
||||
date |= uint32(t.Month())
|
||||
date <<= 5
|
||||
date |= uint32(t.Day())
|
||||
return date
|
||||
}
|
||||
|
||||
func fromDate(date uint32) time.Time {
|
||||
return time.Date(int(date>>9), time.Month((date>>5)&0xf), int(date&0x1f), 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
185
vendor/golang.org/x/text/currency/currency.go
generated
vendored
Normal file
185
vendor/golang.org/x/text/currency/currency.go
generated
vendored
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
// 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.
|
||||
|
||||
//go:generate go run gen.go gen_common.go -output tables.go
|
||||
|
||||
// Package currency contains currency-related functionality.
|
||||
//
|
||||
// NOTE: the formatting functionality is currently under development and may
|
||||
// change without notice.
|
||||
package currency // import "golang.org/x/text/currency"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/text/internal/tag"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// TODO:
|
||||
// - language-specific currency names.
|
||||
// - currency formatting.
|
||||
// - currency information per region
|
||||
// - register currency code (there are no private use area)
|
||||
|
||||
// TODO: remove Currency type from package language.
|
||||
|
||||
// Kind determines the rounding and rendering properties of a currency value.
|
||||
type Kind struct {
|
||||
rounding rounding
|
||||
// TODO: formatting type: standard, accounting. See CLDR.
|
||||
}
|
||||
|
||||
type rounding byte
|
||||
|
||||
const (
|
||||
standard rounding = iota
|
||||
cash
|
||||
)
|
||||
|
||||
var (
|
||||
// Standard defines standard rounding and formatting for currencies.
|
||||
Standard Kind = Kind{rounding: standard}
|
||||
|
||||
// Cash defines rounding and formatting standards for cash transactions.
|
||||
Cash Kind = Kind{rounding: cash}
|
||||
|
||||
// Accounting defines rounding and formatting standards for accounting.
|
||||
Accounting Kind = Kind{rounding: standard}
|
||||
)
|
||||
|
||||
// Rounding reports the rounding characteristics for the given currency, where
|
||||
// scale is the number of fractional decimals and increment is the number of
|
||||
// units in terms of 10^(-scale) to which to round to.
|
||||
func (k Kind) Rounding(cur Unit) (scale, increment int) {
|
||||
info := currency.Elem(int(cur.index))[3]
|
||||
switch k.rounding {
|
||||
case standard:
|
||||
info &= roundMask
|
||||
case cash:
|
||||
info >>= cashShift
|
||||
}
|
||||
return int(roundings[info].scale), int(roundings[info].increment)
|
||||
}
|
||||
|
||||
// Unit is an ISO 4217 currency designator.
|
||||
type Unit struct {
|
||||
index uint16
|
||||
}
|
||||
|
||||
// String returns the ISO code of u.
|
||||
func (u Unit) String() string {
|
||||
if u.index == 0 {
|
||||
return "XXX"
|
||||
}
|
||||
return currency.Elem(int(u.index))[:3]
|
||||
}
|
||||
|
||||
// Amount creates an Amount for the given currency unit and amount.
|
||||
func (u Unit) Amount(amount interface{}) Amount {
|
||||
// TODO: verify amount is a supported number type
|
||||
return Amount{amount: amount, currency: u}
|
||||
}
|
||||
|
||||
var (
|
||||
errSyntax = errors.New("currency: tag is not well-formed")
|
||||
errValue = errors.New("currency: tag is not a recognized currency")
|
||||
)
|
||||
|
||||
// ParseISO parses a 3-letter ISO 4217 currency code. It returns an error if s
|
||||
// is not well-formed or not a recognized currency code.
|
||||
func ParseISO(s string) (Unit, error) {
|
||||
var buf [4]byte // Take one byte more to detect oversize keys.
|
||||
key := buf[:copy(buf[:], s)]
|
||||
if !tag.FixCase("XXX", key) {
|
||||
return Unit{}, errSyntax
|
||||
}
|
||||
if i := currency.Index(key); i >= 0 {
|
||||
if i == xxx {
|
||||
return Unit{}, nil
|
||||
}
|
||||
return Unit{uint16(i)}, nil
|
||||
}
|
||||
return Unit{}, errValue
|
||||
}
|
||||
|
||||
// MustParseISO is like ParseISO, but panics if the given currency unit
|
||||
// cannot be parsed. It simplifies safe initialization of Unit values.
|
||||
func MustParseISO(s string) Unit {
|
||||
c, err := ParseISO(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// FromRegion reports the currency unit that is currently legal tender in the
|
||||
// given region according to CLDR. It will return false if region currently does
|
||||
// not have a legal tender.
|
||||
func FromRegion(r language.Region) (currency Unit, ok bool) {
|
||||
x := regionToCode(r)
|
||||
i := sort.Search(len(regionToCurrency), func(i int) bool {
|
||||
return regionToCurrency[i].region >= x
|
||||
})
|
||||
if i < len(regionToCurrency) && regionToCurrency[i].region == x {
|
||||
return Unit{regionToCurrency[i].code}, true
|
||||
}
|
||||
return Unit{}, false
|
||||
}
|
||||
|
||||
// FromTag reports the most likely currency for the given tag. It considers the
|
||||
// currency defined in the -u extension and infers the region if necessary.
|
||||
func FromTag(t language.Tag) (Unit, language.Confidence) {
|
||||
if cur := t.TypeForKey("cu"); len(cur) == 3 {
|
||||
c, _ := ParseISO(cur)
|
||||
return c, language.Exact
|
||||
}
|
||||
r, conf := t.Region()
|
||||
if cur, ok := FromRegion(r); ok {
|
||||
return cur, conf
|
||||
}
|
||||
return Unit{}, language.No
|
||||
}
|
||||
|
||||
var (
|
||||
// Undefined and testing.
|
||||
XXX Unit = Unit{}
|
||||
XTS Unit = Unit{xts}
|
||||
|
||||
// G10 currencies https://en.wikipedia.org/wiki/G10_currencies.
|
||||
USD Unit = Unit{usd}
|
||||
EUR Unit = Unit{eur}
|
||||
JPY Unit = Unit{jpy}
|
||||
GBP Unit = Unit{gbp}
|
||||
CHF Unit = Unit{chf}
|
||||
AUD Unit = Unit{aud}
|
||||
NZD Unit = Unit{nzd}
|
||||
CAD Unit = Unit{cad}
|
||||
SEK Unit = Unit{sek}
|
||||
NOK Unit = Unit{nok}
|
||||
|
||||
// Additional common currencies as defined by CLDR.
|
||||
BRL Unit = Unit{brl}
|
||||
CNY Unit = Unit{cny}
|
||||
DKK Unit = Unit{dkk}
|
||||
INR Unit = Unit{inr}
|
||||
RUB Unit = Unit{rub}
|
||||
HKD Unit = Unit{hkd}
|
||||
IDR Unit = Unit{idr}
|
||||
KRW Unit = Unit{krw}
|
||||
MXN Unit = Unit{mxn}
|
||||
PLN Unit = Unit{pln}
|
||||
SAR Unit = Unit{sar}
|
||||
THB Unit = Unit{thb}
|
||||
TRY Unit = Unit{try}
|
||||
TWD Unit = Unit{twd}
|
||||
ZAR Unit = Unit{zar}
|
||||
|
||||
// Precious metals.
|
||||
XAG Unit = Unit{xag}
|
||||
XAU Unit = Unit{xau}
|
||||
XPT Unit = Unit{xpt}
|
||||
XPD Unit = Unit{xpd}
|
||||
)
|
||||
171
vendor/golang.org/x/text/currency/currency_test.go
generated
vendored
Normal file
171
vendor/golang.org/x/text/currency/currency_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
// 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 currency
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/internal/testtext"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
var (
|
||||
cup = MustParseISO("CUP")
|
||||
czk = MustParseISO("CZK")
|
||||
xcd = MustParseISO("XCD")
|
||||
zwr = MustParseISO("ZWR")
|
||||
)
|
||||
|
||||
func TestParseISO(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in string
|
||||
out Unit
|
||||
ok bool
|
||||
}{
|
||||
{"USD", USD, true},
|
||||
{"xxx", XXX, true},
|
||||
{"xts", XTS, true},
|
||||
{"XX", XXX, false},
|
||||
{"XXXX", XXX, false},
|
||||
{"", XXX, false}, // not well-formed
|
||||
{"UUU", XXX, false}, // unknown
|
||||
{"\u22A9", XXX, false}, // non-ASCII, printable
|
||||
|
||||
{"aaa", XXX, false},
|
||||
{"zzz", XXX, false},
|
||||
{"000", XXX, false},
|
||||
{"999", XXX, false},
|
||||
{"---", XXX, false},
|
||||
{"\x00\x00\x00", XXX, false},
|
||||
{"\xff\xff\xff", XXX, false},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
if x, err := ParseISO(tc.in); x != tc.out || err == nil != tc.ok {
|
||||
t.Errorf("%d:%s: was %s, %v; want %s, %v", i, tc.in, x, err == nil, tc.out, tc.ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromRegion(t *testing.T) {
|
||||
testCases := []struct {
|
||||
region string
|
||||
currency Unit
|
||||
ok bool
|
||||
}{
|
||||
{"NL", EUR, true},
|
||||
{"BE", EUR, true},
|
||||
{"AG", xcd, true},
|
||||
{"CH", CHF, true},
|
||||
{"CU", cup, true}, // first of multiple
|
||||
{"DG", USD, true}, // does not have M49 code
|
||||
{"150", XXX, false}, // implicit false
|
||||
{"CP", XXX, false}, // explicit false in CLDR
|
||||
{"CS", XXX, false}, // all expired
|
||||
{"ZZ", XXX, false}, // none match
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
cur, ok := FromRegion(language.MustParseRegion(tc.region))
|
||||
if cur != tc.currency || ok != tc.ok {
|
||||
t.Errorf("%s: got %v, %v; want %v, %v", tc.region, cur, ok, tc.currency, tc.ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromTag(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tag string
|
||||
currency Unit
|
||||
conf language.Confidence
|
||||
}{
|
||||
{"nl", EUR, language.Low}, // nl also spoken outside Euro land.
|
||||
{"nl-BE", EUR, language.Exact}, // region is known
|
||||
{"pt", BRL, language.Low},
|
||||
{"en", USD, language.Low},
|
||||
{"en-u-cu-eur", EUR, language.Exact},
|
||||
{"tlh", XXX, language.No}, // Klingon has no country.
|
||||
{"es-419", XXX, language.No},
|
||||
{"und", USD, language.Low},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
cur, conf := FromTag(language.MustParse(tc.tag))
|
||||
if cur != tc.currency || conf != tc.conf {
|
||||
t.Errorf("%s: got %v, %v; want %v, %v", tc.tag, cur, conf, tc.currency, tc.conf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTable(t *testing.T) {
|
||||
for i := 4; i < len(currency); i += 4 {
|
||||
if a, b := currency[i-4:i-1], currency[i:i+3]; a >= b {
|
||||
t.Errorf("currency unordered at element %d: %s >= %s", i, a, b)
|
||||
}
|
||||
}
|
||||
// First currency has index 1, last is numCurrencies.
|
||||
if c := currency.Elem(1)[:3]; c != "ADP" {
|
||||
t.Errorf("first was %c; want ADP", c)
|
||||
}
|
||||
if c := currency.Elem(numCurrencies)[:3]; c != "ZWR" {
|
||||
t.Errorf("last was %c; want ZWR", c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKindRounding(t *testing.T) {
|
||||
testCases := []struct {
|
||||
kind Kind
|
||||
cur Unit
|
||||
scale int
|
||||
inc int
|
||||
}{
|
||||
{Standard, USD, 2, 1},
|
||||
{Standard, CHF, 2, 1},
|
||||
{Cash, CHF, 2, 5},
|
||||
{Standard, TWD, 2, 1},
|
||||
{Cash, TWD, 0, 1},
|
||||
{Standard, czk, 2, 1},
|
||||
{Cash, czk, 0, 1},
|
||||
{Standard, zwr, 2, 1},
|
||||
{Cash, zwr, 0, 1},
|
||||
{Standard, KRW, 0, 1},
|
||||
{Cash, KRW, 0, 1}, // Cash defaults to standard.
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
if scale, inc := tc.kind.Rounding(tc.cur); scale != tc.scale && inc != tc.inc {
|
||||
t.Errorf("%d: got %d, %d; want %d, %d", i, scale, inc, tc.scale, tc.inc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const body = `package main
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/text/currency"
|
||||
)
|
||||
func main() {
|
||||
%s
|
||||
}
|
||||
`
|
||||
|
||||
func TestLinking(t *testing.T) {
|
||||
base := getSize(t, `fmt.Print(currency.CLDRVersion)`)
|
||||
symbols := getSize(t, `fmt.Print(currency.Symbol(currency.USD))`)
|
||||
if d := symbols - base; d < 2*1024 {
|
||||
t.Errorf("size(symbols)-size(base) was %d; want > 2K", d)
|
||||
}
|
||||
}
|
||||
|
||||
func getSize(t *testing.T, main string) int {
|
||||
size, err := testtext.CodeSize(fmt.Sprintf(body, main))
|
||||
if err != nil {
|
||||
t.Skipf("skipping link size test; binary size could not be determined: %v", err)
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func BenchmarkString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
USD.String()
|
||||
}
|
||||
}
|
||||
27
vendor/golang.org/x/text/currency/example_test.go
generated
vendored
Normal file
27
vendor/golang.org/x/text/currency/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 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 currency_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/currency"
|
||||
)
|
||||
|
||||
func ExampleQuery() {
|
||||
t1799, _ := time.Parse("2006-01-02", "1799-01-01")
|
||||
for it := currency.Query(currency.Date(t1799)); it.Next(); {
|
||||
from := ""
|
||||
if t, ok := it.From(); ok {
|
||||
from = t.Format("2006-01-01")
|
||||
}
|
||||
fmt.Printf("%v is used in %v since: %v\n", it.Unit(), it.Region(), from)
|
||||
}
|
||||
// Output:
|
||||
// GBP is used in GB since: 1694-07-07
|
||||
// GIP is used in GI since: 1713-01-01
|
||||
// USD is used in US since: 1792-01-01
|
||||
}
|
||||
215
vendor/golang.org/x/text/currency/format.go
generated
vendored
Normal file
215
vendor/golang.org/x/text/currency/format.go
generated
vendored
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
// 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 currency
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/text/internal"
|
||||
"golang.org/x/text/internal/format"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Amount is an amount-currency unit pair.
|
||||
type Amount struct {
|
||||
amount interface{} // Change to decimal(64|128).
|
||||
currency Unit
|
||||
}
|
||||
|
||||
// Currency reports the currency unit of this amount.
|
||||
func (a Amount) Currency() Unit { return a.currency }
|
||||
|
||||
// TODO: based on decimal type, but may make sense to customize a bit.
|
||||
// func (a Amount) Decimal()
|
||||
// func (a Amount) Int() (int64, error)
|
||||
// func (a Amount) Fraction() (int64, error)
|
||||
// func (a Amount) Rat() *big.Rat
|
||||
// func (a Amount) Float() (float64, error)
|
||||
// func (a Amount) Scale() uint
|
||||
// func (a Amount) Precision() uint
|
||||
// func (a Amount) Sign() int
|
||||
//
|
||||
// Add/Sub/Div/Mul/Round.
|
||||
|
||||
var space = []byte(" ")
|
||||
|
||||
// Format implements fmt.Formatter. It accepts format.State for
|
||||
// language-specific rendering.
|
||||
func (a Amount) Format(s fmt.State, verb rune) {
|
||||
v := formattedValue{
|
||||
currency: a.currency,
|
||||
amount: a.amount,
|
||||
format: defaultFormat,
|
||||
}
|
||||
v.Format(s, verb)
|
||||
}
|
||||
|
||||
// formattedValue is currency amount or unit that implements language-sensitive
|
||||
// formatting.
|
||||
type formattedValue struct {
|
||||
currency Unit
|
||||
amount interface{} // Amount, Unit, or number.
|
||||
format *options
|
||||
}
|
||||
|
||||
// Format implements fmt.Formatter. It accepts format.State for
|
||||
// language-specific rendering.
|
||||
func (v formattedValue) Format(s fmt.State, verb rune) {
|
||||
var lang int
|
||||
if state, ok := s.(format.State); ok {
|
||||
lang, _ = language.CompactIndex(state.Language())
|
||||
}
|
||||
|
||||
// Get the options. Use DefaultFormat if not present.
|
||||
opt := v.format
|
||||
if opt == nil {
|
||||
opt = defaultFormat
|
||||
}
|
||||
cur := v.currency
|
||||
if cur.index == 0 {
|
||||
cur = opt.currency
|
||||
}
|
||||
|
||||
// TODO: use pattern.
|
||||
io.WriteString(s, opt.symbol(lang, cur))
|
||||
if v.amount != nil {
|
||||
s.Write(space)
|
||||
|
||||
// TODO: apply currency-specific rounding
|
||||
scale, _ := opt.kind.Rounding(cur)
|
||||
if _, ok := s.Precision(); !ok {
|
||||
fmt.Fprintf(s, "%.*f", scale, v.amount)
|
||||
} else {
|
||||
fmt.Fprint(s, v.amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Formatter decorates a given number, Unit or Amount with formatting options.
|
||||
type Formatter func(amount interface{}) formattedValue
|
||||
|
||||
// func (f Formatter) Options(opts ...Option) Formatter
|
||||
|
||||
// TODO: call this a Formatter or FormatFunc?
|
||||
|
||||
var dummy = USD.Amount(0)
|
||||
|
||||
// adjust creates a new Formatter based on the adjustments of fn on f.
|
||||
func (f Formatter) adjust(fn func(*options)) Formatter {
|
||||
var o options = *(f(dummy).format)
|
||||
fn(&o)
|
||||
return o.format
|
||||
}
|
||||
|
||||
// Default creates a new Formatter that defaults to currency unit c if a numeric
|
||||
// value is passed that is not associated with a currency.
|
||||
func (f Formatter) Default(currency Unit) Formatter {
|
||||
return f.adjust(func(o *options) { o.currency = currency })
|
||||
}
|
||||
|
||||
// Kind sets the kind of the underlying currency unit.
|
||||
func (f Formatter) Kind(k Kind) Formatter {
|
||||
return f.adjust(func(o *options) { o.kind = k })
|
||||
}
|
||||
|
||||
var defaultFormat *options = ISO(dummy).format
|
||||
|
||||
var (
|
||||
// Uses Narrow symbols. Overrides Symbol, if present.
|
||||
NarrowSymbol Formatter = Formatter(formNarrow)
|
||||
|
||||
// Use Symbols instead of ISO codes, when available.
|
||||
Symbol Formatter = Formatter(formSymbol)
|
||||
|
||||
// Use ISO code as symbol.
|
||||
ISO Formatter = Formatter(formISO)
|
||||
|
||||
// TODO:
|
||||
// // Use full name as symbol.
|
||||
// Name Formatter
|
||||
)
|
||||
|
||||
// options configures rendering and rounding options for an Amount.
|
||||
type options struct {
|
||||
currency Unit
|
||||
kind Kind
|
||||
|
||||
symbol func(compactIndex int, c Unit) string
|
||||
}
|
||||
|
||||
func (o *options) format(amount interface{}) formattedValue {
|
||||
v := formattedValue{format: o}
|
||||
switch x := amount.(type) {
|
||||
case Amount:
|
||||
v.amount = x.amount
|
||||
v.currency = x.currency
|
||||
case *Amount:
|
||||
v.amount = x.amount
|
||||
v.currency = x.currency
|
||||
case Unit:
|
||||
v.currency = x
|
||||
case *Unit:
|
||||
v.currency = *x
|
||||
default:
|
||||
if o.currency.index == 0 {
|
||||
panic("cannot format number without a currency being set")
|
||||
}
|
||||
// TODO: Must be a number.
|
||||
v.amount = x
|
||||
v.currency = o.currency
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
var (
|
||||
optISO = options{symbol: lookupISO}
|
||||
optSymbol = options{symbol: lookupSymbol}
|
||||
optNarrow = options{symbol: lookupNarrow}
|
||||
)
|
||||
|
||||
// These need to be functions, rather than curried methods, as curried methods
|
||||
// are evaluated at init time, causing tables to be included unconditionally.
|
||||
func formISO(x interface{}) formattedValue { return optISO.format(x) }
|
||||
func formSymbol(x interface{}) formattedValue { return optSymbol.format(x) }
|
||||
func formNarrow(x interface{}) formattedValue { return optNarrow.format(x) }
|
||||
|
||||
func lookupISO(x int, c Unit) string { return c.String() }
|
||||
func lookupSymbol(x int, c Unit) string { return normalSymbol.lookup(x, c) }
|
||||
func lookupNarrow(x int, c Unit) string { return narrowSymbol.lookup(x, c) }
|
||||
|
||||
type symbolIndex struct {
|
||||
index []uint16 // position corresponds with compact index of language.
|
||||
data []curToIndex
|
||||
}
|
||||
|
||||
var (
|
||||
normalSymbol = symbolIndex{normalLangIndex, normalSymIndex}
|
||||
narrowSymbol = symbolIndex{narrowLangIndex, narrowSymIndex}
|
||||
)
|
||||
|
||||
func (x *symbolIndex) lookup(lang int, c Unit) string {
|
||||
for {
|
||||
index := x.data[x.index[lang]:x.index[lang+1]]
|
||||
i := sort.Search(len(index), func(i int) bool {
|
||||
return index[i].cur >= c.index
|
||||
})
|
||||
if i < len(index) && index[i].cur == c.index {
|
||||
x := index[i].idx
|
||||
start := x + 1
|
||||
end := start + uint16(symbols[x])
|
||||
if start == end {
|
||||
return c.String()
|
||||
}
|
||||
return symbols[start:end]
|
||||
}
|
||||
if lang == 0 {
|
||||
break
|
||||
}
|
||||
lang = int(internal.Parent[lang])
|
||||
}
|
||||
return c.String()
|
||||
}
|
||||
70
vendor/golang.org/x/text/currency/format_test.go
generated
vendored
Normal file
70
vendor/golang.org/x/text/currency/format_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// 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 currency
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
var (
|
||||
en = language.English
|
||||
fr = language.French
|
||||
en_US = language.AmericanEnglish
|
||||
en_GB = language.BritishEnglish
|
||||
en_AU = language.MustParse("en-AU")
|
||||
und = language.Und
|
||||
)
|
||||
|
||||
func TestFormatting(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tag language.Tag
|
||||
value interface{}
|
||||
format Formatter
|
||||
want string
|
||||
}{
|
||||
0: {en, USD.Amount(0.1), nil, "USD 0.10"},
|
||||
1: {en, XPT.Amount(1.0), Symbol, "XPT 1.00"},
|
||||
|
||||
2: {en, USD.Amount(2.0), ISO, "USD 2.00"},
|
||||
3: {und, USD.Amount(3.0), Symbol, "US$ 3.00"},
|
||||
4: {en, USD.Amount(4.0), Symbol, "$ 4.00"},
|
||||
|
||||
5: {en, USD.Amount(5.20), NarrowSymbol, "$ 5.20"},
|
||||
6: {en, AUD.Amount(6.20), Symbol, "A$ 6.20"},
|
||||
|
||||
7: {en_AU, AUD.Amount(7.20), Symbol, "$ 7.20"},
|
||||
8: {en_GB, USD.Amount(8.20), Symbol, "US$ 8.20"},
|
||||
|
||||
9: {en, 9.0, Symbol.Default(EUR), "€ 9.00"},
|
||||
10: {en, 10.123, Symbol.Default(KRW), "₩ 10"},
|
||||
11: {fr, 11.52, Symbol.Default(TWD), "TWD 11.52"},
|
||||
12: {en, 12.123, Symbol.Default(czk), "CZK 12.12"},
|
||||
13: {en, 13.123, Symbol.Default(czk).Kind(Cash), "CZK 13"},
|
||||
14: {en, 14.12345, ISO.Default(MustParseISO("CLF")), "CLF 14.1235"},
|
||||
15: {en, USD.Amount(15.00), ISO.Default(TWD), "USD 15.00"},
|
||||
16: {en, KRW.Amount(16.00), ISO.Kind(Cash), "KRW 16"},
|
||||
|
||||
// TODO: support integers as well.
|
||||
|
||||
17: {en, USD, nil, "USD"},
|
||||
18: {en, USD, ISO, "USD"},
|
||||
19: {en, USD, Symbol, "$"},
|
||||
20: {en_GB, USD, Symbol, "US$"},
|
||||
21: {en_AU, USD, NarrowSymbol, "$"},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
p := message.NewPrinter(tc.tag)
|
||||
v := tc.value
|
||||
if tc.format != nil {
|
||||
v = tc.format(v)
|
||||
}
|
||||
if got := p.Sprint(v); got != tc.want {
|
||||
t.Errorf("%d: got %q; want %q", i, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
400
vendor/golang.org/x/text/currency/gen.go
generated
vendored
Normal file
400
vendor/golang.org/x/text/currency/gen.go
generated
vendored
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
// 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// Generator for currency-related data.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/internal"
|
||||
"golang.org/x/text/internal/gen"
|
||||
"golang.org/x/text/internal/tag"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/unicode/cldr"
|
||||
)
|
||||
|
||||
var (
|
||||
test = flag.Bool("test", false,
|
||||
"test existing tables; can be used to compare web data with package data.")
|
||||
outputFile = flag.String("output", "tables.go", "output file")
|
||||
|
||||
draft = flag.String("draft",
|
||||
"contributed",
|
||||
`Minimal draft requirements (approved, contributed, provisional, unconfirmed).`)
|
||||
)
|
||||
|
||||
func main() {
|
||||
gen.Init()
|
||||
|
||||
gen.Repackage("gen_common.go", "common.go", "currency")
|
||||
|
||||
// Read the CLDR zip file.
|
||||
r := gen.OpenCLDRCoreZip()
|
||||
defer r.Close()
|
||||
|
||||
d := &cldr.Decoder{}
|
||||
d.SetDirFilter("supplemental", "main")
|
||||
d.SetSectionFilter("numbers")
|
||||
data, err := d.DecodeZip(r)
|
||||
if err != nil {
|
||||
log.Fatalf("DecodeZip: %v", err)
|
||||
}
|
||||
|
||||
w := gen.NewCodeWriter()
|
||||
defer w.WriteGoFile(*outputFile, "currency")
|
||||
|
||||
fmt.Fprintln(w, `import "golang.org/x/text/internal/tag"`)
|
||||
|
||||
gen.WriteCLDRVersion(w)
|
||||
b := &builder{}
|
||||
b.genCurrencies(w, data.Supplemental())
|
||||
b.genSymbols(w, data)
|
||||
}
|
||||
|
||||
var constants = []string{
|
||||
// Undefined and testing.
|
||||
"XXX", "XTS",
|
||||
// G11 currencies https://en.wikipedia.org/wiki/G10_currencies.
|
||||
"USD", "EUR", "JPY", "GBP", "CHF", "AUD", "NZD", "CAD", "SEK", "NOK", "DKK",
|
||||
// Precious metals.
|
||||
"XAG", "XAU", "XPT", "XPD",
|
||||
|
||||
// Additional common currencies as defined by CLDR.
|
||||
"BRL", "CNY", "INR", "RUB", "HKD", "IDR", "KRW", "MXN", "PLN", "SAR",
|
||||
"THB", "TRY", "TWD", "ZAR",
|
||||
}
|
||||
|
||||
type builder struct {
|
||||
currencies tag.Index
|
||||
numCurrencies int
|
||||
}
|
||||
|
||||
func (b *builder) genCurrencies(w *gen.CodeWriter, data *cldr.SupplementalData) {
|
||||
// 3-letter ISO currency codes
|
||||
// Start with dummy to let index start at 1.
|
||||
currencies := []string{"\x00\x00\x00\x00"}
|
||||
|
||||
// currency codes
|
||||
for _, reg := range data.CurrencyData.Region {
|
||||
for _, cur := range reg.Currency {
|
||||
currencies = append(currencies, cur.Iso4217)
|
||||
}
|
||||
}
|
||||
// Not included in the list for some reasons:
|
||||
currencies = append(currencies, "MVP")
|
||||
|
||||
sort.Strings(currencies)
|
||||
// Unique the elements.
|
||||
k := 0
|
||||
for i := 1; i < len(currencies); i++ {
|
||||
if currencies[k] != currencies[i] {
|
||||
currencies[k+1] = currencies[i]
|
||||
k++
|
||||
}
|
||||
}
|
||||
currencies = currencies[:k+1]
|
||||
|
||||
// Close with dummy for simpler and faster searching.
|
||||
currencies = append(currencies, "\xff\xff\xff\xff")
|
||||
|
||||
// Write currency values.
|
||||
fmt.Fprintln(w, "const (")
|
||||
for _, c := range constants {
|
||||
index := sort.SearchStrings(currencies, c)
|
||||
fmt.Fprintf(w, "\t%s = %d\n", strings.ToLower(c), index)
|
||||
}
|
||||
fmt.Fprint(w, ")")
|
||||
|
||||
// Compute currency-related data that we merge into the table.
|
||||
for _, info := range data.CurrencyData.Fractions[0].Info {
|
||||
if info.Iso4217 == "DEFAULT" {
|
||||
continue
|
||||
}
|
||||
standard := getRoundingIndex(info.Digits, info.Rounding, 0)
|
||||
cash := getRoundingIndex(info.CashDigits, info.CashRounding, standard)
|
||||
|
||||
index := sort.SearchStrings(currencies, info.Iso4217)
|
||||
currencies[index] += mkCurrencyInfo(standard, cash)
|
||||
}
|
||||
|
||||
// Set default values for entries that weren't touched.
|
||||
for i, c := range currencies {
|
||||
if len(c) == 3 {
|
||||
currencies[i] += mkCurrencyInfo(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
b.currencies = tag.Index(strings.Join(currencies, ""))
|
||||
w.WriteComment(`
|
||||
currency holds an alphabetically sorted list of canonical 3-letter currency
|
||||
identifiers. Each identifier is followed by a byte of type currencyInfo,
|
||||
defined in gen_common.go.`)
|
||||
w.WriteConst("currency", b.currencies)
|
||||
|
||||
// Hack alert: gofmt indents a trailing comment after an indented string.
|
||||
// Ensure that the next thing written is not a comment.
|
||||
b.numCurrencies = (len(b.currencies) / 4) - 2
|
||||
w.WriteConst("numCurrencies", b.numCurrencies)
|
||||
|
||||
// Create a table that maps regions to currencies.
|
||||
regionToCurrency := []toCurrency{}
|
||||
|
||||
for _, reg := range data.CurrencyData.Region {
|
||||
if len(reg.Iso3166) != 2 {
|
||||
log.Fatalf("Unexpected group %q in region data", reg.Iso3166)
|
||||
}
|
||||
if len(reg.Currency) == 0 {
|
||||
continue
|
||||
}
|
||||
cur := reg.Currency[0]
|
||||
if cur.To != "" || cur.Tender == "false" {
|
||||
continue
|
||||
}
|
||||
regionToCurrency = append(regionToCurrency, toCurrency{
|
||||
region: regionToCode(language.MustParseRegion(reg.Iso3166)),
|
||||
code: uint16(b.currencies.Index([]byte(cur.Iso4217))),
|
||||
})
|
||||
}
|
||||
sort.Sort(byRegion(regionToCurrency))
|
||||
|
||||
w.WriteType(toCurrency{})
|
||||
w.WriteVar("regionToCurrency", regionToCurrency)
|
||||
|
||||
// Create a table that maps regions to currencies.
|
||||
regionData := []regionInfo{}
|
||||
|
||||
for _, reg := range data.CurrencyData.Region {
|
||||
if len(reg.Iso3166) != 2 {
|
||||
log.Fatalf("Unexpected group %q in region data", reg.Iso3166)
|
||||
}
|
||||
for _, cur := range reg.Currency {
|
||||
from, _ := time.Parse("2006-01-02", cur.From)
|
||||
to, _ := time.Parse("2006-01-02", cur.To)
|
||||
code := uint16(b.currencies.Index([]byte(cur.Iso4217)))
|
||||
if cur.Tender == "false" {
|
||||
code |= nonTenderBit
|
||||
}
|
||||
regionData = append(regionData, regionInfo{
|
||||
region: regionToCode(language.MustParseRegion(reg.Iso3166)),
|
||||
code: code,
|
||||
from: toDate(from),
|
||||
to: toDate(to),
|
||||
})
|
||||
}
|
||||
}
|
||||
sort.Stable(byRegionCode(regionData))
|
||||
|
||||
w.WriteType(regionInfo{})
|
||||
w.WriteVar("regionData", regionData)
|
||||
}
|
||||
|
||||
type regionInfo struct {
|
||||
region uint16
|
||||
code uint16 // 0x8000 not legal tender
|
||||
from uint32
|
||||
to uint32
|
||||
}
|
||||
|
||||
type byRegionCode []regionInfo
|
||||
|
||||
func (a byRegionCode) Len() int { return len(a) }
|
||||
func (a byRegionCode) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a byRegionCode) Less(i, j int) bool { return a[i].region < a[j].region }
|
||||
|
||||
type toCurrency struct {
|
||||
region uint16
|
||||
code uint16
|
||||
}
|
||||
|
||||
type byRegion []toCurrency
|
||||
|
||||
func (a byRegion) Len() int { return len(a) }
|
||||
func (a byRegion) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a byRegion) Less(i, j int) bool { return a[i].region < a[j].region }
|
||||
|
||||
func mkCurrencyInfo(standard, cash int) string {
|
||||
return string([]byte{byte(cash<<cashShift | standard)})
|
||||
}
|
||||
|
||||
func getRoundingIndex(digits, rounding string, defIndex int) int {
|
||||
round := roundings[defIndex] // default
|
||||
|
||||
if digits != "" {
|
||||
round.scale = parseUint8(digits)
|
||||
}
|
||||
if rounding != "" && rounding != "0" { // 0 means 1 here in CLDR
|
||||
round.increment = parseUint8(rounding)
|
||||
}
|
||||
|
||||
// Will panic if the entry doesn't exist:
|
||||
for i, r := range roundings {
|
||||
if r == round {
|
||||
return i
|
||||
}
|
||||
}
|
||||
log.Fatalf("Rounding entry %#v does not exist.", round)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// genSymbols generates the symbols used for currencies. Most symbols are
|
||||
// defined in root and there is only very small variation per language.
|
||||
// The following rules apply:
|
||||
// - A symbol can be requested as normal or narrow.
|
||||
// - If a symbol is not defined for a currency, it defaults to its ISO code.
|
||||
func (b *builder) genSymbols(w *gen.CodeWriter, data *cldr.CLDR) {
|
||||
d, err := cldr.ParseDraft(*draft)
|
||||
if err != nil {
|
||||
log.Fatalf("filter: %v", err)
|
||||
}
|
||||
|
||||
const (
|
||||
normal = iota
|
||||
narrow
|
||||
numTypes
|
||||
)
|
||||
// language -> currency -> type -> symbol
|
||||
var symbols [language.NumCompactTags][][numTypes]*string
|
||||
|
||||
// Collect symbol information per language.
|
||||
for _, lang := range data.Locales() {
|
||||
ldml := data.RawLDML(lang)
|
||||
if ldml.Numbers == nil || ldml.Numbers.Currencies == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
langIndex, ok := language.CompactIndex(language.MustParse(lang))
|
||||
if !ok {
|
||||
log.Fatalf("No compact index for language %s", lang)
|
||||
}
|
||||
|
||||
symbols[langIndex] = make([][numTypes]*string, b.numCurrencies+1)
|
||||
|
||||
for _, c := range ldml.Numbers.Currencies.Currency {
|
||||
syms := cldr.MakeSlice(&c.Symbol)
|
||||
syms.SelectDraft(d)
|
||||
|
||||
for _, sym := range c.Symbol {
|
||||
v := sym.Data()
|
||||
if v == c.Type {
|
||||
// We define "" to mean the ISO symbol.
|
||||
v = ""
|
||||
}
|
||||
cur := b.currencies.Index([]byte(c.Type))
|
||||
// XXX gets reassigned to 0 in the package's code.
|
||||
if c.Type == "XXX" {
|
||||
cur = 0
|
||||
}
|
||||
if cur == -1 {
|
||||
fmt.Println("Unsupported:", c.Type)
|
||||
continue
|
||||
}
|
||||
|
||||
switch sym.Alt {
|
||||
case "":
|
||||
symbols[langIndex][cur][normal] = &v
|
||||
case "narrow":
|
||||
symbols[langIndex][cur][narrow] = &v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove values identical to the parent.
|
||||
for langIndex, data := range symbols {
|
||||
for curIndex, curs := range data {
|
||||
for typ, sym := range curs {
|
||||
if sym == nil {
|
||||
continue
|
||||
}
|
||||
for p := uint16(langIndex); p != 0; {
|
||||
p = internal.Parent[p]
|
||||
x := symbols[p]
|
||||
if x == nil {
|
||||
continue
|
||||
}
|
||||
if v := x[curIndex][typ]; v != nil || p == 0 {
|
||||
// Value is equal to the default value root value is undefined.
|
||||
parentSym := ""
|
||||
if v != nil {
|
||||
parentSym = *v
|
||||
}
|
||||
if parentSym == *sym {
|
||||
// Value is the same as parent.
|
||||
data[curIndex][typ] = nil
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create symbol index.
|
||||
symbolData := []byte{0}
|
||||
symbolLookup := map[string]uint16{"": 0} // 0 means default, so block that value.
|
||||
for _, data := range symbols {
|
||||
for _, curs := range data {
|
||||
for _, sym := range curs {
|
||||
if sym == nil {
|
||||
continue
|
||||
}
|
||||
if _, ok := symbolLookup[*sym]; !ok {
|
||||
symbolLookup[*sym] = uint16(len(symbolData))
|
||||
symbolData = append(symbolData, byte(len(*sym)))
|
||||
symbolData = append(symbolData, *sym...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
w.WriteComment(`
|
||||
symbols holds symbol data of the form <n> <str>, where n is the length of
|
||||
the symbol string str.`)
|
||||
w.WriteConst("symbols", string(symbolData))
|
||||
|
||||
// Create index from language to currency lookup to symbol.
|
||||
type curToIndex struct{ cur, idx uint16 }
|
||||
w.WriteType(curToIndex{})
|
||||
|
||||
prefix := []string{"normal", "narrow"}
|
||||
// Create data for regular and narrow symbol data.
|
||||
for typ := normal; typ <= narrow; typ++ {
|
||||
|
||||
indexes := []curToIndex{} // maps currency to symbol index
|
||||
languages := []uint16{}
|
||||
|
||||
for _, data := range symbols {
|
||||
languages = append(languages, uint16(len(indexes)))
|
||||
for curIndex, curs := range data {
|
||||
|
||||
if sym := curs[typ]; sym != nil {
|
||||
indexes = append(indexes, curToIndex{uint16(curIndex), symbolLookup[*sym]})
|
||||
}
|
||||
}
|
||||
}
|
||||
languages = append(languages, uint16(len(indexes)))
|
||||
|
||||
w.WriteVar(prefix[typ]+"LangIndex", languages)
|
||||
w.WriteVar(prefix[typ]+"SymIndex", indexes)
|
||||
}
|
||||
}
|
||||
func parseUint8(str string) uint8 {
|
||||
x, err := strconv.ParseUint(str, 10, 8)
|
||||
if err != nil {
|
||||
// Show line number of where this function was called.
|
||||
log.New(os.Stderr, "", log.Lshortfile).Output(2, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return uint8(x)
|
||||
}
|
||||
70
vendor/golang.org/x/text/currency/gen_common.go
generated
vendored
Normal file
70
vendor/golang.org/x/text/currency/gen_common.go
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// This file contains code common to gen.go and the package code.
|
||||
|
||||
const (
|
||||
cashShift = 3
|
||||
roundMask = 0x7
|
||||
|
||||
nonTenderBit = 0x8000
|
||||
)
|
||||
|
||||
// currencyInfo contains information about a currency.
|
||||
// bits 0..2: index into roundings for standard rounding
|
||||
// bits 3..5: index into roundings for cash rounding
|
||||
type currencyInfo byte
|
||||
|
||||
// roundingType defines the scale (number of fractional decimals) and increments
|
||||
// in terms of units of size 10^-scale. For example, for scale == 2 and
|
||||
// increment == 1, the currency is rounded to units of 0.01.
|
||||
type roundingType struct {
|
||||
scale, increment uint8
|
||||
}
|
||||
|
||||
// roundings contains rounding data for currencies. This struct is
|
||||
// created by hand as it is very unlikely to change much.
|
||||
var roundings = [...]roundingType{
|
||||
{2, 1}, // default
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{2, 5}, // cash rounding alternative
|
||||
}
|
||||
|
||||
// regionToCode returns a 16-bit region code. Only two-letter codes are
|
||||
// supported. (Three-letter codes are not needed.)
|
||||
func regionToCode(r language.Region) uint16 {
|
||||
if s := r.String(); len(s) == 2 {
|
||||
return uint16(s[0])<<8 | uint16(s[1])
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func toDate(t time.Time) uint32 {
|
||||
y := t.Year()
|
||||
if y == 1 {
|
||||
return 0
|
||||
}
|
||||
date := uint32(y) << 4
|
||||
date |= uint32(t.Month())
|
||||
date <<= 5
|
||||
date |= uint32(t.Day())
|
||||
return date
|
||||
}
|
||||
|
||||
func fromDate(date uint32) time.Time {
|
||||
return time.Date(int(date>>9), time.Month((date>>5)&0xf), int(date&0x1f), 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
152
vendor/golang.org/x/text/currency/query.go
generated
vendored
Normal file
152
vendor/golang.org/x/text/currency/query.go
generated
vendored
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2016 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 currency
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// QueryIter represents a set of Units. The default set includes all Units that
|
||||
// are currently in use as legal tender in any Region.
|
||||
type QueryIter interface {
|
||||
// Next returns true if there is a next element available.
|
||||
// It must be called before any of the other methods are called.
|
||||
Next() bool
|
||||
|
||||
// Unit returns the unit of the current iteration.
|
||||
Unit() Unit
|
||||
|
||||
// Region returns the Region for the current iteration.
|
||||
Region() language.Region
|
||||
|
||||
// From returns the date from which the unit was used in the region.
|
||||
// It returns false if this date is unknown.
|
||||
From() (time.Time, bool)
|
||||
|
||||
// To returns the date up till which the unit was used in the region.
|
||||
// It returns false if this date is unknown or if the unit is still in use.
|
||||
To() (time.Time, bool)
|
||||
|
||||
// IsTender reports whether the unit is a legal tender in the region during
|
||||
// the specified date range.
|
||||
IsTender() bool
|
||||
}
|
||||
|
||||
// Query represents a set of Units. The default set includes all Units that are
|
||||
// currently in use as legal tender in any Region.
|
||||
func Query(options ...QueryOption) QueryIter {
|
||||
it := &iter{
|
||||
end: len(regionData),
|
||||
date: 0xFFFFFFFF,
|
||||
}
|
||||
for _, fn := range options {
|
||||
fn(it)
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
// NonTender returns a new query that also includes matching Units that are not
|
||||
// legal tender.
|
||||
var NonTender QueryOption = nonTender
|
||||
|
||||
func nonTender(i *iter) {
|
||||
i.nonTender = true
|
||||
}
|
||||
|
||||
// Historical selects the units for all dates.
|
||||
var Historical QueryOption = historical
|
||||
|
||||
func historical(i *iter) {
|
||||
i.date = hist
|
||||
}
|
||||
|
||||
// A QueryOption can be used to change the set of unit information returned by
|
||||
// a query.
|
||||
type QueryOption func(*iter)
|
||||
|
||||
// Date queries the units that were in use at the given point in history.
|
||||
func Date(t time.Time) QueryOption {
|
||||
d := toDate(t)
|
||||
return func(i *iter) {
|
||||
i.date = d
|
||||
}
|
||||
}
|
||||
|
||||
// Region limits the query to only return entries for the given region.
|
||||
func Region(r language.Region) QueryOption {
|
||||
p, end := len(regionData), len(regionData)
|
||||
x := regionToCode(r)
|
||||
i := sort.Search(len(regionData), func(i int) bool {
|
||||
return regionData[i].region >= x
|
||||
})
|
||||
if i < len(regionData) && regionData[i].region == x {
|
||||
p = i
|
||||
for i++; i < len(regionData) && regionData[i].region == x; i++ {
|
||||
}
|
||||
end = i
|
||||
}
|
||||
return func(i *iter) {
|
||||
i.p, i.end = p, end
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
hist = 0x00
|
||||
now = 0xFFFFFFFF
|
||||
)
|
||||
|
||||
type iter struct {
|
||||
*regionInfo
|
||||
p, end int
|
||||
date uint32
|
||||
nonTender bool
|
||||
}
|
||||
|
||||
func (i *iter) Next() bool {
|
||||
for ; i.p < i.end; i.p++ {
|
||||
i.regionInfo = ®ionData[i.p]
|
||||
if !i.nonTender && !i.IsTender() {
|
||||
continue
|
||||
}
|
||||
if i.date == hist || (i.from <= i.date && (i.to == 0 || i.date <= i.to)) {
|
||||
i.p++
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *regionInfo) Region() language.Region {
|
||||
// TODO: this could be much faster.
|
||||
var buf [2]byte
|
||||
buf[0] = uint8(r.region >> 8)
|
||||
buf[1] = uint8(r.region)
|
||||
return language.MustParseRegion(string(buf[:]))
|
||||
}
|
||||
|
||||
func (r *regionInfo) Unit() Unit {
|
||||
return Unit{r.code &^ nonTenderBit}
|
||||
}
|
||||
|
||||
func (r *regionInfo) IsTender() bool {
|
||||
return r.code&nonTenderBit == 0
|
||||
}
|
||||
|
||||
func (r *regionInfo) From() (time.Time, bool) {
|
||||
if r.from == 0 {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return fromDate(r.from), true
|
||||
}
|
||||
|
||||
func (r *regionInfo) To() (time.Time, bool) {
|
||||
if r.to == 0 {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return fromDate(r.to), true
|
||||
}
|
||||
107
vendor/golang.org/x/text/currency/query_test.go
generated
vendored
Normal file
107
vendor/golang.org/x/text/currency/query_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2016 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 currency
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func TestQuery(t *testing.T) {
|
||||
r := func(region string) language.Region {
|
||||
return language.MustParseRegion(region)
|
||||
}
|
||||
t1800, _ := time.Parse("2006-01-02", "1800-01-01")
|
||||
type result struct {
|
||||
region language.Region
|
||||
unit Unit
|
||||
isTender bool
|
||||
from, to string
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
opts []QueryOption
|
||||
results []result
|
||||
}{{
|
||||
name: "XA",
|
||||
opts: []QueryOption{Region(r("XA"))},
|
||||
results: []result{},
|
||||
}, {
|
||||
name: "AC",
|
||||
opts: []QueryOption{Region(r("AC"))},
|
||||
results: []result{
|
||||
{r("AC"), MustParseISO("SHP"), true, "1976-01-01", ""},
|
||||
},
|
||||
}, {
|
||||
name: "US",
|
||||
opts: []QueryOption{Region(r("US"))},
|
||||
results: []result{
|
||||
{r("US"), MustParseISO("USD"), true, "1792-01-01", ""},
|
||||
},
|
||||
}, {
|
||||
name: "US-hist",
|
||||
opts: []QueryOption{Region(r("US")), Historical},
|
||||
results: []result{
|
||||
{r("US"), MustParseISO("USD"), true, "1792-01-01", ""},
|
||||
},
|
||||
}, {
|
||||
name: "US-non-tender",
|
||||
opts: []QueryOption{Region(r("US")), NonTender},
|
||||
results: []result{
|
||||
{r("US"), MustParseISO("USD"), true, "1792-01-01", ""},
|
||||
{r("US"), MustParseISO("USN"), false, "", ""},
|
||||
},
|
||||
}, {
|
||||
name: "US-historical+non-tender",
|
||||
opts: []QueryOption{Region(r("US")), Historical, NonTender},
|
||||
results: []result{
|
||||
{r("US"), MustParseISO("USD"), true, "1792-01-01", ""},
|
||||
{r("US"), MustParseISO("USN"), false, "", ""},
|
||||
{r("US"), MustParseISO("USS"), false, "", "2014-03-01"},
|
||||
},
|
||||
}, {
|
||||
name: "1800",
|
||||
opts: []QueryOption{Date(t1800)},
|
||||
results: []result{
|
||||
{r("CH"), MustParseISO("CHF"), true, "1799-03-17", ""},
|
||||
{r("GB"), MustParseISO("GBP"), true, "1694-07-27", ""},
|
||||
{r("GI"), MustParseISO("GIP"), true, "1713-01-01", ""},
|
||||
// The date for IE and PR seem wrong, so these may be updated at
|
||||
// some point causing the tests to fail.
|
||||
{r("IE"), MustParseISO("GBP"), true, "1800-01-01", "1922-01-01"},
|
||||
{r("PR"), MustParseISO("ESP"), true, "1800-01-01", "1898-12-10"},
|
||||
{r("US"), MustParseISO("USD"), true, "1792-01-01", ""},
|
||||
},
|
||||
}}
|
||||
for _, tc := range testCases {
|
||||
n := 0
|
||||
for it := Query(tc.opts...); it.Next(); n++ {
|
||||
if n < len(tc.results) {
|
||||
got := result{
|
||||
it.Region(),
|
||||
it.Unit(),
|
||||
it.IsTender(),
|
||||
getTime(it.From()),
|
||||
getTime(it.To()),
|
||||
}
|
||||
if got != tc.results[n] {
|
||||
t.Errorf("%s:%d: got %v; want %v", tc.name, n, got, tc.results[n])
|
||||
}
|
||||
}
|
||||
}
|
||||
if n != len(tc.results) {
|
||||
t.Errorf("%s: unexpected number of results: got %d; want %d", tc.name, n, len(tc.results))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getTime(t time.Time, ok bool) string {
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return t.Format("2006-01-02")
|
||||
}
|
||||
2480
vendor/golang.org/x/text/currency/tables.go
generated
vendored
Normal file
2480
vendor/golang.org/x/text/currency/tables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
93
vendor/golang.org/x/text/currency/tables_test.go
generated
vendored
Normal file
93
vendor/golang.org/x/text/currency/tables_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package currency
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/internal/gen"
|
||||
"golang.org/x/text/internal/testtext"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
"golang.org/x/text/unicode/cldr"
|
||||
)
|
||||
|
||||
var draft = flag.String("draft",
|
||||
"contributed",
|
||||
`Minimal draft requirements (approved, contributed, provisional, unconfirmed).`)
|
||||
|
||||
func TestTables(t *testing.T) {
|
||||
testtext.SkipIfNotLong(t)
|
||||
|
||||
// Read the CLDR zip file.
|
||||
r := gen.OpenCLDRCoreZip()
|
||||
defer r.Close()
|
||||
|
||||
d := &cldr.Decoder{}
|
||||
d.SetDirFilter("supplemental", "main")
|
||||
d.SetSectionFilter("numbers")
|
||||
data, err := d.DecodeZip(r)
|
||||
if err != nil {
|
||||
t.Fatalf("DecodeZip: %v", err)
|
||||
}
|
||||
|
||||
dr, err := cldr.ParseDraft(*draft)
|
||||
if err != nil {
|
||||
t.Fatalf("filter: %v", err)
|
||||
}
|
||||
|
||||
for _, lang := range data.Locales() {
|
||||
p := message.NewPrinter(language.MustParse(lang))
|
||||
|
||||
ldml := data.RawLDML(lang)
|
||||
if ldml.Numbers == nil || ldml.Numbers.Currencies == nil {
|
||||
continue
|
||||
}
|
||||
for _, c := range ldml.Numbers.Currencies.Currency {
|
||||
syms := cldr.MakeSlice(&c.Symbol)
|
||||
syms.SelectDraft(dr)
|
||||
|
||||
for _, sym := range c.Symbol {
|
||||
cur, err := ParseISO(c.Type)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
formatter := Symbol
|
||||
switch sym.Alt {
|
||||
case "":
|
||||
case "narrow":
|
||||
formatter = NarrowSymbol
|
||||
default:
|
||||
continue
|
||||
}
|
||||
want := sym.Data()
|
||||
if got := p.Sprint(formatter(cur)); got != want {
|
||||
t.Errorf("%s:%sSymbol(%s) = %s; want %s", lang, strings.Title(sym.Alt), c.Type, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, reg := range data.Supplemental().CurrencyData.Region {
|
||||
i := 0
|
||||
for ; regionData[i].Region().String() != reg.Iso3166; i++ {
|
||||
}
|
||||
it := Query(Historical, NonTender, Region(language.MustParseRegion(reg.Iso3166)))
|
||||
for _, cur := range reg.Currency {
|
||||
from, _ := time.Parse("2006-01-02", cur.From)
|
||||
to, _ := time.Parse("2006-01-02", cur.To)
|
||||
|
||||
it.Next()
|
||||
for j, r := range []QueryIter{&iter{regionInfo: ®ionData[i]}, it} {
|
||||
if got, _ := r.From(); from != got {
|
||||
t.Errorf("%d:%s:%s:from: got %v; want %v", j, reg.Iso3166, cur.Iso4217, got, from)
|
||||
}
|
||||
if got, _ := r.To(); to != got {
|
||||
t.Errorf("%d:%s:%s:to: got %v; want %v", j, reg.Iso3166, cur.Iso4217, got, to)
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
13
vendor/golang.org/x/text/doc.go
generated
vendored
Normal file
13
vendor/golang.org/x/text/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// 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.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// text is a repository of text-related packages related to internationalization
|
||||
// (i18n) and localization (l10n), such as character encodings, text
|
||||
// transformations, and locale-specific text handling.
|
||||
package text
|
||||
|
||||
// TODO: more documentation on general concepts, such as Transformers, use
|
||||
// of normalization, etc.
|
||||
209
vendor/golang.org/x/text/encoding/charmap/charmap.go
generated
vendored
Normal file
209
vendor/golang.org/x/text/encoding/charmap/charmap.go
generated
vendored
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
// 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.
|
||||
|
||||
//go:generate go run maketables.go
|
||||
|
||||
// Package charmap provides simple character encodings such as IBM Code Page 437
|
||||
// and Windows 1252.
|
||||
package charmap // import "golang.org/x/text/encoding/charmap"
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// These encodings vary only in the way clients should interpret them. Their
|
||||
// coded character set is identical and a single implementation can be shared.
|
||||
var (
|
||||
// ISO8859_6E is the ISO 8859-6E encoding.
|
||||
ISO8859_6E encoding.Encoding = &iso8859_6E
|
||||
|
||||
// ISO8859_6I is the ISO 8859-6I encoding.
|
||||
ISO8859_6I encoding.Encoding = &iso8859_6I
|
||||
|
||||
// ISO8859_8E is the ISO 8859-8E encoding.
|
||||
ISO8859_8E encoding.Encoding = &iso8859_8E
|
||||
|
||||
// ISO8859_8I is the ISO 8859-8I encoding.
|
||||
ISO8859_8I encoding.Encoding = &iso8859_8I
|
||||
|
||||
iso8859_6E = internal.Encoding{
|
||||
ISO8859_6,
|
||||
"ISO-8859-6E",
|
||||
identifier.ISO88596E,
|
||||
}
|
||||
|
||||
iso8859_6I = internal.Encoding{
|
||||
ISO8859_6,
|
||||
"ISO-8859-6I",
|
||||
identifier.ISO88596I,
|
||||
}
|
||||
|
||||
iso8859_8E = internal.Encoding{
|
||||
ISO8859_8,
|
||||
"ISO-8859-8E",
|
||||
identifier.ISO88598E,
|
||||
}
|
||||
|
||||
iso8859_8I = internal.Encoding{
|
||||
ISO8859_8,
|
||||
"ISO-8859-8I",
|
||||
identifier.ISO88598I,
|
||||
}
|
||||
)
|
||||
|
||||
// All is a list of all defined encodings in this package.
|
||||
var All = listAll
|
||||
|
||||
// TODO: implement these encodings, in order of importance.
|
||||
// ASCII, ISO8859_1: Rather common. Close to Windows 1252.
|
||||
// ISO8859_9: Close to Windows 1254.
|
||||
|
||||
// utf8Enc holds a rune's UTF-8 encoding in data[:len].
|
||||
type utf8Enc struct {
|
||||
len uint8
|
||||
data [3]byte
|
||||
}
|
||||
|
||||
// charmap describes an 8-bit character set encoding.
|
||||
type charmap struct {
|
||||
// name is the encoding's name.
|
||||
name string
|
||||
// mib is the encoding type of this encoder.
|
||||
mib identifier.MIB
|
||||
// asciiSuperset states whether the encoding is a superset of ASCII.
|
||||
asciiSuperset bool
|
||||
// low is the lower bound of the encoded byte for a non-ASCII rune. If
|
||||
// charmap.asciiSuperset is true then this will be 0x80, otherwise 0x00.
|
||||
low uint8
|
||||
// replacement is the encoded replacement character.
|
||||
replacement byte
|
||||
// decode is the map from encoded byte to UTF-8.
|
||||
decode [256]utf8Enc
|
||||
// encoding is the map from runes to encoded bytes. Each entry is a
|
||||
// uint32: the high 8 bits are the encoded byte and the low 24 bits are
|
||||
// the rune. The table entries are sorted by ascending rune.
|
||||
encode [256]uint32
|
||||
}
|
||||
|
||||
func (m *charmap) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: charmapDecoder{charmap: m}}
|
||||
}
|
||||
|
||||
func (m *charmap) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: charmapEncoder{charmap: m}}
|
||||
}
|
||||
|
||||
func (m *charmap) String() string {
|
||||
return m.name
|
||||
}
|
||||
|
||||
func (m *charmap) ID() (mib identifier.MIB, other string) {
|
||||
return m.mib, ""
|
||||
}
|
||||
|
||||
// charmapDecoder implements transform.Transformer by decoding to UTF-8.
|
||||
type charmapDecoder struct {
|
||||
transform.NopResetter
|
||||
charmap *charmap
|
||||
}
|
||||
|
||||
func (m charmapDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for i, c := range src {
|
||||
if m.charmap.asciiSuperset && c < utf8.RuneSelf {
|
||||
if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = c
|
||||
nDst++
|
||||
nSrc = i + 1
|
||||
continue
|
||||
}
|
||||
|
||||
decode := &m.charmap.decode[c]
|
||||
n := int(decode.len)
|
||||
if nDst+n > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
// It's 15% faster to avoid calling copy for these tiny slices.
|
||||
for j := 0; j < n; j++ {
|
||||
dst[nDst] = decode.data[j]
|
||||
nDst++
|
||||
}
|
||||
nSrc = i + 1
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
// charmapEncoder implements transform.Transformer by encoding from UTF-8.
|
||||
type charmapEncoder struct {
|
||||
transform.NopResetter
|
||||
charmap *charmap
|
||||
}
|
||||
|
||||
func (m charmapEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
loop:
|
||||
for nSrc < len(src) {
|
||||
if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
r = rune(src[nSrc])
|
||||
|
||||
// Decode a 1-byte rune.
|
||||
if r < utf8.RuneSelf {
|
||||
if m.charmap.asciiSuperset {
|
||||
nSrc++
|
||||
dst[nDst] = uint8(r)
|
||||
nDst++
|
||||
continue
|
||||
}
|
||||
size = 1
|
||||
|
||||
} else {
|
||||
// Decode a multi-byte rune.
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
} else {
|
||||
err = internal.RepertoireError(m.charmap.replacement)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Binary search in [low, high) for that rune in the m.charmap.encode table.
|
||||
for low, high := int(m.charmap.low), 0x100; ; {
|
||||
if low >= high {
|
||||
err = internal.RepertoireError(m.charmap.replacement)
|
||||
break loop
|
||||
}
|
||||
mid := (low + high) / 2
|
||||
got := m.charmap.encode[mid]
|
||||
gotRune := rune(got & (1<<24 - 1))
|
||||
if gotRune < r {
|
||||
low = mid + 1
|
||||
} else if gotRune > r {
|
||||
high = mid
|
||||
} else {
|
||||
dst[nDst] = byte(got >> 24)
|
||||
nDst++
|
||||
break
|
||||
}
|
||||
}
|
||||
nSrc += size
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
45
vendor/golang.org/x/text/encoding/charmap/charmap_test.go
generated
vendored
Normal file
45
vendor/golang.org/x/text/encoding/charmap/charmap_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// 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 charmap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func dec(e encoding.Encoding) (dir string, t transform.Transformer, err error) {
|
||||
return "Decode", e.NewDecoder(), nil
|
||||
}
|
||||
func enc(e encoding.Encoding) (dir string, t transform.Transformer, err error) {
|
||||
return "Encode", e.NewEncoder(), internal.ErrASCIIReplacement
|
||||
}
|
||||
|
||||
func TestNonRepertoire(t *testing.T) {
|
||||
testCases := []struct {
|
||||
init func(e encoding.Encoding) (string, transform.Transformer, error)
|
||||
e encoding.Encoding
|
||||
src, want string
|
||||
}{
|
||||
{dec, Windows1252, "\x81", "\ufffd"},
|
||||
|
||||
{enc, Windows1252, "갂", ""},
|
||||
{enc, Windows1252, "a갂", "a"},
|
||||
{enc, Windows1252, "\u00E9갂", "\xE9"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
dir, tr, wantErr := tc.init(tc.e)
|
||||
|
||||
dst, _, err := transform.String(tr, tc.src)
|
||||
if err != wantErr {
|
||||
t.Errorf("%s %v(%q): got %v; want %v", dir, tc.e, tc.src, err, wantErr)
|
||||
}
|
||||
if got := string(dst); got != tc.want {
|
||||
t.Errorf("%s %v(%q):\ngot %q\nwant %q", dir, tc.e, tc.src, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
500
vendor/golang.org/x/text/encoding/charmap/maketables.go
generated
vendored
Normal file
500
vendor/golang.org/x/text/encoding/charmap/maketables.go
generated
vendored
Normal file
|
|
@ -0,0 +1,500 @@
|
|||
// 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/internal/gen"
|
||||
)
|
||||
|
||||
const ascii = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" +
|
||||
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
|
||||
` !"#$%&'()*+,-./0123456789:;<=>?` +
|
||||
`@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_` +
|
||||
"`abcdefghijklmnopqrstuvwxyz{|}~\u007f"
|
||||
|
||||
var encodings = []struct {
|
||||
name string
|
||||
mib string
|
||||
comment string
|
||||
varName string
|
||||
replacement byte
|
||||
mapping string
|
||||
}{
|
||||
{
|
||||
"IBM Code Page 437",
|
||||
"PC8CodePage437",
|
||||
"",
|
||||
"CodePage437",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM437-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 850",
|
||||
"PC850Multilingual",
|
||||
"",
|
||||
"CodePage850",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM850-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 852",
|
||||
"PCp852",
|
||||
"",
|
||||
"CodePage852",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM852-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 855",
|
||||
"IBM855",
|
||||
"",
|
||||
"CodePage855",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM855-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"Windows Code Page 858", // PC latin1 with Euro
|
||||
"IBM00858",
|
||||
"",
|
||||
"CodePage858",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/windows-858-2000.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 862",
|
||||
"PC862LatinHebrew",
|
||||
"",
|
||||
"CodePage862",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM862-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 866",
|
||||
"IBM866",
|
||||
"",
|
||||
"CodePage866",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-ibm866.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-1",
|
||||
"ISOLatin1",
|
||||
"",
|
||||
"ISO8859_1",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/iso-8859_1-1998.ucm",
|
||||
},
|
||||
{
|
||||
"ISO 8859-2",
|
||||
"ISOLatin2",
|
||||
"",
|
||||
"ISO8859_2",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-2.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-3",
|
||||
"ISOLatin3",
|
||||
"",
|
||||
"ISO8859_3",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-3.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-4",
|
||||
"ISOLatin4",
|
||||
"",
|
||||
"ISO8859_4",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-4.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-5",
|
||||
"ISOLatinCyrillic",
|
||||
"",
|
||||
"ISO8859_5",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-5.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-6",
|
||||
"ISOLatinArabic",
|
||||
"",
|
||||
"ISO8859_6,ISO8859_6E,ISO8859_6I",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-6.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-7",
|
||||
"ISOLatinGreek",
|
||||
"",
|
||||
"ISO8859_7",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-7.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-8",
|
||||
"ISOLatinHebrew",
|
||||
"",
|
||||
"ISO8859_8,ISO8859_8E,ISO8859_8I",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-8.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-10",
|
||||
"ISOLatin6",
|
||||
"",
|
||||
"ISO8859_10",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-10.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-13",
|
||||
"ISO885913",
|
||||
"",
|
||||
"ISO8859_13",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-13.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-14",
|
||||
"ISO885914",
|
||||
"",
|
||||
"ISO8859_14",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-14.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-15",
|
||||
"ISO885915",
|
||||
"",
|
||||
"ISO8859_15",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-15.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-16",
|
||||
"ISO885916",
|
||||
"",
|
||||
"ISO8859_16",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-16.txt",
|
||||
},
|
||||
{
|
||||
"KOI8-R",
|
||||
"KOI8R",
|
||||
"",
|
||||
"KOI8R",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-koi8-r.txt",
|
||||
},
|
||||
{
|
||||
"KOI8-U",
|
||||
"KOI8U",
|
||||
"",
|
||||
"KOI8U",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-koi8-u.txt",
|
||||
},
|
||||
{
|
||||
"Macintosh",
|
||||
"Macintosh",
|
||||
"",
|
||||
"Macintosh",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-macintosh.txt",
|
||||
},
|
||||
{
|
||||
"Macintosh Cyrillic",
|
||||
"MacintoshCyrillic",
|
||||
"",
|
||||
"MacintoshCyrillic",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-x-mac-cyrillic.txt",
|
||||
},
|
||||
{
|
||||
"Windows 874",
|
||||
"Windows874",
|
||||
"",
|
||||
"Windows874",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-874.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1250",
|
||||
"Windows1250",
|
||||
"",
|
||||
"Windows1250",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1250.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1251",
|
||||
"Windows1251",
|
||||
"",
|
||||
"Windows1251",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1251.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1252",
|
||||
"Windows1252",
|
||||
"",
|
||||
"Windows1252",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1252.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1253",
|
||||
"Windows1253",
|
||||
"",
|
||||
"Windows1253",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1253.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1254",
|
||||
"Windows1254",
|
||||
"",
|
||||
"Windows1254",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1254.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1255",
|
||||
"Windows1255",
|
||||
"",
|
||||
"Windows1255",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1255.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1256",
|
||||
"Windows1256",
|
||||
"",
|
||||
"Windows1256",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1256.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1257",
|
||||
"Windows1257",
|
||||
"",
|
||||
"Windows1257",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1257.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1258",
|
||||
"Windows1258",
|
||||
"",
|
||||
"Windows1258",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1258.txt",
|
||||
},
|
||||
{
|
||||
"X-User-Defined",
|
||||
"XUserDefined",
|
||||
"It is defined at http://encoding.spec.whatwg.org/#x-user-defined",
|
||||
"XUserDefined",
|
||||
encoding.ASCIISub,
|
||||
ascii +
|
||||
"\uf780\uf781\uf782\uf783\uf784\uf785\uf786\uf787" +
|
||||
"\uf788\uf789\uf78a\uf78b\uf78c\uf78d\uf78e\uf78f" +
|
||||
"\uf790\uf791\uf792\uf793\uf794\uf795\uf796\uf797" +
|
||||
"\uf798\uf799\uf79a\uf79b\uf79c\uf79d\uf79e\uf79f" +
|
||||
"\uf7a0\uf7a1\uf7a2\uf7a3\uf7a4\uf7a5\uf7a6\uf7a7" +
|
||||
"\uf7a8\uf7a9\uf7aa\uf7ab\uf7ac\uf7ad\uf7ae\uf7af" +
|
||||
"\uf7b0\uf7b1\uf7b2\uf7b3\uf7b4\uf7b5\uf7b6\uf7b7" +
|
||||
"\uf7b8\uf7b9\uf7ba\uf7bb\uf7bc\uf7bd\uf7be\uf7bf" +
|
||||
"\uf7c0\uf7c1\uf7c2\uf7c3\uf7c4\uf7c5\uf7c6\uf7c7" +
|
||||
"\uf7c8\uf7c9\uf7ca\uf7cb\uf7cc\uf7cd\uf7ce\uf7cf" +
|
||||
"\uf7d0\uf7d1\uf7d2\uf7d3\uf7d4\uf7d5\uf7d6\uf7d7" +
|
||||
"\uf7d8\uf7d9\uf7da\uf7db\uf7dc\uf7dd\uf7de\uf7df" +
|
||||
"\uf7e0\uf7e1\uf7e2\uf7e3\uf7e4\uf7e5\uf7e6\uf7e7" +
|
||||
"\uf7e8\uf7e9\uf7ea\uf7eb\uf7ec\uf7ed\uf7ee\uf7ef" +
|
||||
"\uf7f0\uf7f1\uf7f2\uf7f3\uf7f4\uf7f5\uf7f6\uf7f7" +
|
||||
"\uf7f8\uf7f9\uf7fa\uf7fb\uf7fc\uf7fd\uf7fe\uf7ff",
|
||||
},
|
||||
}
|
||||
|
||||
func getWHATWG(url string) string {
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Fatalf("%q: Get: %v", url, err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
mapping := make([]rune, 128)
|
||||
for i := range mapping {
|
||||
mapping[i] = '\ufffd'
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(res.Body)
|
||||
for scanner.Scan() {
|
||||
s := strings.TrimSpace(scanner.Text())
|
||||
if s == "" || s[0] == '#' {
|
||||
continue
|
||||
}
|
||||
x, y := 0, 0
|
||||
if _, err := fmt.Sscanf(s, "%d\t0x%x", &x, &y); err != nil {
|
||||
log.Fatalf("could not parse %q", s)
|
||||
}
|
||||
if x < 0 || 128 <= x {
|
||||
log.Fatalf("code %d is out of range", x)
|
||||
}
|
||||
if 0x80 <= y && y < 0xa0 {
|
||||
// We diverge from the WHATWG spec by mapping control characters
|
||||
// in the range [0x80, 0xa0) to U+FFFD.
|
||||
continue
|
||||
}
|
||||
mapping[x] = rune(y)
|
||||
}
|
||||
return ascii + string(mapping)
|
||||
}
|
||||
|
||||
func getUCM(url string) string {
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Fatalf("%q: Get: %v", url, err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
mapping := make([]rune, 256)
|
||||
for i := range mapping {
|
||||
mapping[i] = '\ufffd'
|
||||
}
|
||||
|
||||
charsFound := 0
|
||||
scanner := bufio.NewScanner(res.Body)
|
||||
for scanner.Scan() {
|
||||
s := strings.TrimSpace(scanner.Text())
|
||||
if s == "" || s[0] == '#' {
|
||||
continue
|
||||
}
|
||||
var c byte
|
||||
var r rune
|
||||
if _, err := fmt.Sscanf(s, `<U%x> \x%x |0`, &r, &c); err != nil {
|
||||
continue
|
||||
}
|
||||
mapping[c] = r
|
||||
charsFound++
|
||||
}
|
||||
|
||||
if charsFound < 200 {
|
||||
log.Fatalf("%q: only %d characters found (wrong page format?)", url, charsFound)
|
||||
}
|
||||
|
||||
return string(mapping)
|
||||
}
|
||||
|
||||
func main() {
|
||||
mibs := map[string]bool{}
|
||||
all := []string{}
|
||||
|
||||
w := gen.NewCodeWriter()
|
||||
defer w.WriteGoFile("tables.go", "charmap")
|
||||
|
||||
printf := func(s string, a ...interface{}) { fmt.Fprintf(w, s, a...) }
|
||||
|
||||
printf("import (\n")
|
||||
printf("\t\"golang.org/x/text/encoding\"\n")
|
||||
printf("\t\"golang.org/x/text/encoding/internal/identifier\"\n")
|
||||
printf(")\n\n")
|
||||
for _, e := range encodings {
|
||||
varNames := strings.Split(e.varName, ",")
|
||||
all = append(all, varNames...)
|
||||
varName := varNames[0]
|
||||
switch {
|
||||
case strings.HasPrefix(e.mapping, "http://encoding.spec.whatwg.org/"):
|
||||
e.mapping = getWHATWG(e.mapping)
|
||||
case strings.HasPrefix(e.mapping, "http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/"):
|
||||
e.mapping = getUCM(e.mapping)
|
||||
}
|
||||
|
||||
asciiSuperset, low := strings.HasPrefix(e.mapping, ascii), 0x00
|
||||
if asciiSuperset {
|
||||
low = 0x80
|
||||
}
|
||||
lvn := 1
|
||||
if strings.HasPrefix(varName, "ISO") || strings.HasPrefix(varName, "KOI") {
|
||||
lvn = 3
|
||||
}
|
||||
lowerVarName := strings.ToLower(varName[:lvn]) + varName[lvn:]
|
||||
printf("// %s is the %s encoding.\n", varName, e.name)
|
||||
if e.comment != "" {
|
||||
printf("//\n// %s\n", e.comment)
|
||||
}
|
||||
printf("var %s encoding.Encoding = &%s\n\nvar %s = charmap{\nname: %q,\n",
|
||||
varName, lowerVarName, lowerVarName, e.name)
|
||||
if mibs[e.mib] {
|
||||
log.Fatalf("MIB type %q declared multiple times.", e.mib)
|
||||
}
|
||||
printf("mib: identifier.%s,\n", e.mib)
|
||||
printf("asciiSuperset: %t,\n", asciiSuperset)
|
||||
printf("low: 0x%02x,\n", low)
|
||||
printf("replacement: 0x%02x,\n", e.replacement)
|
||||
|
||||
printf("decode: [256]utf8Enc{\n")
|
||||
i, backMapping := 0, map[rune]byte{}
|
||||
for _, c := range e.mapping {
|
||||
if _, ok := backMapping[c]; !ok && c != utf8.RuneError {
|
||||
backMapping[c] = byte(i)
|
||||
}
|
||||
var buf [8]byte
|
||||
n := utf8.EncodeRune(buf[:], c)
|
||||
if n > 3 {
|
||||
panic(fmt.Sprintf("rune %q (%U) is too long", c, c))
|
||||
}
|
||||
printf("{%d,[3]byte{0x%02x,0x%02x,0x%02x}},", n, buf[0], buf[1], buf[2])
|
||||
if i%2 == 1 {
|
||||
printf("\n")
|
||||
}
|
||||
i++
|
||||
}
|
||||
printf("},\n")
|
||||
|
||||
printf("encode: [256]uint32{\n")
|
||||
encode := make([]uint32, 0, 256)
|
||||
for c, i := range backMapping {
|
||||
encode = append(encode, uint32(i)<<24|uint32(c))
|
||||
}
|
||||
sort.Sort(byRune(encode))
|
||||
for len(encode) < cap(encode) {
|
||||
encode = append(encode, encode[len(encode)-1])
|
||||
}
|
||||
for i, enc := range encode {
|
||||
printf("0x%08x,", enc)
|
||||
if i%8 == 7 {
|
||||
printf("\n")
|
||||
}
|
||||
}
|
||||
printf("},\n}\n")
|
||||
|
||||
// Add an estimate of the size of a single charmap{} struct value, which
|
||||
// includes two 256 elem arrays of 4 bytes and some extra fields, which
|
||||
// align to 3 uint64s on 64-bit architectures.
|
||||
w.Size += 2*4*256 + 3*8
|
||||
}
|
||||
// TODO: add proper line breaking.
|
||||
printf("var listAll = []encoding.Encoding{\n%s,\n}\n\n", strings.Join(all, ",\n"))
|
||||
}
|
||||
|
||||
type byRune []uint32
|
||||
|
||||
func (b byRune) Len() int { return len(b) }
|
||||
func (b byRune) Less(i, j int) bool { return b[i]&0xffffff < b[j]&0xffffff }
|
||||
func (b byRune) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
6178
vendor/golang.org/x/text/encoding/charmap/tables.go
generated
vendored
Normal file
6178
vendor/golang.org/x/text/encoding/charmap/tables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
335
vendor/golang.org/x/text/encoding/encoding.go
generated
vendored
Normal file
335
vendor/golang.org/x/text/encoding/encoding.go
generated
vendored
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
// 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 encoding defines an interface for character encodings, such as Shift
|
||||
// JIS and Windows 1252, that can convert to and from UTF-8.
|
||||
//
|
||||
// Encoding implementations are provided in other packages, such as
|
||||
// golang.org/x/text/encoding/charmap and
|
||||
// golang.org/x/text/encoding/japanese.
|
||||
package encoding // import "golang.org/x/text/encoding"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// TODO:
|
||||
// - There seems to be some inconsistency in when decoders return errors
|
||||
// and when not. Also documentation seems to suggest they shouldn't return
|
||||
// errors at all (except for UTF-16).
|
||||
// - Encoders seem to rely on or at least benefit from the input being in NFC
|
||||
// normal form. Perhaps add an example how users could prepare their output.
|
||||
|
||||
// Encoding is a character set encoding that can be transformed to and from
|
||||
// UTF-8.
|
||||
type Encoding interface {
|
||||
// NewDecoder returns a Decoder.
|
||||
NewDecoder() *Decoder
|
||||
|
||||
// NewEncoder returns an Encoder.
|
||||
NewEncoder() *Encoder
|
||||
}
|
||||
|
||||
// A Decoder converts bytes to UTF-8. It implements transform.Transformer.
|
||||
//
|
||||
// Transforming source bytes that are not of that encoding will not result in an
|
||||
// error per se. Each byte that cannot be transcoded will be represented in the
|
||||
// output by the UTF-8 encoding of '\uFFFD', the replacement rune.
|
||||
type Decoder struct {
|
||||
transform.Transformer
|
||||
|
||||
// This forces external creators of Decoders to use names in struct
|
||||
// initializers, allowing for future extendibility without having to break
|
||||
// code.
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// Bytes converts the given encoded bytes to UTF-8. It returns the converted
|
||||
// bytes or 0, err if any error occurred.
|
||||
func (d *Decoder) Bytes(b []byte) ([]byte, error) {
|
||||
b, _, err := transform.Bytes(d, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// String converts the given encoded string to UTF-8. It returns the converted
|
||||
// string or 0, err if any error occurred.
|
||||
func (d *Decoder) String(s string) (string, error) {
|
||||
s, _, err := transform.String(d, s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Reader wraps another Reader to decode its bytes.
|
||||
//
|
||||
// The Decoder may not be used for any other operation as long as the returned
|
||||
// Reader is in use.
|
||||
func (d *Decoder) Reader(r io.Reader) io.Reader {
|
||||
return transform.NewReader(r, d)
|
||||
}
|
||||
|
||||
// An Encoder converts bytes from UTF-8. It implements transform.Transformer.
|
||||
//
|
||||
// Each rune that cannot be transcoded will result in an error. In this case,
|
||||
// the transform will consume all source byte up to, not including the offending
|
||||
// rune. Transforming source bytes that are not valid UTF-8 will be replaced by
|
||||
// `\uFFFD`. To return early with an error instead, use transform.Chain to
|
||||
// preprocess the data with a UTF8Validator.
|
||||
type Encoder struct {
|
||||
transform.Transformer
|
||||
|
||||
// This forces external creators of Encoders to use names in struct
|
||||
// initializers, allowing for future extendibility without having to break
|
||||
// code.
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// Bytes converts bytes from UTF-8. It returns the converted bytes or 0, err if
|
||||
// any error occurred.
|
||||
func (e *Encoder) Bytes(b []byte) ([]byte, error) {
|
||||
b, _, err := transform.Bytes(e, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// String converts a string from UTF-8. It returns the converted string or
|
||||
// 0, err if any error occurred.
|
||||
func (e *Encoder) String(s string) (string, error) {
|
||||
s, _, err := transform.String(e, s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Writer wraps another Writer to encode its UTF-8 output.
|
||||
//
|
||||
// The Encoder may not be used for any other operation as long as the returned
|
||||
// Writer is in use.
|
||||
func (e *Encoder) Writer(w io.Writer) io.Writer {
|
||||
return transform.NewWriter(w, e)
|
||||
}
|
||||
|
||||
// ASCIISub is the ASCII substitute character, as recommended by
|
||||
// http://unicode.org/reports/tr36/#Text_Comparison
|
||||
const ASCIISub = '\x1a'
|
||||
|
||||
// Nop is the nop encoding. Its transformed bytes are the same as the source
|
||||
// bytes; it does not replace invalid UTF-8 sequences.
|
||||
var Nop Encoding = nop{}
|
||||
|
||||
type nop struct{}
|
||||
|
||||
func (nop) NewDecoder() *Decoder {
|
||||
return &Decoder{Transformer: transform.Nop}
|
||||
}
|
||||
func (nop) NewEncoder() *Encoder {
|
||||
return &Encoder{Transformer: transform.Nop}
|
||||
}
|
||||
|
||||
// Replacement is the replacement encoding. Decoding from the replacement
|
||||
// encoding yields a single '\uFFFD' replacement rune. Encoding from UTF-8 to
|
||||
// the replacement encoding yields the same as the source bytes except that
|
||||
// invalid UTF-8 is converted to '\uFFFD'.
|
||||
//
|
||||
// It is defined at http://encoding.spec.whatwg.org/#replacement
|
||||
var Replacement Encoding = replacement{}
|
||||
|
||||
type replacement struct{}
|
||||
|
||||
func (replacement) NewDecoder() *Decoder {
|
||||
return &Decoder{Transformer: replacementDecoder{}}
|
||||
}
|
||||
|
||||
func (replacement) NewEncoder() *Encoder {
|
||||
return &Encoder{Transformer: replacementEncoder{}}
|
||||
}
|
||||
|
||||
func (replacement) ID() (mib identifier.MIB, other string) {
|
||||
return identifier.Replacement, ""
|
||||
}
|
||||
|
||||
type replacementDecoder struct{ transform.NopResetter }
|
||||
|
||||
func (replacementDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if len(dst) < 3 {
|
||||
return 0, 0, transform.ErrShortDst
|
||||
}
|
||||
if atEOF {
|
||||
const fffd = "\ufffd"
|
||||
dst[0] = fffd[0]
|
||||
dst[1] = fffd[1]
|
||||
dst[2] = fffd[2]
|
||||
nDst = 3
|
||||
}
|
||||
return nDst, len(src), nil
|
||||
}
|
||||
|
||||
type replacementEncoder struct{ transform.NopResetter }
|
||||
|
||||
func (replacementEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
r = rune(src[nSrc])
|
||||
|
||||
// Decode a 1-byte rune.
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
|
||||
} else {
|
||||
// Decode a multi-byte rune.
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
r = '\ufffd'
|
||||
}
|
||||
}
|
||||
|
||||
if nDst+utf8.RuneLen(r) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
nDst += utf8.EncodeRune(dst[nDst:], r)
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
// HTMLEscapeUnsupported wraps encoders to replace source runes outside the
|
||||
// repertoire of the destination encoding with HTML escape sequences.
|
||||
//
|
||||
// This wrapper exists to comply to URL and HTML forms requiring a
|
||||
// non-terminating legacy encoder. The produced sequences may lead to data
|
||||
// loss as they are indistinguishable from legitimate input. To avoid this
|
||||
// issue, use UTF-8 encodings whenever possible.
|
||||
func HTMLEscapeUnsupported(e *Encoder) *Encoder {
|
||||
return &Encoder{Transformer: &errorHandler{e, errorToHTML}}
|
||||
}
|
||||
|
||||
// ReplaceUnsupported wraps encoders to replace source runes outside the
|
||||
// repertoire of the destination encoding with an encoding-specific
|
||||
// replacement.
|
||||
//
|
||||
// This wrapper is only provided for backwards compatibility and legacy
|
||||
// handling. Its use is strongly discouraged. Use UTF-8 whenever possible.
|
||||
func ReplaceUnsupported(e *Encoder) *Encoder {
|
||||
return &Encoder{Transformer: &errorHandler{e, errorToReplacement}}
|
||||
}
|
||||
|
||||
type errorHandler struct {
|
||||
*Encoder
|
||||
handler func(dst []byte, r rune, err repertoireError) (n int, ok bool)
|
||||
}
|
||||
|
||||
// TODO: consider making this error public in some form.
|
||||
type repertoireError interface {
|
||||
Replacement() byte
|
||||
}
|
||||
|
||||
func (h errorHandler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
nDst, nSrc, err = h.Transformer.Transform(dst, src, atEOF)
|
||||
for err != nil {
|
||||
rerr, ok := err.(repertoireError)
|
||||
if !ok {
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
r, sz := utf8.DecodeRune(src[nSrc:])
|
||||
n, ok := h.handler(dst[nDst:], r, rerr)
|
||||
if !ok {
|
||||
return nDst, nSrc, transform.ErrShortDst
|
||||
}
|
||||
err = nil
|
||||
nDst += n
|
||||
if nSrc += sz; nSrc < len(src) {
|
||||
var dn, sn int
|
||||
dn, sn, err = h.Transformer.Transform(dst[nDst:], src[nSrc:], atEOF)
|
||||
nDst += dn
|
||||
nSrc += sn
|
||||
}
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
func errorToHTML(dst []byte, r rune, err repertoireError) (n int, ok bool) {
|
||||
buf := [8]byte{}
|
||||
b := strconv.AppendUint(buf[:0], uint64(r), 10)
|
||||
if n = len(b) + len("&#;"); n >= len(dst) {
|
||||
return 0, false
|
||||
}
|
||||
dst[0] = '&'
|
||||
dst[1] = '#'
|
||||
dst[copy(dst[2:], b)+2] = ';'
|
||||
return n, true
|
||||
}
|
||||
|
||||
func errorToReplacement(dst []byte, r rune, err repertoireError) (n int, ok bool) {
|
||||
if len(dst) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
dst[0] = err.Replacement()
|
||||
return 1, true
|
||||
}
|
||||
|
||||
// ErrInvalidUTF8 means that a transformer encountered invalid UTF-8.
|
||||
var ErrInvalidUTF8 = errors.New("encoding: invalid UTF-8")
|
||||
|
||||
// UTF8Validator is a transformer that returns ErrInvalidUTF8 on the first
|
||||
// input byte that is not valid UTF-8.
|
||||
var UTF8Validator transform.Transformer = utf8Validator{}
|
||||
|
||||
type utf8Validator struct{ transform.NopResetter }
|
||||
|
||||
func (utf8Validator) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
n := len(src)
|
||||
if n > len(dst) {
|
||||
n = len(dst)
|
||||
}
|
||||
for i := 0; i < n; {
|
||||
if c := src[i]; c < utf8.RuneSelf {
|
||||
dst[i] = c
|
||||
i++
|
||||
continue
|
||||
}
|
||||
_, size := utf8.DecodeRune(src[i:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
err = ErrInvalidUTF8
|
||||
if !atEOF && !utf8.FullRune(src[i:]) {
|
||||
err = transform.ErrShortSrc
|
||||
}
|
||||
return i, i, err
|
||||
}
|
||||
if i+size > len(dst) {
|
||||
return i, i, transform.ErrShortDst
|
||||
}
|
||||
for ; size > 0; size-- {
|
||||
dst[i] = src[i]
|
||||
i++
|
||||
}
|
||||
}
|
||||
if len(src) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
}
|
||||
return n, n, err
|
||||
}
|
||||
1128
vendor/golang.org/x/text/encoding/encoding_test.go
generated
vendored
Normal file
1128
vendor/golang.org/x/text/encoding/encoding_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
42
vendor/golang.org/x/text/encoding/example_test.go
generated
vendored
Normal file
42
vendor/golang.org/x/text/encoding/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// 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 encoding_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func ExampleDecodeWindows1252() {
|
||||
sr := strings.NewReader("Gar\xe7on !")
|
||||
tr := charmap.Windows1252.NewDecoder().Reader(sr)
|
||||
io.Copy(os.Stdout, tr)
|
||||
// Output: Garçon !
|
||||
}
|
||||
|
||||
func ExampleUTF8Validator() {
|
||||
for i := 0; i < 2; i++ {
|
||||
var transformer transform.Transformer
|
||||
transformer = unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewEncoder()
|
||||
if i == 1 {
|
||||
transformer = transform.Chain(encoding.UTF8Validator, transformer)
|
||||
}
|
||||
dst := make([]byte, 256)
|
||||
src := []byte("abc\xffxyz") // src is invalid UTF-8.
|
||||
nDst, nSrc, err := transformer.Transform(dst, src, true)
|
||||
fmt.Printf("i=%d: produced %q, consumed %q, error %v\n",
|
||||
i, dst[:nDst], src[:nSrc], err)
|
||||
}
|
||||
// Output:
|
||||
// i=0: produced "\x00a\x00b\x00c\xff\xfd\x00x\x00y\x00z", consumed "abc\xffxyz", error <nil>
|
||||
// i=1: produced "\x00a\x00b\x00c", consumed "abc", error encoding: invalid UTF-8
|
||||
}
|
||||
167
vendor/golang.org/x/text/encoding/htmlindex/gen.go
generated
vendored
Normal file
167
vendor/golang.org/x/text/encoding/htmlindex/gen.go
generated
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/internal/gen"
|
||||
)
|
||||
|
||||
type group struct {
|
||||
Encodings []struct {
|
||||
Labels []string
|
||||
Name string
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
gen.Init()
|
||||
|
||||
r := gen.Open("http://www.w3.org/TR", "w3", "encoding/indexes/encodings.json")
|
||||
var groups []group
|
||||
if err := json.NewDecoder(r).Decode(&groups); err != nil {
|
||||
log.Fatalf("Error reading encodings.json: %v", err)
|
||||
}
|
||||
|
||||
w := &bytes.Buffer{}
|
||||
fmt.Fprintln(w, "type htmlEncoding byte")
|
||||
fmt.Fprintln(w, "const (")
|
||||
for i, g := range groups {
|
||||
for _, e := range g.Encodings {
|
||||
name := consts[e.Name]
|
||||
if name == "" {
|
||||
log.Fatalf("No const defined for %s.", e.Name)
|
||||
}
|
||||
if i == 0 {
|
||||
fmt.Fprintf(w, "%s htmlEncoding = iota\n", name)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\n", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(w, "numEncodings")
|
||||
fmt.Fprint(w, ")\n\n")
|
||||
|
||||
fmt.Fprintln(w, "var canonical = [numEncodings]string{")
|
||||
for _, g := range groups {
|
||||
for _, e := range g.Encodings {
|
||||
fmt.Fprintf(w, "%q,\n", e.Name)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, "}\n\n")
|
||||
|
||||
fmt.Fprintln(w, "var nameMap = map[string]htmlEncoding{")
|
||||
for _, g := range groups {
|
||||
for _, e := range g.Encodings {
|
||||
for _, l := range e.Labels {
|
||||
fmt.Fprintf(w, "%q: %s,\n", l, consts[e.Name])
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, "}\n\n")
|
||||
|
||||
var tags []string
|
||||
fmt.Fprintln(w, "var localeMap = []htmlEncoding{")
|
||||
for _, loc := range locales {
|
||||
tags = append(tags, loc.tag)
|
||||
fmt.Fprintf(w, "%s, // %s \n", consts[loc.name], loc.tag)
|
||||
}
|
||||
fmt.Fprint(w, "}\n\n")
|
||||
|
||||
fmt.Fprintf(w, "const locales = %q\n", strings.Join(tags, " "))
|
||||
|
||||
gen.WriteGoFile("tables.go", "htmlindex", w.Bytes())
|
||||
}
|
||||
|
||||
// consts maps canonical encoding name to internal constant.
|
||||
var consts = map[string]string{
|
||||
"utf-8": "utf8",
|
||||
"ibm866": "ibm866",
|
||||
"iso-8859-2": "iso8859_2",
|
||||
"iso-8859-3": "iso8859_3",
|
||||
"iso-8859-4": "iso8859_4",
|
||||
"iso-8859-5": "iso8859_5",
|
||||
"iso-8859-6": "iso8859_6",
|
||||
"iso-8859-7": "iso8859_7",
|
||||
"iso-8859-8": "iso8859_8",
|
||||
"iso-8859-8-i": "iso8859_8I",
|
||||
"iso-8859-10": "iso8859_10",
|
||||
"iso-8859-13": "iso8859_13",
|
||||
"iso-8859-14": "iso8859_14",
|
||||
"iso-8859-15": "iso8859_15",
|
||||
"iso-8859-16": "iso8859_16",
|
||||
"koi8-r": "koi8r",
|
||||
"koi8-u": "koi8u",
|
||||
"macintosh": "macintosh",
|
||||
"windows-874": "windows874",
|
||||
"windows-1250": "windows1250",
|
||||
"windows-1251": "windows1251",
|
||||
"windows-1252": "windows1252",
|
||||
"windows-1253": "windows1253",
|
||||
"windows-1254": "windows1254",
|
||||
"windows-1255": "windows1255",
|
||||
"windows-1256": "windows1256",
|
||||
"windows-1257": "windows1257",
|
||||
"windows-1258": "windows1258",
|
||||
"x-mac-cyrillic": "macintoshCyrillic",
|
||||
"gbk": "gbk",
|
||||
"gb18030": "gb18030",
|
||||
// "hz-gb-2312": "hzgb2312", // Was removed from WhatWG
|
||||
"big5": "big5",
|
||||
"euc-jp": "eucjp",
|
||||
"iso-2022-jp": "iso2022jp",
|
||||
"shift_jis": "shiftJIS",
|
||||
"euc-kr": "euckr",
|
||||
"replacement": "replacement",
|
||||
"utf-16be": "utf16be",
|
||||
"utf-16le": "utf16le",
|
||||
"x-user-defined": "xUserDefined",
|
||||
}
|
||||
|
||||
// locales is taken from
|
||||
// https://html.spec.whatwg.org/multipage/syntax.html#encoding-sniffing-algorithm.
|
||||
var locales = []struct{ tag, name string }{
|
||||
{"und", "windows-1252"}, // The default value.
|
||||
{"ar", "windows-1256"},
|
||||
{"ba", "windows-1251"},
|
||||
{"be", "windows-1251"},
|
||||
{"bg", "windows-1251"},
|
||||
{"cs", "windows-1250"},
|
||||
{"el", "iso-8859-7"},
|
||||
{"et", "windows-1257"},
|
||||
{"fa", "windows-1256"},
|
||||
{"he", "windows-1255"},
|
||||
{"hr", "windows-1250"},
|
||||
{"hu", "iso-8859-2"},
|
||||
{"ja", "shift_jis"},
|
||||
{"kk", "windows-1251"},
|
||||
{"ko", "euc-kr"},
|
||||
{"ku", "windows-1254"},
|
||||
{"ky", "windows-1251"},
|
||||
{"lt", "windows-1257"},
|
||||
{"lv", "windows-1257"},
|
||||
{"mk", "windows-1251"},
|
||||
{"pl", "iso-8859-2"},
|
||||
{"ru", "windows-1251"},
|
||||
{"sah", "windows-1251"},
|
||||
{"sk", "windows-1250"},
|
||||
{"sl", "iso-8859-2"},
|
||||
{"sr", "windows-1251"},
|
||||
{"tg", "windows-1251"},
|
||||
{"th", "windows-874"},
|
||||
{"tr", "windows-1254"},
|
||||
{"tt", "windows-1251"},
|
||||
{"uk", "windows-1251"},
|
||||
{"vi", "windows-1258"},
|
||||
{"zh-hans", "gb18030"},
|
||||
{"zh-hant", "big5"},
|
||||
}
|
||||
86
vendor/golang.org/x/text/encoding/htmlindex/htmlindex.go
generated
vendored
Normal file
86
vendor/golang.org/x/text/encoding/htmlindex/htmlindex.go
generated
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
// 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.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// Package htmlindex maps character set encoding names to Encodings as
|
||||
// recommended by the W3C for use in HTML 5. See http://www.w3.org/TR/encoding.
|
||||
package htmlindex
|
||||
|
||||
// TODO: perhaps have a "bare" version of the index (used by this package) that
|
||||
// is not pre-loaded with all encodings. Global variables in encodings prevent
|
||||
// the linker from being able to purge unneeded tables. This means that
|
||||
// referencing all encodings, as this package does for the default index, links
|
||||
// in all encodings unconditionally.
|
||||
//
|
||||
// This issue can be solved by either solving the linking issue (see
|
||||
// https://github.com/golang/go/issues/6330) or refactoring the encoding tables
|
||||
// (e.g. moving the tables to internal packages that do not use global
|
||||
// variables).
|
||||
|
||||
// TODO: allow canonicalizing names
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidName = errors.New("htmlindex: invalid encoding name")
|
||||
errUnknown = errors.New("htmlindex: unknown Encoding")
|
||||
errUnsupported = errors.New("htmlindex: this encoding is not supported")
|
||||
)
|
||||
|
||||
var (
|
||||
matcherOnce sync.Once
|
||||
matcher language.Matcher
|
||||
)
|
||||
|
||||
// LanguageDefault returns the canonical name of the default encoding for a
|
||||
// given language.
|
||||
func LanguageDefault(tag language.Tag) string {
|
||||
matcherOnce.Do(func() {
|
||||
tags := []language.Tag{}
|
||||
for _, t := range strings.Split(locales, " ") {
|
||||
tags = append(tags, language.MustParse(t))
|
||||
}
|
||||
matcher = language.NewMatcher(tags)
|
||||
})
|
||||
_, i, _ := matcher.Match(tag)
|
||||
return canonical[localeMap[i]] // Default is Windows-1252.
|
||||
}
|
||||
|
||||
// Get returns an Encoding for one of the names listed in
|
||||
// http://www.w3.org/TR/encoding using the Default Index. Matching is case-
|
||||
// insensitive.
|
||||
func Get(name string) (encoding.Encoding, error) {
|
||||
x, ok := nameMap[strings.ToLower(strings.TrimSpace(name))]
|
||||
if !ok {
|
||||
return nil, errInvalidName
|
||||
}
|
||||
return encodings[x], nil
|
||||
}
|
||||
|
||||
// Name reports the canonical name of the given Encoding. It will return
|
||||
// an error if e is not associated with a supported encoding scheme.
|
||||
func Name(e encoding.Encoding) (string, error) {
|
||||
id, ok := e.(identifier.Interface)
|
||||
if !ok {
|
||||
return "", errUnknown
|
||||
}
|
||||
mib, _ := id.ID()
|
||||
if mib == 0 {
|
||||
return "", errUnknown
|
||||
}
|
||||
v, ok := mibMap[mib]
|
||||
if !ok {
|
||||
return "", errUnsupported
|
||||
}
|
||||
return canonical[v], nil
|
||||
}
|
||||
144
vendor/golang.org/x/text/encoding/htmlindex/htmlindex_test.go
generated
vendored
Normal file
144
vendor/golang.org/x/text/encoding/htmlindex/htmlindex_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// 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 htmlindex
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
name string
|
||||
canonical string
|
||||
err error
|
||||
}{
|
||||
{"utf-8", "utf-8", nil},
|
||||
{" utf-8 ", "utf-8", nil},
|
||||
{" l5 ", "windows-1254", nil},
|
||||
{"latin5 ", "windows-1254", nil},
|
||||
{"latin 5", "", errInvalidName},
|
||||
{"latin-5", "", errInvalidName},
|
||||
} {
|
||||
enc, err := Get(tc.name)
|
||||
if err != tc.err {
|
||||
t.Errorf("%d: error was %v; want %v", i, err, tc.err)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if got, err := Name(enc); got != tc.canonical {
|
||||
t.Errorf("%d: Name(Get(%q)) = %q; want %q (%v)", i, tc.name, got, tc.canonical, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTables(t *testing.T) {
|
||||
for name, index := range nameMap {
|
||||
got, err := Get(name)
|
||||
if err != nil {
|
||||
t.Errorf("%s:err: expected non-nil error", name)
|
||||
}
|
||||
if want := encodings[index]; got != want {
|
||||
t.Errorf("%s:encoding: got %v; want %v", name, got, want)
|
||||
}
|
||||
mib, _ := got.(identifier.Interface).ID()
|
||||
if mibMap[mib] != index {
|
||||
t.Errorf("%s:mibMab: got %d; want %d", name, mibMap[mib], index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestName(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
desc string
|
||||
enc encoding.Encoding
|
||||
name string
|
||||
err error
|
||||
}{{
|
||||
"defined encoding",
|
||||
charmap.ISO8859_2,
|
||||
"iso-8859-2",
|
||||
nil,
|
||||
}, {
|
||||
"defined Unicode encoding",
|
||||
unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM),
|
||||
"utf-16be",
|
||||
nil,
|
||||
}, {
|
||||
"undefined Unicode encoding in HTML standard",
|
||||
unicode.UTF16(unicode.BigEndian, unicode.UseBOM),
|
||||
"",
|
||||
errUnsupported,
|
||||
}, {
|
||||
"undefined other encoding in HTML standard",
|
||||
charmap.CodePage437,
|
||||
"",
|
||||
errUnsupported,
|
||||
}, {
|
||||
"unknown encoding",
|
||||
encoding.Nop,
|
||||
"",
|
||||
errUnknown,
|
||||
}} {
|
||||
name, err := Name(tc.enc)
|
||||
if name != tc.name || err != tc.err {
|
||||
t.Errorf("%d:%s: got %q, %v; want %q, %v", i, tc.desc, name, err, tc.name, tc.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLanguageDefault(t *testing.T) {
|
||||
for _, tc := range []struct{ tag, want string }{
|
||||
{"und", "windows-1252"}, // The default value.
|
||||
{"ar", "windows-1256"},
|
||||
{"ba", "windows-1251"},
|
||||
{"be", "windows-1251"},
|
||||
{"bg", "windows-1251"},
|
||||
{"cs", "windows-1250"},
|
||||
{"el", "iso-8859-7"},
|
||||
{"et", "windows-1257"},
|
||||
{"fa", "windows-1256"},
|
||||
{"he", "windows-1255"},
|
||||
{"hr", "windows-1250"},
|
||||
{"hu", "iso-8859-2"},
|
||||
{"ja", "shift_jis"},
|
||||
{"kk", "windows-1251"},
|
||||
{"ko", "euc-kr"},
|
||||
{"ku", "windows-1254"},
|
||||
{"ky", "windows-1251"},
|
||||
{"lt", "windows-1257"},
|
||||
{"lv", "windows-1257"},
|
||||
{"mk", "windows-1251"},
|
||||
{"pl", "iso-8859-2"},
|
||||
{"ru", "windows-1251"},
|
||||
{"sah", "windows-1251"},
|
||||
{"sk", "windows-1250"},
|
||||
{"sl", "iso-8859-2"},
|
||||
{"sr", "windows-1251"},
|
||||
{"tg", "windows-1251"},
|
||||
{"th", "windows-874"},
|
||||
{"tr", "windows-1254"},
|
||||
{"tt", "windows-1251"},
|
||||
{"uk", "windows-1251"},
|
||||
{"vi", "windows-1258"},
|
||||
{"zh-hans", "gb18030"},
|
||||
{"zh-hant", "big5"},
|
||||
// Variants and close approximates of the above.
|
||||
{"ar_EG", "windows-1256"},
|
||||
{"bs", "windows-1250"}, // Bosnian Latin maps to Croatian.
|
||||
// Use default fallback in case of miss.
|
||||
{"nl", "windows-1252"},
|
||||
} {
|
||||
if got := LanguageDefault(language.MustParse(tc.tag)); got != tc.want {
|
||||
t.Errorf("LanguageDefault(%s) = %s; want %s", tc.tag, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
105
vendor/golang.org/x/text/encoding/htmlindex/map.go
generated
vendored
Normal file
105
vendor/golang.org/x/text/encoding/htmlindex/map.go
generated
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// 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 htmlindex
|
||||
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/encoding/korean"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/encoding/traditionalchinese"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
)
|
||||
|
||||
// mibMap maps a MIB identifier to an htmlEncoding index.
|
||||
var mibMap = map[identifier.MIB]htmlEncoding{
|
||||
identifier.UTF8: utf8,
|
||||
identifier.UTF16BE: utf16be,
|
||||
identifier.UTF16LE: utf16le,
|
||||
identifier.IBM866: ibm866,
|
||||
identifier.ISOLatin2: iso8859_2,
|
||||
identifier.ISOLatin3: iso8859_3,
|
||||
identifier.ISOLatin4: iso8859_4,
|
||||
identifier.ISOLatinCyrillic: iso8859_5,
|
||||
identifier.ISOLatinArabic: iso8859_6,
|
||||
identifier.ISOLatinGreek: iso8859_7,
|
||||
identifier.ISOLatinHebrew: iso8859_8,
|
||||
identifier.ISO88598I: iso8859_8I,
|
||||
identifier.ISOLatin6: iso8859_10,
|
||||
identifier.ISO885913: iso8859_13,
|
||||
identifier.ISO885914: iso8859_14,
|
||||
identifier.ISO885915: iso8859_15,
|
||||
identifier.ISO885916: iso8859_16,
|
||||
identifier.KOI8R: koi8r,
|
||||
identifier.KOI8U: koi8u,
|
||||
identifier.Macintosh: macintosh,
|
||||
identifier.MacintoshCyrillic: macintoshCyrillic,
|
||||
identifier.Windows874: windows874,
|
||||
identifier.Windows1250: windows1250,
|
||||
identifier.Windows1251: windows1251,
|
||||
identifier.Windows1252: windows1252,
|
||||
identifier.Windows1253: windows1253,
|
||||
identifier.Windows1254: windows1254,
|
||||
identifier.Windows1255: windows1255,
|
||||
identifier.Windows1256: windows1256,
|
||||
identifier.Windows1257: windows1257,
|
||||
identifier.Windows1258: windows1258,
|
||||
identifier.XUserDefined: xUserDefined,
|
||||
identifier.GBK: gbk,
|
||||
identifier.GB18030: gb18030,
|
||||
identifier.Big5: big5,
|
||||
identifier.EUCPkdFmtJapanese: eucjp,
|
||||
identifier.ISO2022JP: iso2022jp,
|
||||
identifier.ShiftJIS: shiftJIS,
|
||||
identifier.EUCKR: euckr,
|
||||
identifier.Replacement: replacement,
|
||||
}
|
||||
|
||||
// encodings maps the internal htmlEncoding to an Encoding.
|
||||
// TODO: consider using a reusable index in encoding/internal.
|
||||
var encodings = [numEncodings]encoding.Encoding{
|
||||
utf8: unicode.UTF8,
|
||||
ibm866: charmap.CodePage866,
|
||||
iso8859_2: charmap.ISO8859_2,
|
||||
iso8859_3: charmap.ISO8859_3,
|
||||
iso8859_4: charmap.ISO8859_4,
|
||||
iso8859_5: charmap.ISO8859_5,
|
||||
iso8859_6: charmap.ISO8859_6,
|
||||
iso8859_7: charmap.ISO8859_7,
|
||||
iso8859_8: charmap.ISO8859_8,
|
||||
iso8859_8I: charmap.ISO8859_8I,
|
||||
iso8859_10: charmap.ISO8859_10,
|
||||
iso8859_13: charmap.ISO8859_13,
|
||||
iso8859_14: charmap.ISO8859_14,
|
||||
iso8859_15: charmap.ISO8859_15,
|
||||
iso8859_16: charmap.ISO8859_16,
|
||||
koi8r: charmap.KOI8R,
|
||||
koi8u: charmap.KOI8U,
|
||||
macintosh: charmap.Macintosh,
|
||||
windows874: charmap.Windows874,
|
||||
windows1250: charmap.Windows1250,
|
||||
windows1251: charmap.Windows1251,
|
||||
windows1252: charmap.Windows1252,
|
||||
windows1253: charmap.Windows1253,
|
||||
windows1254: charmap.Windows1254,
|
||||
windows1255: charmap.Windows1255,
|
||||
windows1256: charmap.Windows1256,
|
||||
windows1257: charmap.Windows1257,
|
||||
windows1258: charmap.Windows1258,
|
||||
macintoshCyrillic: charmap.MacintoshCyrillic,
|
||||
gbk: simplifiedchinese.GBK,
|
||||
gb18030: simplifiedchinese.GB18030,
|
||||
big5: traditionalchinese.Big5,
|
||||
eucjp: japanese.EUCJP,
|
||||
iso2022jp: japanese.ISO2022JP,
|
||||
shiftJIS: japanese.ShiftJIS,
|
||||
euckr: korean.EUCKR,
|
||||
replacement: encoding.Replacement,
|
||||
utf16be: unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM),
|
||||
utf16le: unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM),
|
||||
xUserDefined: charmap.XUserDefined,
|
||||
}
|
||||
352
vendor/golang.org/x/text/encoding/htmlindex/tables.go
generated
vendored
Normal file
352
vendor/golang.org/x/text/encoding/htmlindex/tables.go
generated
vendored
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
// This file was generated by go generate; DO NOT EDIT
|
||||
|
||||
package htmlindex
|
||||
|
||||
type htmlEncoding byte
|
||||
|
||||
const (
|
||||
utf8 htmlEncoding = iota
|
||||
ibm866
|
||||
iso8859_2
|
||||
iso8859_3
|
||||
iso8859_4
|
||||
iso8859_5
|
||||
iso8859_6
|
||||
iso8859_7
|
||||
iso8859_8
|
||||
iso8859_8I
|
||||
iso8859_10
|
||||
iso8859_13
|
||||
iso8859_14
|
||||
iso8859_15
|
||||
iso8859_16
|
||||
koi8r
|
||||
koi8u
|
||||
macintosh
|
||||
windows874
|
||||
windows1250
|
||||
windows1251
|
||||
windows1252
|
||||
windows1253
|
||||
windows1254
|
||||
windows1255
|
||||
windows1256
|
||||
windows1257
|
||||
windows1258
|
||||
macintoshCyrillic
|
||||
gbk
|
||||
gb18030
|
||||
big5
|
||||
eucjp
|
||||
iso2022jp
|
||||
shiftJIS
|
||||
euckr
|
||||
replacement
|
||||
utf16be
|
||||
utf16le
|
||||
xUserDefined
|
||||
numEncodings
|
||||
)
|
||||
|
||||
var canonical = [numEncodings]string{
|
||||
"utf-8",
|
||||
"ibm866",
|
||||
"iso-8859-2",
|
||||
"iso-8859-3",
|
||||
"iso-8859-4",
|
||||
"iso-8859-5",
|
||||
"iso-8859-6",
|
||||
"iso-8859-7",
|
||||
"iso-8859-8",
|
||||
"iso-8859-8-i",
|
||||
"iso-8859-10",
|
||||
"iso-8859-13",
|
||||
"iso-8859-14",
|
||||
"iso-8859-15",
|
||||
"iso-8859-16",
|
||||
"koi8-r",
|
||||
"koi8-u",
|
||||
"macintosh",
|
||||
"windows-874",
|
||||
"windows-1250",
|
||||
"windows-1251",
|
||||
"windows-1252",
|
||||
"windows-1253",
|
||||
"windows-1254",
|
||||
"windows-1255",
|
||||
"windows-1256",
|
||||
"windows-1257",
|
||||
"windows-1258",
|
||||
"x-mac-cyrillic",
|
||||
"gbk",
|
||||
"gb18030",
|
||||
"big5",
|
||||
"euc-jp",
|
||||
"iso-2022-jp",
|
||||
"shift_jis",
|
||||
"euc-kr",
|
||||
"replacement",
|
||||
"utf-16be",
|
||||
"utf-16le",
|
||||
"x-user-defined",
|
||||
}
|
||||
|
||||
var nameMap = map[string]htmlEncoding{
|
||||
"unicode-1-1-utf-8": utf8,
|
||||
"utf-8": utf8,
|
||||
"utf8": utf8,
|
||||
"866": ibm866,
|
||||
"cp866": ibm866,
|
||||
"csibm866": ibm866,
|
||||
"ibm866": ibm866,
|
||||
"csisolatin2": iso8859_2,
|
||||
"iso-8859-2": iso8859_2,
|
||||
"iso-ir-101": iso8859_2,
|
||||
"iso8859-2": iso8859_2,
|
||||
"iso88592": iso8859_2,
|
||||
"iso_8859-2": iso8859_2,
|
||||
"iso_8859-2:1987": iso8859_2,
|
||||
"l2": iso8859_2,
|
||||
"latin2": iso8859_2,
|
||||
"csisolatin3": iso8859_3,
|
||||
"iso-8859-3": iso8859_3,
|
||||
"iso-ir-109": iso8859_3,
|
||||
"iso8859-3": iso8859_3,
|
||||
"iso88593": iso8859_3,
|
||||
"iso_8859-3": iso8859_3,
|
||||
"iso_8859-3:1988": iso8859_3,
|
||||
"l3": iso8859_3,
|
||||
"latin3": iso8859_3,
|
||||
"csisolatin4": iso8859_4,
|
||||
"iso-8859-4": iso8859_4,
|
||||
"iso-ir-110": iso8859_4,
|
||||
"iso8859-4": iso8859_4,
|
||||
"iso88594": iso8859_4,
|
||||
"iso_8859-4": iso8859_4,
|
||||
"iso_8859-4:1988": iso8859_4,
|
||||
"l4": iso8859_4,
|
||||
"latin4": iso8859_4,
|
||||
"csisolatincyrillic": iso8859_5,
|
||||
"cyrillic": iso8859_5,
|
||||
"iso-8859-5": iso8859_5,
|
||||
"iso-ir-144": iso8859_5,
|
||||
"iso8859-5": iso8859_5,
|
||||
"iso88595": iso8859_5,
|
||||
"iso_8859-5": iso8859_5,
|
||||
"iso_8859-5:1988": iso8859_5,
|
||||
"arabic": iso8859_6,
|
||||
"asmo-708": iso8859_6,
|
||||
"csiso88596e": iso8859_6,
|
||||
"csiso88596i": iso8859_6,
|
||||
"csisolatinarabic": iso8859_6,
|
||||
"ecma-114": iso8859_6,
|
||||
"iso-8859-6": iso8859_6,
|
||||
"iso-8859-6-e": iso8859_6,
|
||||
"iso-8859-6-i": iso8859_6,
|
||||
"iso-ir-127": iso8859_6,
|
||||
"iso8859-6": iso8859_6,
|
||||
"iso88596": iso8859_6,
|
||||
"iso_8859-6": iso8859_6,
|
||||
"iso_8859-6:1987": iso8859_6,
|
||||
"csisolatingreek": iso8859_7,
|
||||
"ecma-118": iso8859_7,
|
||||
"elot_928": iso8859_7,
|
||||
"greek": iso8859_7,
|
||||
"greek8": iso8859_7,
|
||||
"iso-8859-7": iso8859_7,
|
||||
"iso-ir-126": iso8859_7,
|
||||
"iso8859-7": iso8859_7,
|
||||
"iso88597": iso8859_7,
|
||||
"iso_8859-7": iso8859_7,
|
||||
"iso_8859-7:1987": iso8859_7,
|
||||
"sun_eu_greek": iso8859_7,
|
||||
"csiso88598e": iso8859_8,
|
||||
"csisolatinhebrew": iso8859_8,
|
||||
"hebrew": iso8859_8,
|
||||
"iso-8859-8": iso8859_8,
|
||||
"iso-8859-8-e": iso8859_8,
|
||||
"iso-ir-138": iso8859_8,
|
||||
"iso8859-8": iso8859_8,
|
||||
"iso88598": iso8859_8,
|
||||
"iso_8859-8": iso8859_8,
|
||||
"iso_8859-8:1988": iso8859_8,
|
||||
"visual": iso8859_8,
|
||||
"csiso88598i": iso8859_8I,
|
||||
"iso-8859-8-i": iso8859_8I,
|
||||
"logical": iso8859_8I,
|
||||
"csisolatin6": iso8859_10,
|
||||
"iso-8859-10": iso8859_10,
|
||||
"iso-ir-157": iso8859_10,
|
||||
"iso8859-10": iso8859_10,
|
||||
"iso885910": iso8859_10,
|
||||
"l6": iso8859_10,
|
||||
"latin6": iso8859_10,
|
||||
"iso-8859-13": iso8859_13,
|
||||
"iso8859-13": iso8859_13,
|
||||
"iso885913": iso8859_13,
|
||||
"iso-8859-14": iso8859_14,
|
||||
"iso8859-14": iso8859_14,
|
||||
"iso885914": iso8859_14,
|
||||
"csisolatin9": iso8859_15,
|
||||
"iso-8859-15": iso8859_15,
|
||||
"iso8859-15": iso8859_15,
|
||||
"iso885915": iso8859_15,
|
||||
"iso_8859-15": iso8859_15,
|
||||
"l9": iso8859_15,
|
||||
"iso-8859-16": iso8859_16,
|
||||
"cskoi8r": koi8r,
|
||||
"koi": koi8r,
|
||||
"koi8": koi8r,
|
||||
"koi8-r": koi8r,
|
||||
"koi8_r": koi8r,
|
||||
"koi8-ru": koi8u,
|
||||
"koi8-u": koi8u,
|
||||
"csmacintosh": macintosh,
|
||||
"mac": macintosh,
|
||||
"macintosh": macintosh,
|
||||
"x-mac-roman": macintosh,
|
||||
"dos-874": windows874,
|
||||
"iso-8859-11": windows874,
|
||||
"iso8859-11": windows874,
|
||||
"iso885911": windows874,
|
||||
"tis-620": windows874,
|
||||
"windows-874": windows874,
|
||||
"cp1250": windows1250,
|
||||
"windows-1250": windows1250,
|
||||
"x-cp1250": windows1250,
|
||||
"cp1251": windows1251,
|
||||
"windows-1251": windows1251,
|
||||
"x-cp1251": windows1251,
|
||||
"ansi_x3.4-1968": windows1252,
|
||||
"ascii": windows1252,
|
||||
"cp1252": windows1252,
|
||||
"cp819": windows1252,
|
||||
"csisolatin1": windows1252,
|
||||
"ibm819": windows1252,
|
||||
"iso-8859-1": windows1252,
|
||||
"iso-ir-100": windows1252,
|
||||
"iso8859-1": windows1252,
|
||||
"iso88591": windows1252,
|
||||
"iso_8859-1": windows1252,
|
||||
"iso_8859-1:1987": windows1252,
|
||||
"l1": windows1252,
|
||||
"latin1": windows1252,
|
||||
"us-ascii": windows1252,
|
||||
"windows-1252": windows1252,
|
||||
"x-cp1252": windows1252,
|
||||
"cp1253": windows1253,
|
||||
"windows-1253": windows1253,
|
||||
"x-cp1253": windows1253,
|
||||
"cp1254": windows1254,
|
||||
"csisolatin5": windows1254,
|
||||
"iso-8859-9": windows1254,
|
||||
"iso-ir-148": windows1254,
|
||||
"iso8859-9": windows1254,
|
||||
"iso88599": windows1254,
|
||||
"iso_8859-9": windows1254,
|
||||
"iso_8859-9:1989": windows1254,
|
||||
"l5": windows1254,
|
||||
"latin5": windows1254,
|
||||
"windows-1254": windows1254,
|
||||
"x-cp1254": windows1254,
|
||||
"cp1255": windows1255,
|
||||
"windows-1255": windows1255,
|
||||
"x-cp1255": windows1255,
|
||||
"cp1256": windows1256,
|
||||
"windows-1256": windows1256,
|
||||
"x-cp1256": windows1256,
|
||||
"cp1257": windows1257,
|
||||
"windows-1257": windows1257,
|
||||
"x-cp1257": windows1257,
|
||||
"cp1258": windows1258,
|
||||
"windows-1258": windows1258,
|
||||
"x-cp1258": windows1258,
|
||||
"x-mac-cyrillic": macintoshCyrillic,
|
||||
"x-mac-ukrainian": macintoshCyrillic,
|
||||
"chinese": gbk,
|
||||
"csgb2312": gbk,
|
||||
"csiso58gb231280": gbk,
|
||||
"gb2312": gbk,
|
||||
"gb_2312": gbk,
|
||||
"gb_2312-80": gbk,
|
||||
"gbk": gbk,
|
||||
"iso-ir-58": gbk,
|
||||
"x-gbk": gbk,
|
||||
"gb18030": gb18030,
|
||||
"big5": big5,
|
||||
"big5-hkscs": big5,
|
||||
"cn-big5": big5,
|
||||
"csbig5": big5,
|
||||
"x-x-big5": big5,
|
||||
"cseucpkdfmtjapanese": eucjp,
|
||||
"euc-jp": eucjp,
|
||||
"x-euc-jp": eucjp,
|
||||
"csiso2022jp": iso2022jp,
|
||||
"iso-2022-jp": iso2022jp,
|
||||
"csshiftjis": shiftJIS,
|
||||
"ms932": shiftJIS,
|
||||
"ms_kanji": shiftJIS,
|
||||
"shift-jis": shiftJIS,
|
||||
"shift_jis": shiftJIS,
|
||||
"sjis": shiftJIS,
|
||||
"windows-31j": shiftJIS,
|
||||
"x-sjis": shiftJIS,
|
||||
"cseuckr": euckr,
|
||||
"csksc56011987": euckr,
|
||||
"euc-kr": euckr,
|
||||
"iso-ir-149": euckr,
|
||||
"korean": euckr,
|
||||
"ks_c_5601-1987": euckr,
|
||||
"ks_c_5601-1989": euckr,
|
||||
"ksc5601": euckr,
|
||||
"ksc_5601": euckr,
|
||||
"windows-949": euckr,
|
||||
"csiso2022kr": replacement,
|
||||
"hz-gb-2312": replacement,
|
||||
"iso-2022-cn": replacement,
|
||||
"iso-2022-cn-ext": replacement,
|
||||
"iso-2022-kr": replacement,
|
||||
"utf-16be": utf16be,
|
||||
"utf-16": utf16le,
|
||||
"utf-16le": utf16le,
|
||||
"x-user-defined": xUserDefined,
|
||||
}
|
||||
|
||||
var localeMap = []htmlEncoding{
|
||||
windows1252, // und
|
||||
windows1256, // ar
|
||||
windows1251, // ba
|
||||
windows1251, // be
|
||||
windows1251, // bg
|
||||
windows1250, // cs
|
||||
iso8859_7, // el
|
||||
windows1257, // et
|
||||
windows1256, // fa
|
||||
windows1255, // he
|
||||
windows1250, // hr
|
||||
iso8859_2, // hu
|
||||
shiftJIS, // ja
|
||||
windows1251, // kk
|
||||
euckr, // ko
|
||||
windows1254, // ku
|
||||
windows1251, // ky
|
||||
windows1257, // lt
|
||||
windows1257, // lv
|
||||
windows1251, // mk
|
||||
iso8859_2, // pl
|
||||
windows1251, // ru
|
||||
windows1251, // sah
|
||||
windows1250, // sk
|
||||
iso8859_2, // sl
|
||||
windows1251, // sr
|
||||
windows1251, // tg
|
||||
windows874, // th
|
||||
windows1254, // tr
|
||||
windows1251, // tt
|
||||
windows1251, // uk
|
||||
windows1258, // vi
|
||||
gb18030, // zh-hans
|
||||
big5, // zh-hant
|
||||
}
|
||||
|
||||
const locales = "und ar ba be bg cs el et fa he hr hu ja kk ko ku ky lt lv mk pl ru sah sk sl sr tg th tr tt uk vi zh-hans zh-hant"
|
||||
26
vendor/golang.org/x/text/encoding/ianaindex/example_test.go
generated
vendored
Normal file
26
vendor/golang.org/x/text/encoding/ianaindex/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// 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 ianaindex_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/ianaindex"
|
||||
)
|
||||
|
||||
func ExampleIndex() {
|
||||
fmt.Println(ianaindex.MIME.Name(charmap.ISO8859_7))
|
||||
|
||||
fmt.Println(ianaindex.IANA.Name(charmap.ISO8859_7))
|
||||
|
||||
e, _ := ianaindex.IANA.Get("cp437")
|
||||
fmt.Println(ianaindex.IANA.Name(e))
|
||||
|
||||
// TODO: Output:
|
||||
// ISO-8859-7
|
||||
// ISO8859_7:1987
|
||||
// IBM437
|
||||
}
|
||||
64
vendor/golang.org/x/text/encoding/ianaindex/ianaindex.go
generated
vendored
Normal file
64
vendor/golang.org/x/text/encoding/ianaindex/ianaindex.go
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// 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 ianaindex maps names to Encodings as specified by the IANA registry.
|
||||
// This includes both the MIME and IANA names.
|
||||
//
|
||||
// See http://www.iana.org/assignments/character-sets/character-sets.xhtml for
|
||||
// more details.
|
||||
package ianaindex
|
||||
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
)
|
||||
|
||||
// TODO: allow users to specify their own aliases?
|
||||
// TODO: allow users to specify their own indexes?
|
||||
// TODO: allow canonicalizing names
|
||||
|
||||
// NOTE: only use these top-level variables if we can get the linker to drop
|
||||
// the indexes when they are not used. Make them a function or perhaps only
|
||||
// support MIME otherwise.
|
||||
|
||||
var (
|
||||
// MIME is an index to map MIME names. It does not support aliases.
|
||||
MIME *Index
|
||||
|
||||
// IANA is an index that supports all names and aliases using IANA names as
|
||||
// the canonical identifier.
|
||||
IANA *Index
|
||||
)
|
||||
|
||||
// Index maps names registered by IANA to Encodings.
|
||||
type Index struct {
|
||||
}
|
||||
|
||||
// Get returns an Encoding for IANA-registered names. Matching is
|
||||
// case-insensitive.
|
||||
func (x *Index) Get(name string) (encoding.Encoding, error) {
|
||||
panic("TODO: implement")
|
||||
}
|
||||
|
||||
// Name reports the canonical name of the given Encoding. It will return an
|
||||
// error if the e is not associated with a known encoding scheme.
|
||||
func (x *Index) Name(e encoding.Encoding) (string, error) {
|
||||
panic("TODO: implement")
|
||||
}
|
||||
|
||||
// TODO: the coverage of this index is rather spotty. Allowing users to set
|
||||
// encodings would allow:
|
||||
// - users to increase coverage
|
||||
// - allow a partially loaded set of encodings in case the user doesn't need to
|
||||
// them all.
|
||||
// - write an OS-specific wrapper for supported encodings and set them.
|
||||
// The exact definition of Set depends a bit on if and how we want to let users
|
||||
// write their own Encoding implementations. Also, it is not possible yet to
|
||||
// only partially load the encodings without doing some refactoring. Until this
|
||||
// is solved, we might as well not support Set.
|
||||
// // Set sets the e to be used for the encoding scheme identified by name. Only
|
||||
// // canonical names may be used. An empty name assigns e to its internally
|
||||
// // associated encoding scheme.
|
||||
// func (x *Index) Set(name string, e encoding.Encoding) error {
|
||||
// panic("TODO: implement")
|
||||
// }
|
||||
137
vendor/golang.org/x/text/encoding/internal/identifier/gen.go
generated
vendored
Normal file
137
vendor/golang.org/x/text/encoding/internal/identifier/gen.go
generated
vendored
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/internal/gen"
|
||||
)
|
||||
|
||||
type registry struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Updated string `xml:"updated"`
|
||||
Registry []struct {
|
||||
ID string `xml:"id,attr"`
|
||||
Record []struct {
|
||||
Name string `xml:"name"`
|
||||
Xref []struct {
|
||||
Type string `xml:"type,attr"`
|
||||
Data string `xml:"data,attr"`
|
||||
} `xml:"xref"`
|
||||
Desc struct {
|
||||
Data string `xml:",innerxml"`
|
||||
// Any []struct {
|
||||
// Data string `xml:",chardata"`
|
||||
// } `xml:",any"`
|
||||
// Data string `xml:",chardata"`
|
||||
} `xml:"description,"`
|
||||
MIB string `xml:"value"`
|
||||
Alias []string `xml:"alias"`
|
||||
MIME string `xml:"preferred_alias"`
|
||||
} `xml:"record"`
|
||||
} `xml:"registry"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gen.OpenIANAFile("assignments/character-sets/character-sets.xml")
|
||||
reg := ®istry{}
|
||||
if err := xml.NewDecoder(r).Decode(®); err != nil && err != io.EOF {
|
||||
log.Fatalf("Error decoding charset registry: %v", err)
|
||||
}
|
||||
if len(reg.Registry) == 0 || reg.Registry[0].ID != "character-sets-1" {
|
||||
log.Fatalf("Unexpected ID %s", reg.Registry[0].ID)
|
||||
}
|
||||
|
||||
w := &bytes.Buffer{}
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, rec := range reg.Registry[0].Record {
|
||||
constName := ""
|
||||
for _, a := range rec.Alias {
|
||||
if strings.HasPrefix(a, "cs") && strings.IndexByte(a, '-') == -1 {
|
||||
// Some of the constant definitions have comments in them. Strip those.
|
||||
constName = strings.Title(strings.SplitN(a[2:], "\n", 2)[0])
|
||||
}
|
||||
}
|
||||
if constName == "" {
|
||||
switch rec.MIB {
|
||||
case "2085":
|
||||
constName = "HZGB2312" // Not listed as alias for some reason.
|
||||
default:
|
||||
log.Fatalf("No cs alias defined for %s.", rec.MIB)
|
||||
}
|
||||
}
|
||||
if rec.MIME != "" {
|
||||
rec.MIME = fmt.Sprintf(" (MIME: %s)", rec.MIME)
|
||||
}
|
||||
fmt.Fprintf(w, "// %s is the MIB identifier with IANA name %s%s.\n//\n", constName, rec.Name, rec.MIME)
|
||||
if len(rec.Desc.Data) > 0 {
|
||||
fmt.Fprint(w, "// ")
|
||||
d := xml.NewDecoder(strings.NewReader(rec.Desc.Data))
|
||||
inElem := true
|
||||
attr := ""
|
||||
for {
|
||||
t, err := d.Token()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Fatal(err)
|
||||
}
|
||||
break
|
||||
}
|
||||
switch x := t.(type) {
|
||||
case xml.CharData:
|
||||
attr = "" // Don't need attribute info.
|
||||
a := bytes.Split([]byte(x), []byte("\n"))
|
||||
for i, b := range a {
|
||||
if b = bytes.TrimSpace(b); len(b) != 0 {
|
||||
if !inElem && i > 0 {
|
||||
fmt.Fprint(w, "\n// ")
|
||||
}
|
||||
inElem = false
|
||||
fmt.Fprintf(w, "%s ", string(b))
|
||||
}
|
||||
}
|
||||
case xml.StartElement:
|
||||
if x.Name.Local == "xref" {
|
||||
inElem = true
|
||||
use := false
|
||||
for _, a := range x.Attr {
|
||||
if a.Name.Local == "type" {
|
||||
use = use || a.Value != "person"
|
||||
}
|
||||
if a.Name.Local == "data" && use {
|
||||
attr = a.Value + " "
|
||||
}
|
||||
}
|
||||
}
|
||||
case xml.EndElement:
|
||||
inElem = false
|
||||
fmt.Fprint(w, attr)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
}
|
||||
for _, x := range rec.Xref {
|
||||
switch x.Type {
|
||||
case "rfc":
|
||||
fmt.Fprintf(w, "// Reference: %s\n", strings.ToUpper(x.Data))
|
||||
case "uri":
|
||||
fmt.Fprintf(w, "// Reference: %s\n", x.Data)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "%s MIB = %s\n", constName, rec.MIB)
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
fmt.Fprintln(w, ")")
|
||||
|
||||
gen.WriteGoFile("mib.go", "identifier", w.Bytes())
|
||||
}
|
||||
81
vendor/golang.org/x/text/encoding/internal/identifier/identifier.go
generated
vendored
Normal file
81
vendor/golang.org/x/text/encoding/internal/identifier/identifier.go
generated
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// 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.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// Package identifier defines the contract between implementations of Encoding
|
||||
// and Index by defining identifiers that uniquely identify standardized coded
|
||||
// character sets (CCS) and character encoding schemes (CES), which we will
|
||||
// together refer to as encodings, for which Encoding implementations provide
|
||||
// converters to and from UTF-8. This package is typically only of concern to
|
||||
// implementers of Indexes and Encodings.
|
||||
//
|
||||
// One part of the identifier is the MIB code, which is defined by IANA and
|
||||
// uniquely identifies a CCS or CES. Each code is associated with data that
|
||||
// references authorities, official documentation as well as aliases and MIME
|
||||
// names.
|
||||
//
|
||||
// Not all CESs are covered by the IANA registry. The "other" string that is
|
||||
// returned by ID can be used to identify other character sets or versions of
|
||||
// existing ones.
|
||||
//
|
||||
// It is recommended that each package that provides a set of Encodings provide
|
||||
// the All and Common variables to reference all supported encodings and
|
||||
// commonly used subset. This allows Index implementations to include all
|
||||
// available encodings without explicitly referencing or knowing about them.
|
||||
package identifier
|
||||
|
||||
// Note: this package is internal, but could be made public if there is a need
|
||||
// for writing third-party Indexes and Encodings.
|
||||
|
||||
// References:
|
||||
// - http://source.icu-project.org/repos/icu/icu/trunk/source/data/mappings/convrtrs.txt
|
||||
// - http://www.iana.org/assignments/character-sets/character-sets.xhtml
|
||||
// - http://www.iana.org/assignments/ianacharset-mib/ianacharset-mib
|
||||
// - http://www.ietf.org/rfc/rfc2978.txt
|
||||
// - http://www.unicode.org/reports/tr22/
|
||||
// - http://www.w3.org/TR/encoding/
|
||||
// - http://www.w3.org/TR/encoding/indexes/encodings.json
|
||||
// - https://encoding.spec.whatwg.org/
|
||||
// - https://tools.ietf.org/html/rfc6657#section-5
|
||||
|
||||
// Interface can be implemented by Encodings to define the CCS or CES for which
|
||||
// it implements conversions.
|
||||
type Interface interface {
|
||||
// ID returns an encoding identifier. Exactly one of the mib and other
|
||||
// values should be non-zero.
|
||||
//
|
||||
// In the usual case it is only necessary to indicate the MIB code. The
|
||||
// other string can be used to specify encodings for which there is no MIB,
|
||||
// such as "x-mac-dingbat".
|
||||
//
|
||||
// The other string may only contain the characters a-z, A-Z, 0-9, - and _.
|
||||
ID() (mib MIB, other string)
|
||||
|
||||
// NOTE: the restrictions on the encoding are to allow extending the syntax
|
||||
// with additional information such as versions, vendors and other variants.
|
||||
}
|
||||
|
||||
// A MIB identifies an encoding. It is derived from the IANA MIB codes and adds
|
||||
// some identifiers for some encodings that are not covered by the IANA
|
||||
// standard.
|
||||
//
|
||||
// See http://www.iana.org/assignments/ianacharset-mib.
|
||||
type MIB uint16
|
||||
|
||||
// These additional MIB types are not defined in IANA. They are added because
|
||||
// they are common and defined within the text repo.
|
||||
const (
|
||||
// Unofficial marks the start of encodings not registered by IANA.
|
||||
Unofficial MIB = 10000 + iota
|
||||
|
||||
// Replacement is the WhatWG replacement encoding.
|
||||
Replacement
|
||||
|
||||
// XUserDefined is the code for x-user-defined.
|
||||
XUserDefined
|
||||
|
||||
// MacintoshCyrillic is the code for x-mac-cyrillic.
|
||||
MacintoshCyrillic
|
||||
)
|
||||
1621
vendor/golang.org/x/text/encoding/internal/identifier/mib.go
generated
vendored
Normal file
1621
vendor/golang.org/x/text/encoding/internal/identifier/mib.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
75
vendor/golang.org/x/text/encoding/internal/internal.go
generated
vendored
Normal file
75
vendor/golang.org/x/text/encoding/internal/internal.go
generated
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// 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 internal contains code that is shared among encoding implementations.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// Encoding is an implementation of the Encoding interface that adds the String
|
||||
// and ID methods to an existing encoding.
|
||||
type Encoding struct {
|
||||
encoding.Encoding
|
||||
Name string
|
||||
MIB identifier.MIB
|
||||
}
|
||||
|
||||
// _ verifies that Encoding implements identifier.Interface.
|
||||
var _ identifier.Interface = (*Encoding)(nil)
|
||||
|
||||
func (e *Encoding) String() string {
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *Encoding) ID() (mib identifier.MIB, other string) {
|
||||
return e.MIB, ""
|
||||
}
|
||||
|
||||
// SimpleEncoding is an Encoding that combines two Transformers.
|
||||
type SimpleEncoding struct {
|
||||
Decoder transform.Transformer
|
||||
Encoder transform.Transformer
|
||||
}
|
||||
|
||||
func (e *SimpleEncoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: e.Decoder}
|
||||
}
|
||||
|
||||
func (e *SimpleEncoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: e.Encoder}
|
||||
}
|
||||
|
||||
// FuncEncoding is an Encoding that combines two functions returning a new
|
||||
// Transformer.
|
||||
type FuncEncoding struct {
|
||||
Decoder func() transform.Transformer
|
||||
Encoder func() transform.Transformer
|
||||
}
|
||||
|
||||
func (e FuncEncoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: e.Decoder()}
|
||||
}
|
||||
|
||||
func (e FuncEncoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: e.Encoder()}
|
||||
}
|
||||
|
||||
// A RepertoireError indicates a rune is not in the repertoire of a destination
|
||||
// encoding. It is associated with an encoding-specific suggested replacement
|
||||
// byte.
|
||||
type RepertoireError byte
|
||||
|
||||
// Error implements the error interrface.
|
||||
func (r RepertoireError) Error() string {
|
||||
return "encoding: rune not supported by encoding."
|
||||
}
|
||||
|
||||
// Replacement returns the replacement string associated with this error.
|
||||
func (r RepertoireError) Replacement() byte { return byte(r) }
|
||||
|
||||
var ErrASCIIReplacement = RepertoireError(encoding.ASCIISub)
|
||||
12
vendor/golang.org/x/text/encoding/japanese/all.go
generated
vendored
Normal file
12
vendor/golang.org/x/text/encoding/japanese/all.go
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// 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 japanese
|
||||
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
)
|
||||
|
||||
// All is a list of all defined encodings in this package.
|
||||
var All = []encoding.Encoding{EUCJP, ISO2022JP, ShiftJIS}
|
||||
80
vendor/golang.org/x/text/encoding/japanese/all_test.go
generated
vendored
Normal file
80
vendor/golang.org/x/text/encoding/japanese/all_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// 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 japanese
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func dec(e encoding.Encoding) (dir string, t transform.Transformer, err error) {
|
||||
return "Decode", e.NewDecoder(), nil
|
||||
}
|
||||
func enc(e encoding.Encoding) (dir string, t transform.Transformer, err error) {
|
||||
return "Encode", e.NewEncoder(), internal.ErrASCIIReplacement
|
||||
}
|
||||
|
||||
func TestNonRepertoire(t *testing.T) {
|
||||
testCases := []struct {
|
||||
init func(e encoding.Encoding) (string, transform.Transformer, error)
|
||||
e encoding.Encoding
|
||||
src, want string
|
||||
}{
|
||||
{dec, EUCJP, "\xfe\xfc", "\ufffd"},
|
||||
{dec, ISO2022JP, "\x1b$B\x7e\x7e", "\ufffd"},
|
||||
{dec, ShiftJIS, "\xef\xfc", "\ufffd"},
|
||||
|
||||
{enc, EUCJP, "갂", ""},
|
||||
{enc, EUCJP, "a갂", "a"},
|
||||
{enc, EUCJP, "丌갂", "\x8f\xb0\xa4"},
|
||||
|
||||
{enc, ISO2022JP, "갂", ""},
|
||||
{enc, ISO2022JP, "a갂", "a"},
|
||||
{enc, ISO2022JP, "朗갂", "\x1b$BzF\x1b(B"}, // switch back to ASCII mode at end
|
||||
|
||||
{enc, ShiftJIS, "갂", ""},
|
||||
{enc, ShiftJIS, "a갂", "a"},
|
||||
{enc, ShiftJIS, "\u2190갂", "\x81\xa9"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
dir, tr, wantErr := tc.init(tc.e)
|
||||
|
||||
dst, _, err := transform.String(tr, tc.src)
|
||||
if err != wantErr {
|
||||
t.Errorf("%s %v(%q): got %v; want %v", dir, tc.e, tc.src, err, wantErr)
|
||||
}
|
||||
if got := string(dst); got != tc.want {
|
||||
t.Errorf("%s %v(%q):\ngot %q\nwant %q", dir, tc.e, tc.src, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorrect(t *testing.T) {
|
||||
testCases := []struct {
|
||||
init func(e encoding.Encoding) (string, transform.Transformer, error)
|
||||
e encoding.Encoding
|
||||
src, want string
|
||||
}{
|
||||
{dec, ShiftJIS, "\x9f\xfc", "滌"},
|
||||
{dec, ShiftJIS, "\xfb\xfc", "髙"},
|
||||
{dec, ShiftJIS, "\xfa\xb1", "﨑"},
|
||||
{enc, ShiftJIS, "滌", "\x9f\xfc"},
|
||||
{enc, ShiftJIS, "﨑", "\xed\x95"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
dir, tr, _ := tc.init(tc.e)
|
||||
|
||||
dst, _, err := transform.String(tr, tc.src)
|
||||
if err != nil {
|
||||
t.Errorf("%s %v(%q): got %v; want %v", dir, tc.e, tc.src, err, nil)
|
||||
}
|
||||
if got := string(dst); got != tc.want {
|
||||
t.Errorf("%s %v(%q):\ngot %q\nwant %q", dir, tc.e, tc.src, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
211
vendor/golang.org/x/text/encoding/japanese/eucjp.go
generated
vendored
Normal file
211
vendor/golang.org/x/text/encoding/japanese/eucjp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
// 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 japanese
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// EUCJP is the EUC-JP encoding.
|
||||
var EUCJP encoding.Encoding = &eucJP
|
||||
|
||||
var eucJP = internal.Encoding{
|
||||
&internal.SimpleEncoding{eucJPDecoder{}, eucJPEncoder{}},
|
||||
"EUC-JP",
|
||||
identifier.EUCPkdFmtJapanese,
|
||||
}
|
||||
|
||||
var errInvalidEUCJP = errors.New("japanese: invalid EUC-JP encoding")
|
||||
|
||||
type eucJPDecoder struct{ transform.NopResetter }
|
||||
|
||||
func (eucJPDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
loop:
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
switch c0 := src[nSrc]; {
|
||||
case c0 < utf8.RuneSelf:
|
||||
r, size = rune(c0), 1
|
||||
|
||||
case c0 == 0x8e:
|
||||
if nSrc+1 >= len(src) {
|
||||
err = transform.ErrShortSrc
|
||||
break loop
|
||||
}
|
||||
c1 := src[nSrc+1]
|
||||
if c1 < 0xa1 || 0xdf < c1 {
|
||||
err = errInvalidEUCJP
|
||||
break loop
|
||||
}
|
||||
r, size = rune(c1)+(0xff61-0xa1), 2
|
||||
|
||||
case c0 == 0x8f:
|
||||
if nSrc+2 >= len(src) {
|
||||
err = transform.ErrShortSrc
|
||||
break loop
|
||||
}
|
||||
c1 := src[nSrc+1]
|
||||
if c1 < 0xa1 || 0xfe < c1 {
|
||||
err = errInvalidEUCJP
|
||||
break loop
|
||||
}
|
||||
c2 := src[nSrc+2]
|
||||
if c2 < 0xa1 || 0xfe < c2 {
|
||||
err = errInvalidEUCJP
|
||||
break loop
|
||||
}
|
||||
r, size = '\ufffd', 3
|
||||
if i := int(c1-0xa1)*94 + int(c2-0xa1); i < len(jis0212Decode) {
|
||||
r = rune(jis0212Decode[i])
|
||||
if r == 0 {
|
||||
r = '\ufffd'
|
||||
}
|
||||
}
|
||||
|
||||
case 0xa1 <= c0 && c0 <= 0xfe:
|
||||
if nSrc+1 >= len(src) {
|
||||
err = transform.ErrShortSrc
|
||||
break loop
|
||||
}
|
||||
c1 := src[nSrc+1]
|
||||
if c1 < 0xa1 || 0xfe < c1 {
|
||||
err = errInvalidEUCJP
|
||||
break loop
|
||||
}
|
||||
r, size = '\ufffd', 2
|
||||
if i := int(c0-0xa1)*94 + int(c1-0xa1); i < len(jis0208Decode) {
|
||||
r = rune(jis0208Decode[i])
|
||||
if r == 0 {
|
||||
r = '\ufffd'
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
err = errInvalidEUCJP
|
||||
break loop
|
||||
}
|
||||
|
||||
if nDst+utf8.RuneLen(r) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break loop
|
||||
}
|
||||
nDst += utf8.EncodeRune(dst[nDst:], r)
|
||||
}
|
||||
if atEOF && err == transform.ErrShortSrc {
|
||||
err = errInvalidEUCJP
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
type eucJPEncoder struct{ transform.NopResetter }
|
||||
|
||||
func (eucJPEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
r = rune(src[nSrc])
|
||||
|
||||
// Decode a 1-byte rune.
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
|
||||
} else {
|
||||
// Decode a multi-byte rune.
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// func init checks that the switch covers all tables.
|
||||
switch {
|
||||
case encode0Low <= r && r < encode0High:
|
||||
if r = rune(encode0[r-encode0Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
case encode1Low <= r && r < encode1High:
|
||||
if r = rune(encode1[r-encode1Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
case encode2Low <= r && r < encode2High:
|
||||
if r = rune(encode2[r-encode2Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
case encode3Low <= r && r < encode3High:
|
||||
if r = rune(encode3[r-encode3Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
case encode4Low <= r && r < encode4High:
|
||||
if r = rune(encode4[r-encode4Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
case encode5Low <= r && r < encode5High:
|
||||
if 0xff61 <= r && r < 0xffa0 {
|
||||
goto write2
|
||||
}
|
||||
if r = rune(encode5[r-encode5Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
}
|
||||
err = internal.ErrASCIIReplacement
|
||||
break
|
||||
}
|
||||
|
||||
if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = uint8(r)
|
||||
nDst++
|
||||
continue
|
||||
|
||||
write2or3:
|
||||
if r>>tableShift == jis0208 {
|
||||
if nDst+2 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if nDst+3 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = 0x8f
|
||||
nDst++
|
||||
}
|
||||
dst[nDst+0] = 0xa1 + uint8(r>>codeShift)&codeMask
|
||||
dst[nDst+1] = 0xa1 + uint8(r)&codeMask
|
||||
nDst += 2
|
||||
continue
|
||||
|
||||
write2:
|
||||
if nDst+2 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst+0] = 0x8e
|
||||
dst[nDst+1] = uint8(r - (0xff61 - 0xa1))
|
||||
nDst += 2
|
||||
continue
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Check that the hard-coded encode switch covers all tables.
|
||||
if numEncodeTables != 6 {
|
||||
panic("bad numEncodeTables")
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue