1
0
Fork 0

Custom resource definition

Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
This commit is contained in:
Ludovic Fernandez 2019-03-14 15:56:06 +01:00 committed by Traefiker Bot
parent cfaf47c8a2
commit 4c060a78cc
1348 changed files with 92364 additions and 55766 deletions

View file

@ -1,36 +0,0 @@
// 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 precis
import (
"unicode/utf8"
)
// TODO: Add contextual character rules from Appendix A of RFC5892.
// A class is a set of characters that match certain derived properties. The
// PRECIS framework defines two classes: The Freeform class and the Identifier
// class. The freeform class should be used for profiles where expressiveness is
// prioritized over safety such as nicknames or passwords. The identifier class
// should be used for profiles where safety is the first priority such as
// addressable network labels and usernames.
type class struct {
validFrom property
}
// Contains satisfies the runes.Set interface and returns whether the given rune
// is a member of the class.
func (c class) Contains(r rune) bool {
b := make([]byte, 4)
n := utf8.EncodeRune(b, r)
trieval, _ := dpTrie.lookup(b[:n])
return c.validFrom <= property(trieval)
}
var (
identifier = &class{validFrom: pValid}
freeform = &class{validFrom: idDisOrFreePVal}
)

View file

@ -1,139 +0,0 @@
// 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 precis
import "errors"
// This file contains tables and code related to context rules.
type catBitmap uint16
const (
// These bits, once set depending on the current value, are never unset.
bJapanese catBitmap = 1 << iota
bArabicIndicDigit
bExtendedArabicIndicDigit
// These bits are set on each iteration depending on the current value.
bJoinStart
bJoinMid
bJoinEnd
bVirama
bLatinSmallL
bGreek
bHebrew
// These bits indicated which of the permanent bits need to be set at the
// end of the checks.
bMustHaveJapn
permanent = bJapanese | bArabicIndicDigit | bExtendedArabicIndicDigit | bMustHaveJapn
)
const finalShift = 10
var errContext = errors.New("precis: contextual rule violated")
func init() {
// Programmatically set these required bits as, manually setting them seems
// too error prone.
for i, ct := range categoryTransitions {
categoryTransitions[i].keep |= permanent
categoryTransitions[i].accept |= ct.term
}
}
var categoryTransitions = []struct {
keep catBitmap // mask selecting which bits to keep from the previous state
set catBitmap // mask for which bits to set for this transition
// These bitmaps are used for rules that require lookahead.
// term&accept == term must be true, which is enforced programmatically.
term catBitmap // bits accepted as termination condition
accept catBitmap // bits that pass, but not sufficient as termination
// The rule function cannot take a *context as an argument, as it would
// cause the context to escape, adding significant overhead.
rule func(beforeBits catBitmap) (doLookahead bool, err error)
}{
joiningL: {set: bJoinStart},
joiningD: {set: bJoinStart | bJoinEnd},
joiningT: {keep: bJoinStart, set: bJoinMid},
joiningR: {set: bJoinEnd},
viramaModifier: {set: bVirama},
viramaJoinT: {set: bVirama | bJoinMid},
latinSmallL: {set: bLatinSmallL},
greek: {set: bGreek},
greekJoinT: {set: bGreek | bJoinMid},
hebrew: {set: bHebrew},
hebrewJoinT: {set: bHebrew | bJoinMid},
japanese: {set: bJapanese},
katakanaMiddleDot: {set: bMustHaveJapn},
zeroWidthNonJoiner: {
term: bJoinEnd,
accept: bJoinMid,
rule: func(before catBitmap) (doLookAhead bool, err error) {
if before&bVirama != 0 {
return false, nil
}
if before&bJoinStart == 0 {
return false, errContext
}
return true, nil
},
},
zeroWidthJoiner: {
rule: func(before catBitmap) (doLookAhead bool, err error) {
if before&bVirama == 0 {
err = errContext
}
return false, err
},
},
middleDot: {
term: bLatinSmallL,
rule: func(before catBitmap) (doLookAhead bool, err error) {
if before&bLatinSmallL == 0 {
return false, errContext
}
return true, nil
},
},
greekLowerNumeralSign: {
set: bGreek,
term: bGreek,
rule: func(before catBitmap) (doLookAhead bool, err error) {
return true, nil
},
},
hebrewPreceding: {
set: bHebrew,
rule: func(before catBitmap) (doLookAhead bool, err error) {
if before&bHebrew == 0 {
err = errContext
}
return false, err
},
},
arabicIndicDigit: {
set: bArabicIndicDigit,
rule: func(before catBitmap) (doLookAhead bool, err error) {
if before&bExtendedArabicIndicDigit != 0 {
err = errContext
}
return false, err
},
},
extendedArabicIndicDigit: {
set: bExtendedArabicIndicDigit,
rule: func(before catBitmap) (doLookAhead bool, err error) {
if before&bArabicIndicDigit != 0 {
err = errContext
}
return false, err
},
},
}

View file

@ -1,14 +0,0 @@
// 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 precis contains types and functions for the preparation,
// enforcement, and comparison of internationalized strings ("PRECIS") as
// defined in RFC 7564. It also contains several pre-defined profiles for
// passwords, nicknames, and usernames as defined in RFC 7613 and RFC 7700.
//
// BE ADVISED: This package is under construction and the API may change in
// backwards incompatible ways and without notice.
package precis // import "golang.org/x/text/secure/precis"
//go:generate go run gen.go gen_trieval.go

View file

@ -1,310 +0,0 @@
// 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.
// Unicode table generator.
// Data read from the web.
// +build ignore
package main
import (
"flag"
"log"
"unicode"
"unicode/utf8"
"golang.org/x/text/internal/gen"
"golang.org/x/text/internal/triegen"
"golang.org/x/text/internal/ucd"
"golang.org/x/text/unicode/norm"
"golang.org/x/text/unicode/rangetable"
)
var outputFile = flag.String("output", "tables.go", "output file for generated tables; default tables.go")
var assigned, disallowedRunes *unicode.RangeTable
var runeCategory = map[rune]category{}
var overrides = map[category]category{
viramaModifier: viramaJoinT,
greek: greekJoinT,
hebrew: hebrewJoinT,
}
func setCategory(r rune, cat category) {
if c, ok := runeCategory[r]; ok {
if override, ok := overrides[c]; cat == joiningT && ok {
cat = override
} else {
log.Fatalf("%U: multiple categories for rune (%v and %v)", r, c, cat)
}
}
runeCategory[r] = cat
}
func init() {
if numCategories > 1<<propShift {
log.Fatalf("Number of categories is %d; may at most be %d", numCategories, 1<<propShift)
}
}
func main() {
gen.Init()
// Load data
runes := []rune{}
// PrecisIgnorableProperties: https://tools.ietf.org/html/rfc7564#section-9.13
ucd.Parse(gen.OpenUCDFile("DerivedCoreProperties.txt"), func(p *ucd.Parser) {
if p.String(1) == "Default_Ignorable_Code_Point" {
runes = append(runes, p.Rune(0))
}
})
ucd.Parse(gen.OpenUCDFile("PropList.txt"), func(p *ucd.Parser) {
switch p.String(1) {
case "Noncharacter_Code_Point":
runes = append(runes, p.Rune(0))
}
})
// OldHangulJamo: https://tools.ietf.org/html/rfc5892#section-2.9
ucd.Parse(gen.OpenUCDFile("HangulSyllableType.txt"), func(p *ucd.Parser) {
switch p.String(1) {
case "L", "V", "T":
runes = append(runes, p.Rune(0))
}
})
disallowedRunes = rangetable.New(runes...)
assigned = rangetable.Assigned(unicode.Version)
// Load category data.
runeCategory['l'] = latinSmallL
ucd.Parse(gen.OpenUCDFile("UnicodeData.txt"), func(p *ucd.Parser) {
const cccVirama = 9
if p.Int(ucd.CanonicalCombiningClass) == cccVirama {
setCategory(p.Rune(0), viramaModifier)
}
})
ucd.Parse(gen.OpenUCDFile("Scripts.txt"), func(p *ucd.Parser) {
switch p.String(1) {
case "Greek":
setCategory(p.Rune(0), greek)
case "Hebrew":
setCategory(p.Rune(0), hebrew)
case "Hiragana", "Katakana", "Han":
setCategory(p.Rune(0), japanese)
}
})
// Set the rule categories associated with exceptions. This overrides any
// previously set categories. The original categories are manually
// reintroduced in the categoryTransitions table.
for r, e := range exceptions {
if e.cat != 0 {
runeCategory[r] = e.cat
}
}
cat := map[string]category{
"L": joiningL,
"D": joiningD,
"T": joiningT,
"R": joiningR,
}
ucd.Parse(gen.OpenUCDFile("extracted/DerivedJoiningType.txt"), func(p *ucd.Parser) {
switch v := p.String(1); v {
case "L", "D", "T", "R":
setCategory(p.Rune(0), cat[v])
}
})
writeTables()
gen.Repackage("gen_trieval.go", "trieval.go", "precis")
}
type exception struct {
prop property
cat category
}
func init() {
// Programmatically add the Arabic and Indic digits to the exceptions map.
// See comment in the exceptions map below why these are marked disallowed.
for i := rune(0); i <= 9; i++ {
exceptions[0x0660+i] = exception{
prop: disallowed,
cat: arabicIndicDigit,
}
exceptions[0x06F0+i] = exception{
prop: disallowed,
cat: extendedArabicIndicDigit,
}
}
}
// The Exceptions class as defined in RFC 5892
// https://tools.ietf.org/html/rfc5892#section-2.6
var exceptions = map[rune]exception{
0x00DF: {prop: pValid},
0x03C2: {prop: pValid},
0x06FD: {prop: pValid},
0x06FE: {prop: pValid},
0x0F0B: {prop: pValid},
0x3007: {prop: pValid},
// ContextO|J rules are marked as disallowed, taking a "guilty until proven
// innocent" approach. The main reason for this is that the check for
// whether a context rule should be applied can be moved to the logic for
// handing disallowed runes, taken it off the common path. The exception to
// this rule is for katakanaMiddleDot, as the rule logic is handled without
// using a rule function.
// ContextJ (Join control)
0x200C: {prop: disallowed, cat: zeroWidthNonJoiner},
0x200D: {prop: disallowed, cat: zeroWidthJoiner},
// ContextO
0x00B7: {prop: disallowed, cat: middleDot},
0x0375: {prop: disallowed, cat: greekLowerNumeralSign},
0x05F3: {prop: disallowed, cat: hebrewPreceding}, // punctuation Geresh
0x05F4: {prop: disallowed, cat: hebrewPreceding}, // punctuation Gershayim
0x30FB: {prop: pValid, cat: katakanaMiddleDot},
// These are officially ContextO, but the implementation does not require
// special treatment of these, so we simply mark them as valid.
0x0660: {prop: pValid},
0x0661: {prop: pValid},
0x0662: {prop: pValid},
0x0663: {prop: pValid},
0x0664: {prop: pValid},
0x0665: {prop: pValid},
0x0666: {prop: pValid},
0x0667: {prop: pValid},
0x0668: {prop: pValid},
0x0669: {prop: pValid},
0x06F0: {prop: pValid},
0x06F1: {prop: pValid},
0x06F2: {prop: pValid},
0x06F3: {prop: pValid},
0x06F4: {prop: pValid},
0x06F5: {prop: pValid},
0x06F6: {prop: pValid},
0x06F7: {prop: pValid},
0x06F8: {prop: pValid},
0x06F9: {prop: pValid},
0x0640: {prop: disallowed},
0x07FA: {prop: disallowed},
0x302E: {prop: disallowed},
0x302F: {prop: disallowed},
0x3031: {prop: disallowed},
0x3032: {prop: disallowed},
0x3033: {prop: disallowed},
0x3034: {prop: disallowed},
0x3035: {prop: disallowed},
0x303B: {prop: disallowed},
}
// LetterDigits: https://tools.ietf.org/html/rfc5892#section-2.1
// r in {Ll, Lu, Lo, Nd, Lm, Mn, Mc}.
func isLetterDigits(r rune) bool {
return unicode.In(r,
unicode.Ll, unicode.Lu, unicode.Lm, unicode.Lo, // Letters
unicode.Mn, unicode.Mc, // Modifiers
unicode.Nd, // Digits
)
}
func isIdDisAndFreePVal(r rune) bool {
return unicode.In(r,
// OtherLetterDigits: https://tools.ietf.org/html/rfc7564#section-9.18
// r in in {Lt, Nl, No, Me}
unicode.Lt, unicode.Nl, unicode.No, // Other letters / numbers
unicode.Me, // Modifiers
// Spaces: https://tools.ietf.org/html/rfc7564#section-9.14
// r in in {Zs}
unicode.Zs,
// Symbols: https://tools.ietf.org/html/rfc7564#section-9.15
// r in {Sm, Sc, Sk, So}
unicode.Sm, unicode.Sc, unicode.Sk, unicode.So,
// Punctuation: https://tools.ietf.org/html/rfc7564#section-9.16
// r in {Pc, Pd, Ps, Pe, Pi, Pf, Po}
unicode.Pc, unicode.Pd, unicode.Ps, unicode.Pe,
unicode.Pi, unicode.Pf, unicode.Po,
)
}
// HasCompat: https://tools.ietf.org/html/rfc7564#section-9.17
func hasCompat(r rune) bool {
return !norm.NFKC.IsNormalString(string(r))
}
// From https://tools.ietf.org/html/rfc5892:
//
// If .cp. .in. Exceptions Then Exceptions(cp);
// Else If .cp. .in. BackwardCompatible Then BackwardCompatible(cp);
// Else If .cp. .in. Unassigned Then UNASSIGNED;
// Else If .cp. .in. ASCII7 Then PVALID;
// Else If .cp. .in. JoinControl Then CONTEXTJ;
// Else If .cp. .in. OldHangulJamo Then DISALLOWED;
// Else If .cp. .in. PrecisIgnorableProperties Then DISALLOWED;
// Else If .cp. .in. Controls Then DISALLOWED;
// Else If .cp. .in. HasCompat Then ID_DIS or FREE_PVAL;
// Else If .cp. .in. LetterDigits Then PVALID;
// Else If .cp. .in. OtherLetterDigits Then ID_DIS or FREE_PVAL;
// Else If .cp. .in. Spaces Then ID_DIS or FREE_PVAL;
// Else If .cp. .in. Symbols Then ID_DIS or FREE_PVAL;
// Else If .cp. .in. Punctuation Then ID_DIS or FREE_PVAL;
// Else DISALLOWED;
func writeTables() {
propTrie := triegen.NewTrie("derivedProperties")
w := gen.NewCodeWriter()
defer w.WriteGoFile(*outputFile, "precis")
gen.WriteUnicodeVersion(w)
// Iterate over all the runes...
for i := rune(0); i < unicode.MaxRune; i++ {
r := rune(i)
if !utf8.ValidRune(r) {
continue
}
e, ok := exceptions[i]
p := e.prop
switch {
case ok:
case !unicode.In(r, assigned):
p = unassigned
case r >= 0x0021 && r <= 0x007e: // Is ASCII 7
p = pValid
case unicode.In(r, disallowedRunes, unicode.Cc):
p = disallowed
case hasCompat(r):
p = idDisOrFreePVal
case isLetterDigits(r):
p = pValid
case isIdDisAndFreePVal(r):
p = idDisOrFreePVal
default:
p = disallowed
}
cat := runeCategory[r]
// Don't set category for runes that are disallowed.
if p == disallowed {
cat = exceptions[r].cat
}
propTrie.Insert(r, uint64(p)|uint64(cat))
}
sz, err := propTrie.Gen(w)
if err != nil {
log.Fatal(err)
}
w.Size += sz
}

View file

@ -1,68 +0,0 @@
// 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
// entry is the entry of a trie table
// 7..6 property (unassigned, disallowed, maybe, valid)
// 5..0 category
type entry uint8
const (
propShift = 6
propMask = 0xc0
catMask = 0x3f
)
func (e entry) property() property { return property(e & propMask) }
func (e entry) category() category { return category(e & catMask) }
type property uint8
// The order of these constants matter. A Profile may consider runes to be
// allowed either from pValid or idDisOrFreePVal.
const (
unassigned property = iota << propShift
disallowed
idDisOrFreePVal // disallowed for Identifier, pValid for FreeForm
pValid
)
// compute permutations of all properties and specialCategories.
type category uint8
const (
other category = iota
// Special rune types
joiningL
joiningD
joiningT
joiningR
viramaModifier
viramaJoinT // Virama + JoiningT
latinSmallL // U+006c
greek
greekJoinT // Greek + JoiningT
hebrew
hebrewJoinT // Hebrew + JoiningT
japanese // hirigana, katakana, han
// Special rune types associated with contextual rules defined in
// https://tools.ietf.org/html/rfc5892#appendix-A.
// ContextO
zeroWidthNonJoiner // rule 1
zeroWidthJoiner // rule 2
// ContextJ
middleDot // rule 3
greekLowerNumeralSign // rule 4
hebrewPreceding // rule 5 and 6
katakanaMiddleDot // rule 7
arabicIndicDigit // rule 8
extendedArabicIndicDigit // rule 9
numCategories
)

View file

@ -1,70 +0,0 @@
// 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 precis
import (
"unicode"
"unicode/utf8"
"golang.org/x/text/transform"
)
type nickAdditionalMapping struct {
// TODO: This transformer needs to be stateless somehow…
notStart bool
prevSpace bool
}
func (t *nickAdditionalMapping) Reset() {
t.prevSpace = false
t.notStart = false
}
func (t *nickAdditionalMapping) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
// RFC 7700 §2.1. Rules
//
// 2. Additional Mapping Rule: The additional mapping rule consists of
// the following sub-rules.
//
// 1. Any instances of non-ASCII space MUST be mapped to ASCII
// space (U+0020); a non-ASCII space is any Unicode code point
// having a general category of "Zs", naturally with the
// exception of U+0020.
//
// 2. Any instances of the ASCII space character at the beginning
// or end of a nickname MUST be removed (e.g., "stpeter " is
// mapped to "stpeter").
//
// 3. Interior sequences of more than one ASCII space character
// MUST be mapped to a single ASCII space character (e.g.,
// "St Peter" is mapped to "St Peter").
for nSrc < len(src) {
r, size := utf8.DecodeRune(src[nSrc:])
if size == 0 { // Incomplete UTF-8 encoding
if !atEOF {
return nDst, nSrc, transform.ErrShortSrc
}
size = 1
}
if unicode.Is(unicode.Zs, r) {
t.prevSpace = true
} else {
if t.prevSpace && t.notStart {
dst[nDst] = ' '
nDst += 1
}
if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
nDst += size
return nDst, nSrc, transform.ErrShortDst
}
nDst += size
t.prevSpace = false
t.notStart = true
}
nSrc += size
}
return nDst, nSrc, nil
}

View file

@ -1,153 +0,0 @@
// 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 precis
import (
"golang.org/x/text/cases"
"golang.org/x/text/language"
"golang.org/x/text/runes"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
)
// An Option is used to define the behavior and rules of a Profile.
type Option func(*options)
type options struct {
// Preparation options
foldWidth bool
// Enforcement options
asciiLower bool
cases transform.SpanningTransformer
disallow runes.Set
norm transform.SpanningTransformer
additional []func() transform.SpanningTransformer
width transform.SpanningTransformer
disallowEmpty bool
bidiRule bool
// Comparison options
ignorecase bool
}
func getOpts(o ...Option) (res options) {
for _, f := range o {
f(&res)
}
// Using a SpanningTransformer, instead of norm.Form prevents an allocation
// down the road.
if res.norm == nil {
res.norm = norm.NFC
}
return
}
var (
// The IgnoreCase option causes the profile to perform a case insensitive
// comparison during the PRECIS comparison step.
IgnoreCase Option = ignoreCase
// The FoldWidth option causes the profile to map non-canonical wide and
// narrow variants to their decomposition mapping. This is useful for
// profiles that are based on the identifier class which would otherwise
// disallow such characters.
FoldWidth Option = foldWidth
// The DisallowEmpty option causes the enforcement step to return an error if
// the resulting string would be empty.
DisallowEmpty Option = disallowEmpty
// The BidiRule option causes the Bidi Rule defined in RFC 5893 to be
// applied.
BidiRule Option = bidiRule
)
var (
ignoreCase = func(o *options) {
o.ignorecase = true
}
foldWidth = func(o *options) {
o.foldWidth = true
}
disallowEmpty = func(o *options) {
o.disallowEmpty = true
}
bidiRule = func(o *options) {
o.bidiRule = true
}
)
// TODO: move this logic to package transform
type spanWrap struct{ transform.Transformer }
func (s spanWrap) Span(src []byte, atEOF bool) (n int, err error) {
return 0, transform.ErrEndOfSpan
}
// TODO: allow different types? For instance:
// func() transform.Transformer
// func() transform.SpanningTransformer
// func([]byte) bool // validation only
//
// Also, would be great if we could detect if a transformer is reentrant.
// The AdditionalMapping option defines the additional mapping rule for the
// Profile by applying Transformer's in sequence.
func AdditionalMapping(t ...func() transform.Transformer) Option {
return func(o *options) {
for _, f := range t {
sf := func() transform.SpanningTransformer {
return f().(transform.SpanningTransformer)
}
if _, ok := f().(transform.SpanningTransformer); !ok {
sf = func() transform.SpanningTransformer {
return spanWrap{f()}
}
}
o.additional = append(o.additional, sf)
}
}
}
// The Norm option defines a Profile's normalization rule. Defaults to NFC.
func Norm(f norm.Form) Option {
return func(o *options) {
o.norm = f
}
}
// The FoldCase option defines a Profile's case mapping rule. Options can be
// provided to determine the type of case folding used.
func FoldCase(opts ...cases.Option) Option {
return func(o *options) {
o.asciiLower = true
o.cases = cases.Fold(opts...)
}
}
// The LowerCase option defines a Profile's case mapping rule. Options can be
// provided to determine the type of case folding used.
func LowerCase(opts ...cases.Option) Option {
return func(o *options) {
o.asciiLower = true
if len(opts) == 0 {
o.cases = cases.Lower(language.Und, cases.HandleFinalSigma(false))
return
}
opts = append([]cases.Option{cases.HandleFinalSigma(false)}, opts...)
o.cases = cases.Lower(language.Und, opts...)
}
}
// The Disallow option further restricts a Profile's allowed characters beyond
// what is disallowed by the underlying string class.
func Disallow(set runes.Set) Option {
return func(o *options) {
o.disallow = set
}
}

View file

@ -1,388 +0,0 @@
// 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 precis
import (
"bytes"
"errors"
"unicode/utf8"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"golang.org/x/text/runes"
"golang.org/x/text/secure/bidirule"
"golang.org/x/text/transform"
"golang.org/x/text/width"
)
var (
errDisallowedRune = errors.New("precis: disallowed rune encountered")
)
var dpTrie = newDerivedPropertiesTrie(0)
// A Profile represents a set of rules for normalizing and validating strings in
// the PRECIS framework.
type Profile struct {
options
class *class
}
// NewIdentifier creates a new PRECIS profile based on the Identifier string
// class. Profiles created from this class are suitable for use where safety is
// prioritized over expressiveness like network identifiers, user accounts, chat
// rooms, and file names.
func NewIdentifier(opts ...Option) *Profile {
return &Profile{
options: getOpts(opts...),
class: identifier,
}
}
// NewFreeform creates a new PRECIS profile based on the Freeform string class.
// Profiles created from this class are suitable for use where expressiveness is
// prioritized over safety like passwords, and display-elements such as
// nicknames in a chat room.
func NewFreeform(opts ...Option) *Profile {
return &Profile{
options: getOpts(opts...),
class: freeform,
}
}
// NewTransformer creates a new transform.Transformer that performs the PRECIS
// preparation and enforcement steps on the given UTF-8 encoded bytes.
func (p *Profile) NewTransformer() *Transformer {
var ts []transform.Transformer
// These transforms are applied in the order defined in
// https://tools.ietf.org/html/rfc7564#section-7
if p.options.foldWidth {
ts = append(ts, width.Fold)
}
for _, f := range p.options.additional {
ts = append(ts, f())
}
if p.options.cases != nil {
ts = append(ts, p.options.cases)
}
ts = append(ts, p.options.norm)
if p.options.bidiRule {
ts = append(ts, bidirule.New())
}
ts = append(ts, &checker{p: p, allowed: p.Allowed()})
// TODO: Add the disallow empty rule with a dummy transformer?
return &Transformer{transform.Chain(ts...)}
}
var errEmptyString = errors.New("precis: transformation resulted in empty string")
type buffers struct {
src []byte
buf [2][]byte
next int
}
func (b *buffers) apply(t transform.SpanningTransformer) (err error) {
n, err := t.Span(b.src, true)
if err != transform.ErrEndOfSpan {
return err
}
x := b.next & 1
if b.buf[x] == nil {
b.buf[x] = make([]byte, 0, 8+len(b.src)+len(b.src)>>2)
}
span := append(b.buf[x][:0], b.src[:n]...)
b.src, _, err = transform.Append(t, span, b.src[n:])
b.buf[x] = b.src
b.next++
return err
}
// Pre-allocate transformers when possible. In some cases this avoids allocation.
var (
foldWidthT transform.SpanningTransformer = width.Fold
lowerCaseT transform.SpanningTransformer = cases.Lower(language.Und, cases.HandleFinalSigma(false))
)
// TODO: make this a method on profile.
func (b *buffers) enforce(p *Profile, src []byte, comparing bool) (str []byte, err error) {
b.src = src
ascii := true
for _, c := range src {
if c >= utf8.RuneSelf {
ascii = false
break
}
}
// ASCII fast path.
if ascii {
for _, f := range p.options.additional {
if err = b.apply(f()); err != nil {
return nil, err
}
}
switch {
case p.options.asciiLower || (comparing && p.options.ignorecase):
for i, c := range b.src {
if 'A' <= c && c <= 'Z' {
b.src[i] = c ^ 1<<5
}
}
case p.options.cases != nil:
b.apply(p.options.cases)
}
c := checker{p: p}
if _, err := c.span(b.src, true); err != nil {
return nil, err
}
if p.disallow != nil {
for _, c := range b.src {
if p.disallow.Contains(rune(c)) {
return nil, errDisallowedRune
}
}
}
if p.options.disallowEmpty && len(b.src) == 0 {
return nil, errEmptyString
}
return b.src, nil
}
// These transforms are applied in the order defined in
// https://tools.ietf.org/html/rfc7564#section-7
// TODO: allow different width transforms options.
if p.options.foldWidth || (p.options.ignorecase && comparing) {
b.apply(foldWidthT)
}
for _, f := range p.options.additional {
if err = b.apply(f()); err != nil {
return nil, err
}
}
if p.options.cases != nil {
b.apply(p.options.cases)
}
if comparing && p.options.ignorecase {
b.apply(lowerCaseT)
}
b.apply(p.norm)
if p.options.bidiRule && !bidirule.Valid(b.src) {
return nil, bidirule.ErrInvalid
}
c := checker{p: p}
if _, err := c.span(b.src, true); err != nil {
return nil, err
}
if p.disallow != nil {
for i := 0; i < len(b.src); {
r, size := utf8.DecodeRune(b.src[i:])
if p.disallow.Contains(r) {
return nil, errDisallowedRune
}
i += size
}
}
if p.options.disallowEmpty && len(b.src) == 0 {
return nil, errEmptyString
}
return b.src, nil
}
// Append appends the result of applying p to src writing the result to dst.
// It returns an error if the input string is invalid.
func (p *Profile) Append(dst, src []byte) ([]byte, error) {
var buf buffers
b, err := buf.enforce(p, src, false)
if err != nil {
return nil, err
}
return append(dst, b...), nil
}
func processBytes(p *Profile, b []byte, key bool) ([]byte, error) {
var buf buffers
b, err := buf.enforce(p, b, key)
if err != nil {
return nil, err
}
if buf.next == 0 {
c := make([]byte, len(b))
copy(c, b)
return c, nil
}
return b, nil
}
// Bytes returns a new byte slice with the result of applying the profile to b.
func (p *Profile) Bytes(b []byte) ([]byte, error) {
return processBytes(p, b, false)
}
// AppendCompareKey appends the result of applying p to src (including any
// optional rules to make strings comparable or useful in a map key such as
// applying lowercasing) writing the result to dst. It returns an error if the
// input string is invalid.
func (p *Profile) AppendCompareKey(dst, src []byte) ([]byte, error) {
var buf buffers
b, err := buf.enforce(p, src, true)
if err != nil {
return nil, err
}
return append(dst, b...), nil
}
func processString(p *Profile, s string, key bool) (string, error) {
var buf buffers
b, err := buf.enforce(p, []byte(s), key)
if err != nil {
return "", err
}
return string(b), nil
}
// String returns a string with the result of applying the profile to s.
func (p *Profile) String(s string) (string, error) {
return processString(p, s, false)
}
// CompareKey returns a string that can be used for comparison, hashing, or
// collation.
func (p *Profile) CompareKey(s string) (string, error) {
return processString(p, s, true)
}
// Compare enforces both strings, and then compares them for bit-string identity
// (byte-for-byte equality). If either string cannot be enforced, the comparison
// is false.
func (p *Profile) Compare(a, b string) bool {
var buf buffers
akey, err := buf.enforce(p, []byte(a), true)
if err != nil {
return false
}
buf = buffers{}
bkey, err := buf.enforce(p, []byte(b), true)
if err != nil {
return false
}
return bytes.Compare(akey, bkey) == 0
}
// Allowed returns a runes.Set containing every rune that is a member of the
// underlying profile's string class and not disallowed by any profile specific
// rules.
func (p *Profile) Allowed() runes.Set {
if p.options.disallow != nil {
return runes.Predicate(func(r rune) bool {
return p.class.Contains(r) && !p.options.disallow.Contains(r)
})
}
return p.class
}
type checker struct {
p *Profile
allowed runes.Set
beforeBits catBitmap
termBits catBitmap
acceptBits catBitmap
}
func (c *checker) Reset() {
c.beforeBits = 0
c.termBits = 0
c.acceptBits = 0
}
func (c *checker) span(src []byte, atEOF bool) (n int, err error) {
for n < len(src) {
e, sz := dpTrie.lookup(src[n:])
d := categoryTransitions[category(e&catMask)]
if sz == 0 {
if !atEOF {
return n, transform.ErrShortSrc
}
return n, errDisallowedRune
}
if property(e) < c.p.class.validFrom {
if d.rule == nil {
return n, errDisallowedRune
}
doLookAhead, err := d.rule(c.beforeBits)
if err != nil {
return n, err
}
if doLookAhead {
c.beforeBits &= d.keep
c.beforeBits |= d.set
// We may still have a lookahead rule which we will require to
// complete (by checking termBits == 0) before setting the new
// bits.
if c.termBits != 0 && (!c.checkLookahead() || c.termBits == 0) {
return n, err
}
c.termBits = d.term
c.acceptBits = d.accept
n += sz
continue
}
}
c.beforeBits &= d.keep
c.beforeBits |= d.set
if c.termBits != 0 && !c.checkLookahead() {
return n, errContext
}
n += sz
}
if m := c.beforeBits >> finalShift; c.beforeBits&m != m || c.termBits != 0 {
err = errContext
}
return n, err
}
func (c *checker) checkLookahead() bool {
switch {
case c.beforeBits&c.termBits != 0:
c.termBits = 0
c.acceptBits = 0
case c.beforeBits&c.acceptBits != 0:
default:
return false
}
return true
}
// TODO: we may get rid of this transform if transform.Chain understands
// something like a Spanner interface.
func (c checker) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
short := false
if len(dst) < len(src) {
src = src[:len(dst)]
atEOF = false
short = true
}
nSrc, err = c.span(src, atEOF)
nDst = copy(dst, src[:nSrc])
if short && (err == transform.ErrShortSrc || err == nil) {
err = transform.ErrShortDst
}
return nDst, nSrc, err
}

View file

@ -1,78 +0,0 @@
// 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 precis
import (
"unicode"
"golang.org/x/text/runes"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
)
var (
// Implements the Nickname profile specified in RFC 7700.
// The nickname profile is not idempotent and may need to be applied multiple
// times before being used for comparisons.
Nickname *Profile = nickname
// Implements the UsernameCaseMapped profile specified in RFC 7613.
UsernameCaseMapped *Profile = usernameCaseMap
// Implements the UsernameCasePreserved profile specified in RFC 7613.
UsernameCasePreserved *Profile = usernameNoCaseMap
// Implements the OpaqueString profile defined in RFC 7613 for passwords and other secure labels.
OpaqueString *Profile = opaquestring
)
var (
nickname = &Profile{
options: getOpts(
AdditionalMapping(func() transform.Transformer {
return &nickAdditionalMapping{}
}),
IgnoreCase,
Norm(norm.NFKC),
DisallowEmpty,
),
class: freeform,
}
usernameCaseMap = &Profile{
options: getOpts(
FoldWidth,
LowerCase(),
Norm(norm.NFC),
BidiRule,
),
class: identifier,
}
usernameNoCaseMap = &Profile{
options: getOpts(
FoldWidth,
Norm(norm.NFC),
BidiRule,
),
class: identifier,
}
opaquestring = &Profile{
options: getOpts(
AdditionalMapping(func() transform.Transformer {
return mapSpaces
}),
Norm(norm.NFC),
DisallowEmpty,
),
class: freeform,
}
)
// mapSpaces is a shared value of a runes.Map transformer.
var mapSpaces transform.Transformer = runes.Map(func(r rune) rune {
if unicode.Is(unicode.Zs, r) {
return ' '
}
return r
})

File diff suppressed because it is too large Load diff

View file

@ -1,32 +0,0 @@
// 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 precis
import "golang.org/x/text/transform"
// Transformer implements the transform.Transformer interface.
type Transformer struct {
t transform.Transformer
}
// Reset implements the transform.Transformer interface.
func (t Transformer) Reset() { t.t.Reset() }
// Transform implements the transform.Transformer interface.
func (t Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
return t.t.Transform(dst, src, atEOF)
}
// Bytes returns a new byte slice with the result of applying t to b.
func (t Transformer) Bytes(b []byte) []byte {
b, _, _ = transform.Bytes(t, b)
return b
}
// String returns a string with the result of applying t to s.
func (t Transformer) String(s string) string {
s, _, _ = transform.String(t, s)
return s
}

View file

@ -1,64 +0,0 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
package precis
// entry is the entry of a trie table
// 7..6 property (unassigned, disallowed, maybe, valid)
// 5..0 category
type entry uint8
const (
propShift = 6
propMask = 0xc0
catMask = 0x3f
)
func (e entry) property() property { return property(e & propMask) }
func (e entry) category() category { return category(e & catMask) }
type property uint8
// The order of these constants matter. A Profile may consider runes to be
// allowed either from pValid or idDisOrFreePVal.
const (
unassigned property = iota << propShift
disallowed
idDisOrFreePVal // disallowed for Identifier, pValid for FreeForm
pValid
)
// compute permutations of all properties and specialCategories.
type category uint8
const (
other category = iota
// Special rune types
joiningL
joiningD
joiningT
joiningR
viramaModifier
viramaJoinT // Virama + JoiningT
latinSmallL // U+006c
greek
greekJoinT // Greek + JoiningT
hebrew
hebrewJoinT // Hebrew + JoiningT
japanese // hirigana, katakana, han
// Special rune types associated with contextual rules defined in
// https://tools.ietf.org/html/rfc5892#appendix-A.
// ContextO
zeroWidthNonJoiner // rule 1
zeroWidthJoiner // rule 2
// ContextJ
middleDot // rule 3
greekLowerNumeralSign // rule 4
hebrewPreceding // rule 5 and 6
katakanaMiddleDot // rule 7
arabicIndicDigit // rule 8
extendedArabicIndicDigit // rule 9
numCategories
)