github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/lib/encoder/internal/gen/main.go (about)

     1  // +build go1.10
     2  
     3  package main
     4  
     5  import (
     6  	"fmt"
     7  	"log"
     8  	"math/rand"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/ncw/rclone/lib/encoder"
    14  )
    15  
    16  const (
    17  	edgeLeft = iota
    18  	edgeRight
    19  )
    20  
    21  type mapping struct {
    22  	mask     uint
    23  	src, dst []rune
    24  }
    25  type stringPair struct {
    26  	a, b string
    27  }
    28  
    29  const header = `// Code generated by ./internal/gen/main.go. DO NOT EDIT.
    30  
    31  ` + `//go:generate go run ./internal/gen/main.go
    32  
    33  package encoder
    34  
    35  `
    36  
    37  var maskBits = []struct {
    38  	mask uint
    39  	name string
    40  }{
    41  	{encoder.EncodeZero, "EncodeZero"},
    42  	{encoder.EncodeWin, "EncodeWin"},
    43  	{encoder.EncodeSlash, "EncodeSlash"},
    44  	{encoder.EncodeBackSlash, "EncodeBackSlash"},
    45  	{encoder.EncodeHashPercent, "EncodeHashPercent"},
    46  	{encoder.EncodeDel, "EncodeDel"},
    47  	{encoder.EncodeCtl, "EncodeCtl"},
    48  	{encoder.EncodeLeftSpace, "EncodeLeftSpace"},
    49  	{encoder.EncodeLeftTilde, "EncodeLeftTilde"},
    50  	{encoder.EncodeRightSpace, "EncodeRightSpace"},
    51  	{encoder.EncodeRightPeriod, "EncodeRightPeriod"},
    52  	{encoder.EncodeInvalidUtf8, "EncodeInvalidUtf8"},
    53  }
    54  var edges = []struct {
    55  	mask    uint
    56  	name    string
    57  	edge    int
    58  	orig    rune
    59  	replace rune
    60  }{
    61  	{encoder.EncodeLeftSpace, "EncodeLeftSpace", edgeLeft, ' ', '␠'},
    62  	{encoder.EncodeLeftTilde, "EncodeLeftTilde", edgeLeft, '~', '~'},
    63  	{encoder.EncodeRightSpace, "EncodeRightSpace", edgeRight, ' ', '␠'},
    64  	{encoder.EncodeRightPeriod, "EncodeRightPeriod", edgeRight, '.', '.'},
    65  }
    66  
    67  var allMappings = []mapping{{
    68  	encoder.EncodeZero, []rune{
    69  		0,
    70  	}, []rune{
    71  		'␀',
    72  	}}, {
    73  	encoder.EncodeWin, []rune{
    74  		':', '?', '"', '*', '<', '>', '|',
    75  	}, []rune{
    76  		':', '?', '"', '*', '<', '>', '|',
    77  	}}, {
    78  	encoder.EncodeSlash, []rune{
    79  		'/',
    80  	}, []rune{
    81  		'/',
    82  	}}, {
    83  	encoder.EncodeBackSlash, []rune{
    84  		'\\',
    85  	}, []rune{
    86  		'\',
    87  	}}, {
    88  	encoder.EncodeHashPercent, []rune{
    89  		'#', '%',
    90  	}, []rune{
    91  		'#', '%',
    92  	}}, {
    93  	encoder.EncodeDel, []rune{
    94  		0x7F,
    95  	}, []rune{
    96  		'␡',
    97  	}}, {
    98  	encoder.EncodeCtl,
    99  	runeRange(0x01, 0x1F),
   100  	runeRange('␁', '␟'),
   101  }}
   102  
   103  var (
   104  	rng = rand.New(rand.NewSource(42))
   105  
   106  	printables          = runeRange(0x20, 0x7E)
   107  	fullwidthPrintables = runeRange(0xFF00, 0xFF5E)
   108  	encodables          = collectEncodables(allMappings)
   109  	encoded             = collectEncoded(allMappings)
   110  	greek               = runeRange(0x03B1, 0x03C9)
   111  )
   112  
   113  func main() {
   114  	fd, err := os.Create("encoder_cases_test.go")
   115  	fatal(err, "Unable to open encoder_cases_test.go:")
   116  	defer func() {
   117  		fatal(fd.Close(), "Failed to close encoder_cases_test.go:")
   118  	}()
   119  	fatalW(fd.WriteString(header))("Failed to write header:")
   120  
   121  	fatalW(fd.WriteString("var testCasesSingle = []testCase{\n\t"))("Write:")
   122  	_i := 0
   123  	i := func() (r int) {
   124  		r, _i = _i, _i+1
   125  		return
   126  	}
   127  	for _, m := range maskBits {
   128  		if len(getMapping(m.mask).src) == 0 {
   129  			continue
   130  		}
   131  		if _i != 0 {
   132  			fatalW(fd.WriteString(" "))("Write:")
   133  		}
   134  		in, out := buildTestString(
   135  			[]mapping{getMapping(m.mask)},                               // pick
   136  			[]mapping{getMapping(0)},                                    // quote
   137  			printables, fullwidthPrintables, encodables, encoded, greek) // fill
   138  		fatalW(fmt.Fprintf(fd, `{ // %d
   139  		mask: %s,
   140  		in:   %s,
   141  		out:  %s,
   142  	},`, i(), m.name, strconv.Quote(in), strconv.Quote(out)))("Error writing test case:")
   143  	}
   144  	fatalW(fd.WriteString(`
   145  }
   146  
   147  var testCasesSingleEdge = []testCase{
   148  	`))("Write:")
   149  	_i = 0
   150  	for _, e := range edges {
   151  		if _i != 0 {
   152  			fatalW(fd.WriteString(" "))("Write:")
   153  		}
   154  		fatalW(fmt.Fprintf(fd, `{ // %d
   155  		mask: %s,
   156  		in:   %s,
   157  		out:  %s,
   158  	},`, i(), e.name, strconv.Quote(string(e.orig)), strconv.Quote(string(e.replace))))("Error writing test case:")
   159  		for _, m := range maskBits {
   160  			if len(getMapping(m.mask).src) == 0 {
   161  				continue
   162  			}
   163  			pairs := buildEdgeTestString(
   164  				e.edge, e.orig, e.replace,
   165  				[]mapping{getMapping(0), getMapping(m.mask)}, // quote
   166  				printables, fullwidthPrintables, encodables, encoded, greek) // fill
   167  			for _, p := range pairs {
   168  				fatalW(fmt.Fprintf(fd, ` { // %d
   169  		mask: %s | %s,
   170  		in:   %s,
   171  		out:  %s,
   172  	},`, i(), m.name, e.name, strconv.Quote(p.a), strconv.Quote(p.b)))("Error writing test case:")
   173  			}
   174  		}
   175  	}
   176  	fatalW(fmt.Fprintf(fd, ` { // %d
   177  		mask: EncodeLeftSpace,
   178  		in:   "  ",
   179  		out:  "␠ ",
   180  	}, { // %d
   181  		mask: EncodeLeftTilde,
   182  		in:   "~~",
   183  		out:  "~~",
   184  	}, { // %d
   185  		mask: EncodeRightSpace,
   186  		in:   "  ",
   187  		out:  " ␠",
   188  	}, { // %d
   189  		mask: EncodeRightPeriod,
   190  		in:   "..",
   191  		out:  "..",
   192  	}, { // %d
   193  		mask: EncodeLeftSpace | EncodeRightPeriod,
   194  		in:   " .",
   195  		out:  "␠.",
   196  	}, { // %d
   197  		mask: EncodeLeftSpace | EncodeRightSpace,
   198  		in:   " ",
   199  		out:  "␠",
   200  	}, { // %d
   201  		mask: EncodeLeftSpace | EncodeRightSpace,
   202  		in:   "  ",
   203  		out:  "␠␠",
   204  	}, { // %d
   205  		mask: EncodeLeftSpace | EncodeRightSpace,
   206  		in:   "   ",
   207  		out:  "␠ ␠",
   208  	},
   209  }
   210  `, i(), i(), i(), i(), i(), i(), i(), i()))("Error writing test case:")
   211  }
   212  
   213  func fatal(err error, s ...interface{}) {
   214  	if err != nil {
   215  		log.Fatalln(append(s, err))
   216  	}
   217  }
   218  func fatalW(_ int, err error) func(...interface{}) {
   219  	if err != nil {
   220  		return func(s ...interface{}) {
   221  			log.Fatalln(append(s, err))
   222  		}
   223  	}
   224  	return func(s ...interface{}) {}
   225  }
   226  
   227  // construct a slice containing the runes between (l)ow (inclusive) and (h)igh (inclusive)
   228  func runeRange(l, h rune) []rune {
   229  	if h < l {
   230  		panic("invalid range")
   231  	}
   232  	out := make([]rune, h-l+1)
   233  	for i := range out {
   234  		out[i] = l + rune(i)
   235  	}
   236  	return out
   237  }
   238  
   239  func getMapping(mask uint) mapping {
   240  	for _, m := range allMappings {
   241  		if m.mask == mask {
   242  			return m
   243  		}
   244  	}
   245  	return mapping{}
   246  }
   247  func collectEncodables(m []mapping) (out []rune) {
   248  	for _, s := range m {
   249  		for _, r := range s.src {
   250  			out = append(out, r)
   251  		}
   252  	}
   253  	return
   254  }
   255  func collectEncoded(m []mapping) (out []rune) {
   256  	for _, s := range m {
   257  		for _, r := range s.dst {
   258  			out = append(out, r)
   259  		}
   260  	}
   261  	return
   262  }
   263  
   264  func buildTestString(mappings, testMappings []mapping, fill ...[]rune) (string, string) {
   265  	combinedMappings := append(mappings, testMappings...)
   266  	var (
   267  		rIn  []rune
   268  		rOut []rune
   269  	)
   270  	for _, m := range mappings {
   271  		if len(m.src) == 0 || len(m.src) != len(m.dst) {
   272  			panic("invalid length")
   273  		}
   274  		rIn = append(rIn, m.src...)
   275  		rOut = append(rOut, m.dst...)
   276  	}
   277  	inL := len(rIn)
   278  	testL := inL * 3
   279  	if testL < 30 {
   280  		testL = 30
   281  	}
   282  	rIn = append(rIn, make([]rune, testL-inL)...)
   283  	rOut = append(rOut, make([]rune, testL-inL)...)
   284  	quoteOut := make([]bool, testL)
   285  	set := func(i int, in, out rune, quote bool) {
   286  		rIn[i] = in
   287  		rOut[i] = out
   288  		quoteOut[i] = quote
   289  	}
   290  	for i, r := range rOut[:inL] {
   291  		set(inL+i, r, r, true)
   292  	}
   293  
   294  outer:
   295  	for pos := inL * 2; pos < testL; pos++ {
   296  		m := pos % len(fill)
   297  		i := rng.Intn(len(fill[m]))
   298  		r := fill[m][i]
   299  		for _, m := range combinedMappings {
   300  			if pSrc := runePos(r, m.src); pSrc != -1 {
   301  				set(pos, r, m.dst[pSrc], false)
   302  				continue outer
   303  			} else if pDst := runePos(r, m.dst); pDst != -1 {
   304  				set(pos, r, r, true)
   305  				continue outer
   306  			}
   307  		}
   308  		set(pos, r, r, false)
   309  	}
   310  
   311  	rng.Shuffle(testL, func(i, j int) {
   312  		rIn[i], rIn[j] = rIn[j], rIn[i]
   313  		rOut[i], rOut[j] = rOut[j], rOut[i]
   314  		quoteOut[i], quoteOut[j] = quoteOut[j], quoteOut[i]
   315  	})
   316  
   317  	var bOut strings.Builder
   318  	bOut.Grow(testL)
   319  	for i, r := range rOut {
   320  		if quoteOut[i] {
   321  			bOut.WriteRune(encoder.QuoteRune)
   322  		}
   323  		bOut.WriteRune(r)
   324  	}
   325  	return string(rIn), bOut.String()
   326  }
   327  
   328  func buildEdgeTestString(edge int, orig, replace rune, testMappings []mapping, fill ...[]rune) (out []stringPair) {
   329  	testL := 30
   330  	rIn := make([]rune, testL)
   331  	rOut := make([]rune, testL)
   332  	quoteOut := make([]bool, testL)
   333  
   334  	set := func(i int, in, out rune, quote bool) {
   335  		rIn[i] = in
   336  		rOut[i] = out
   337  		quoteOut[i] = quote
   338  	}
   339  
   340  outer:
   341  	for pos := 0; pos < testL; pos++ {
   342  		m := pos % len(fill)
   343  		i := rng.Intn(len(fill[m]))
   344  		r := fill[m][i]
   345  		for _, m := range testMappings {
   346  			if pSrc := runePos(r, m.src); pSrc != -1 {
   347  				set(pos, r, m.dst[pSrc], false)
   348  				continue outer
   349  			} else if pDst := runePos(r, m.dst); pDst != -1 {
   350  				set(pos, r, r, true)
   351  				continue outer
   352  			}
   353  		}
   354  		set(pos, r, r, false)
   355  	}
   356  
   357  	rng.Shuffle(testL, func(i, j int) {
   358  		rIn[i], rIn[j] = rIn[j], rIn[i]
   359  		rOut[i], rOut[j] = rOut[j], rOut[i]
   360  		quoteOut[i], quoteOut[j] = quoteOut[j], quoteOut[i]
   361  	})
   362  	set(10, orig, orig, false)
   363  
   364  	out = append(out, stringPair{string(rIn), quotedToString(rOut, quoteOut)})
   365  	for _, i := range []int{0, 1, testL - 2, testL - 1} {
   366  		for _, j := range []int{1, testL - 2, testL - 1} {
   367  			if j < i {
   368  				continue
   369  			}
   370  			rIn := append([]rune{}, rIn...)
   371  			rOut := append([]rune{}, rOut...)
   372  			quoteOut := append([]bool{}, quoteOut...)
   373  
   374  			for _, in := range []rune{orig, replace} {
   375  				expect, quote := in, false
   376  				if i == 0 && edge == edgeLeft ||
   377  					i == testL-1 && edge == edgeRight {
   378  					expect, quote = replace, in == replace
   379  				}
   380  				rIn[i], rOut[i], quoteOut[i] = in, expect, quote
   381  
   382  				if i != j {
   383  					for _, in := range []rune{orig, replace} {
   384  						expect, quote = in, false
   385  						if j == testL-1 && edge == edgeRight {
   386  							expect, quote = replace, in == replace
   387  						}
   388  						rIn[j], rOut[j], quoteOut[j] = in, expect, quote
   389  					}
   390  				}
   391  				out = append(out, stringPair{string(rIn), quotedToString(rOut, quoteOut)})
   392  			}
   393  		}
   394  	}
   395  	return
   396  }
   397  
   398  func runePos(r rune, s []rune) int {
   399  	for i, c := range s {
   400  		if c == r {
   401  			return i
   402  		}
   403  	}
   404  	return -1
   405  }
   406  
   407  // quotedToString returns a string for the chars slice where a encoder.QuoteRune is
   408  // inserted before a char[i] when quoted[i] is true.
   409  func quotedToString(chars []rune, quoted []bool) string {
   410  	var out strings.Builder
   411  	out.Grow(len(chars))
   412  	for i, r := range chars {
   413  		if quoted[i] {
   414  			out.WriteRune(encoder.QuoteRune)
   415  		}
   416  		out.WriteRune(r)
   417  	}
   418  	return out.String()
   419  }