rclone/lib/encoder/internal/gen/main.go
Nick Craig-Wood 57d5de6fba build: fix up package paths after repo move
git grep -l github.com/ncw/rclone | xargs -d'\n' perl -i~ -lpe 's|github.com/ncw/rclone|github.com/rclone/rclone|g'
goimports -w `find . -name \*.go`
2019-07-28 18:47:38 +01:00

420 lines
9.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// +build go1.10
package main
import (
"fmt"
"log"
"math/rand"
"os"
"strconv"
"strings"
"github.com/rclone/rclone/lib/encoder"
)
const (
edgeLeft = iota
edgeRight
)
type mapping struct {
mask uint
src, dst []rune
}
type stringPair struct {
a, b string
}
const header = `// Code generated by ./internal/gen/main.go. DO NOT EDIT.
` + `//go:generate go run ./internal/gen/main.go
package encoder
`
var maskBits = []struct {
mask uint
name string
}{
{encoder.EncodeZero, "EncodeZero"},
{encoder.EncodeWin, "EncodeWin"},
{encoder.EncodeSlash, "EncodeSlash"},
{encoder.EncodeBackSlash, "EncodeBackSlash"},
{encoder.EncodeHashPercent, "EncodeHashPercent"},
{encoder.EncodeDel, "EncodeDel"},
{encoder.EncodeCtl, "EncodeCtl"},
{encoder.EncodeLeftSpace, "EncodeLeftSpace"},
{encoder.EncodeLeftTilde, "EncodeLeftTilde"},
{encoder.EncodeRightSpace, "EncodeRightSpace"},
{encoder.EncodeRightPeriod, "EncodeRightPeriod"},
{encoder.EncodeInvalidUtf8, "EncodeInvalidUtf8"},
}
var edges = []struct {
mask uint
name string
edge int
orig rune
replace rune
}{
{encoder.EncodeLeftSpace, "EncodeLeftSpace", edgeLeft, ' ', '␠'},
{encoder.EncodeLeftTilde, "EncodeLeftTilde", edgeLeft, '~', ''},
{encoder.EncodeRightSpace, "EncodeRightSpace", edgeRight, ' ', '␠'},
{encoder.EncodeRightPeriod, "EncodeRightPeriod", edgeRight, '.', ''},
}
var allMappings = []mapping{{
encoder.EncodeZero, []rune{
0,
}, []rune{
'␀',
}}, {
encoder.EncodeWin, []rune{
':', '?', '"', '*', '<', '>', '|',
}, []rune{
'', '', '', '', '', '', '',
}}, {
encoder.EncodeSlash, []rune{
'/',
}, []rune{
'',
}}, {
encoder.EncodeBackSlash, []rune{
'\\',
}, []rune{
'',
}}, {
encoder.EncodeHashPercent, []rune{
'#', '%',
}, []rune{
'', '',
}}, {
encoder.EncodeDel, []rune{
0x7F,
}, []rune{
'␡',
}}, {
encoder.EncodeCtl,
runeRange(0x01, 0x1F),
runeRange('␁', '␟'),
}}
var (
rng = rand.New(rand.NewSource(42))
printables = runeRange(0x20, 0x7E)
fullwidthPrintables = runeRange(0xFF00, 0xFF5E)
encodables = collectEncodables(allMappings)
encoded = collectEncoded(allMappings)
greek = runeRange(0x03B1, 0x03C9)
)
func main() {
fd, err := os.Create("encoder_cases_test.go")
fatal(err, "Unable to open encoder_cases_test.go:")
defer func() {
fatal(fd.Close(), "Failed to close encoder_cases_test.go:")
}()
fatalW(fd.WriteString(header))("Failed to write header:")
fatalW(fd.WriteString("var testCasesSingle = []testCase{\n\t"))("Write:")
_i := 0
i := func() (r int) {
r, _i = _i, _i+1
return
}
for _, m := range maskBits {
if len(getMapping(m.mask).src) == 0 {
continue
}
if _i != 0 {
fatalW(fd.WriteString(" "))("Write:")
}
in, out := buildTestString(
[]mapping{getMapping(m.mask)}, // pick
[]mapping{getMapping(0)}, // quote
printables, fullwidthPrintables, encodables, encoded, greek) // fill
fatalW(fmt.Fprintf(fd, `{ // %d
mask: %s,
in: %s,
out: %s,
},`, i(), m.name, strconv.Quote(in), strconv.Quote(out)))("Error writing test case:")
}
fatalW(fd.WriteString(`
}
var testCasesSingleEdge = []testCase{
`))("Write:")
_i = 0
for _, e := range edges {
if _i != 0 {
fatalW(fd.WriteString(" "))("Write:")
}
fatalW(fmt.Fprintf(fd, `{ // %d
mask: %s,
in: %s,
out: %s,
},`, i(), e.name, strconv.Quote(string(e.orig)), strconv.Quote(string(e.replace))))("Error writing test case:")
for _, m := range maskBits {
if len(getMapping(m.mask).src) == 0 {
continue
}
pairs := buildEdgeTestString(
e.edge, e.orig, e.replace,
[]mapping{getMapping(0), getMapping(m.mask)}, // quote
printables, fullwidthPrintables, encodables, encoded, greek) // fill
for _, p := range pairs {
fatalW(fmt.Fprintf(fd, ` { // %d
mask: %s | %s,
in: %s,
out: %s,
},`, i(), m.name, e.name, strconv.Quote(p.a), strconv.Quote(p.b)))("Error writing test case:")
}
}
}
fatalW(fmt.Fprintf(fd, ` { // %d
mask: EncodeLeftSpace,
in: " ",
out: "␠ ",
}, { // %d
mask: EncodeLeftTilde,
in: "~~",
out: "~",
}, { // %d
mask: EncodeRightSpace,
in: " ",
out: " ␠",
}, { // %d
mask: EncodeRightPeriod,
in: "..",
out: ".",
}, { // %d
mask: EncodeLeftSpace | EncodeRightPeriod,
in: " .",
out: "␠.",
}, { // %d
mask: EncodeLeftSpace | EncodeRightSpace,
in: " ",
out: "␠",
}, { // %d
mask: EncodeLeftSpace | EncodeRightSpace,
in: " ",
out: "␠␠",
}, { // %d
mask: EncodeLeftSpace | EncodeRightSpace,
in: " ",
out: "␠ ␠",
},
}
`, i(), i(), i(), i(), i(), i(), i(), i()))("Error writing test case:")
}
func fatal(err error, s ...interface{}) {
if err != nil {
log.Fatalln(append(s, err))
}
}
func fatalW(_ int, err error) func(...interface{}) {
if err != nil {
return func(s ...interface{}) {
log.Fatalln(append(s, err))
}
}
return func(s ...interface{}) {}
}
// construct a slice containing the runes between (l)ow (inclusive) and (h)igh (inclusive)
func runeRange(l, h rune) []rune {
if h < l {
panic("invalid range")
}
out := make([]rune, h-l+1)
for i := range out {
out[i] = l + rune(i)
}
return out
}
func getMapping(mask uint) mapping {
for _, m := range allMappings {
if m.mask == mask {
return m
}
}
return mapping{}
}
func collectEncodables(m []mapping) (out []rune) {
for _, s := range m {
for _, r := range s.src {
out = append(out, r)
}
}
return
}
func collectEncoded(m []mapping) (out []rune) {
for _, s := range m {
for _, r := range s.dst {
out = append(out, r)
}
}
return
}
func buildTestString(mappings, testMappings []mapping, fill ...[]rune) (string, string) {
combinedMappings := append(mappings, testMappings...)
var (
rIn []rune
rOut []rune
)
for _, m := range mappings {
if len(m.src) == 0 || len(m.src) != len(m.dst) {
panic("invalid length")
}
rIn = append(rIn, m.src...)
rOut = append(rOut, m.dst...)
}
inL := len(rIn)
testL := inL * 3
if testL < 30 {
testL = 30
}
rIn = append(rIn, make([]rune, testL-inL)...)
rOut = append(rOut, make([]rune, testL-inL)...)
quoteOut := make([]bool, testL)
set := func(i int, in, out rune, quote bool) {
rIn[i] = in
rOut[i] = out
quoteOut[i] = quote
}
for i, r := range rOut[:inL] {
set(inL+i, r, r, true)
}
outer:
for pos := inL * 2; pos < testL; pos++ {
m := pos % len(fill)
i := rng.Intn(len(fill[m]))
r := fill[m][i]
for _, m := range combinedMappings {
if pSrc := runePos(r, m.src); pSrc != -1 {
set(pos, r, m.dst[pSrc], false)
continue outer
} else if pDst := runePos(r, m.dst); pDst != -1 {
set(pos, r, r, true)
continue outer
}
}
set(pos, r, r, false)
}
rng.Shuffle(testL, func(i, j int) {
rIn[i], rIn[j] = rIn[j], rIn[i]
rOut[i], rOut[j] = rOut[j], rOut[i]
quoteOut[i], quoteOut[j] = quoteOut[j], quoteOut[i]
})
var bOut strings.Builder
bOut.Grow(testL)
for i, r := range rOut {
if quoteOut[i] {
bOut.WriteRune(encoder.QuoteRune)
}
bOut.WriteRune(r)
}
return string(rIn), bOut.String()
}
func buildEdgeTestString(edge int, orig, replace rune, testMappings []mapping, fill ...[]rune) (out []stringPair) {
testL := 30
rIn := make([]rune, testL)
rOut := make([]rune, testL)
quoteOut := make([]bool, testL)
set := func(i int, in, out rune, quote bool) {
rIn[i] = in
rOut[i] = out
quoteOut[i] = quote
}
outer:
for pos := 0; pos < testL; pos++ {
m := pos % len(fill)
i := rng.Intn(len(fill[m]))
r := fill[m][i]
for _, m := range testMappings {
if pSrc := runePos(r, m.src); pSrc != -1 {
set(pos, r, m.dst[pSrc], false)
continue outer
} else if pDst := runePos(r, m.dst); pDst != -1 {
set(pos, r, r, true)
continue outer
}
}
set(pos, r, r, false)
}
rng.Shuffle(testL, func(i, j int) {
rIn[i], rIn[j] = rIn[j], rIn[i]
rOut[i], rOut[j] = rOut[j], rOut[i]
quoteOut[i], quoteOut[j] = quoteOut[j], quoteOut[i]
})
set(10, orig, orig, false)
out = append(out, stringPair{string(rIn), quotedToString(rOut, quoteOut)})
for _, i := range []int{0, 1, testL - 2, testL - 1} {
for _, j := range []int{1, testL - 2, testL - 1} {
if j < i {
continue
}
rIn := append([]rune{}, rIn...)
rOut := append([]rune{}, rOut...)
quoteOut := append([]bool{}, quoteOut...)
for _, in := range []rune{orig, replace} {
expect, quote := in, false
if i == 0 && edge == edgeLeft ||
i == testL-1 && edge == edgeRight {
expect, quote = replace, in == replace
}
rIn[i], rOut[i], quoteOut[i] = in, expect, quote
if i != j {
for _, in := range []rune{orig, replace} {
expect, quote = in, false
if j == testL-1 && edge == edgeRight {
expect, quote = replace, in == replace
}
rIn[j], rOut[j], quoteOut[j] = in, expect, quote
}
}
out = append(out, stringPair{string(rIn), quotedToString(rOut, quoteOut)})
}
}
}
return
}
func runePos(r rune, s []rune) int {
for i, c := range s {
if c == r {
return i
}
}
return -1
}
// quotedToString returns a string for the chars slice where a encoder.QuoteRune is
// inserted before a char[i] when quoted[i] is true.
func quotedToString(chars []rune, quoted []bool) string {
var out strings.Builder
out.Grow(len(chars))
for i, r := range chars {
if quoted[i] {
out.WriteRune(encoder.QuoteRune)
}
out.WriteRune(r)
}
return out.String()
}