package precis
import (
)
var (
errDisallowedRune = errors.New("precis: disallowed rune encountered")
)
var dpTrie = newDerivedPropertiesTrie(0)
type Profile struct {
options
class *class
}
func ( ...Option) *Profile {
return &Profile{
options: getOpts(...),
class: identifier,
}
}
func ( ...Option) *Profile {
return &Profile{
options: getOpts(...),
class: freeform,
}
}
func ( *Profile, runes.Set) *Profile {
:= *
Disallow()(&.options)
return &
}
func ( *Profile) () *Transformer {
var []transform.Transformer
:= 1
if .options.repeat {
= 4
}
for ; > 0; -- {
if .options.foldWidth {
= append(, width.Fold)
}
for , := range .options.additional {
= append(, ())
}
if .options.cases != nil {
= append(, .options.cases)
}
= append(, .options.norm)
if .options.bidiRule {
= append(, bidirule.New())
}
= append(, &checker{p: , allowed: .Allowed()})
}
return &Transformer{transform.Chain(...)}
}
var errEmptyString = errors.New("precis: transformation resulted in empty string")
type buffers struct {
src []byte
buf [2][]byte
next int
}
func ( *buffers) ( transform.SpanningTransformer) ( error) {
, := .Span(.src, true)
if != transform.ErrEndOfSpan {
return
}
:= .next & 1
if .buf[] == nil {
.buf[] = make([]byte, 0, 8+len(.src)+len(.src)>>2)
}
:= append(.buf[][:0], .src[:]...)
.src, _, = transform.Append(, , .src[:])
.buf[] = .src
.next++
return
}
var (
foldWidthT transform.SpanningTransformer = width.Fold
lowerCaseT transform.SpanningTransformer = cases.Lower(language.Und, cases.HandleFinalSigma(false))
)
func ( *buffers) ( *Profile, []byte, bool) ( []byte, error) {
.src =
:= true
for , := range {
if >= utf8.RuneSelf {
= false
break
}
}
if {
for , := range .options.additional {
if = .apply(()); != nil {
return nil,
}
}
switch {
case .options.asciiLower || ( && .options.ignorecase):
for , := range .src {
if 'A' <= && <= 'Z' {
.src[] = ^ 1<<5
}
}
case .options.cases != nil:
.apply(.options.cases)
}
:= checker{p: }
if , := .span(.src, true); != nil {
return nil,
}
if .disallow != nil {
for , := range .src {
if .disallow.Contains(rune()) {
return nil, errDisallowedRune
}
}
}
if .options.disallowEmpty && len(.src) == 0 {
return nil, errEmptyString
}
return .src, nil
}
:= 1
if .options.repeat {
= 4
}
for ; > 0; -- {
if .options.foldWidth || (.options.ignorecase && ) {
.apply(foldWidthT)
}
for , := range .options.additional {
if = .apply(()); != nil {
return nil,
}
}
if .options.cases != nil {
.apply(.options.cases)
}
if && .options.ignorecase {
.apply(lowerCaseT)
}
.apply(.norm)
if .options.bidiRule && !bidirule.Valid(.src) {
return nil, bidirule.ErrInvalid
}
:= checker{p: }
if , := .span(.src, true); != nil {
return nil,
}
if .disallow != nil {
for := 0; < len(.src); {
, := utf8.DecodeRune(.src[:])
if .disallow.Contains() {
return nil, errDisallowedRune
}
+=
}
}
if .options.disallowEmpty && len(.src) == 0 {
return nil, errEmptyString
}
}
return .src, nil
}
func ( *Profile) (, []byte) ([]byte, error) {
var buffers
, := .enforce(, , false)
if != nil {
return nil,
}
return append(, ...), nil
}
func ( *Profile, []byte, bool) ([]byte, error) {
var buffers
, := .enforce(, , )
if != nil {
return nil,
}
if .next == 0 {
:= make([]byte, len())
copy(, )
return , nil
}
return , nil
}
func ( *Profile) ( []byte) ([]byte, error) {
return processBytes(, , false)
}
func ( *Profile) (, []byte) ([]byte, error) {
var buffers
, := .enforce(, , true)
if != nil {
return nil,
}
return append(, ...), nil
}
func ( *Profile, string, bool) (string, error) {
var buffers
, := .enforce(, []byte(), )
if != nil {
return "",
}
return string(), nil
}
func ( *Profile) ( string) (string, error) {
return processString(, , false)
}
func ( *Profile) ( string) (string, error) {
return processString(, , true)
}
func ( *Profile) (, string) bool {
var buffers
, := .enforce(, []byte(), true)
if != nil {
return false
}
= buffers{}
, := .enforce(, []byte(), true)
if != nil {
return false
}
return bytes.Equal(, )
}
func ( *Profile) () runes.Set {
if .options.disallow != nil {
return runes.Predicate(func( rune) bool {
return .class.Contains() && !.options.disallow.Contains()
})
}
return .class
}
type checker struct {
p *Profile
allowed runes.Set
beforeBits catBitmap
termBits catBitmap
acceptBits catBitmap
}
func ( *checker) () {
.beforeBits = 0
.termBits = 0
.acceptBits = 0
}
func ( *checker) ( []byte, bool) ( int, error) {
for < len() {
, := dpTrie.lookup([:])
:= categoryTransitions[category(&catMask)]
if == 0 {
if ! {
return , transform.ErrShortSrc
}
return , errDisallowedRune
}
:= false
if property() < .p.class.validFrom {
if .rule == nil {
return , errDisallowedRune
}
, = .rule(.beforeBits)
if != nil {
return ,
}
}
.beforeBits &= .keep
.beforeBits |= .set
if .termBits != 0 {
if .beforeBits&.termBits != 0 {
.termBits = 0
.acceptBits = 0
} else if .beforeBits&.acceptBits == 0 {
return , errContext
}
}
if {
if .termBits != 0 {
return , errContext
}
.termBits = .term
.acceptBits = .accept
}
+=
}
if := .beforeBits >> finalShift; .beforeBits& != || .termBits != 0 {
= errContext
}
return ,
}
func ( checker) (, []byte, bool) (, int, error) {
:= false
if len() < len() {
= [:len()]
= false
= true
}
, = .span(, )
= copy(, [:])
if && ( == transform.ErrShortSrc || == nil) {
= transform.ErrShortDst
}
return , ,
}