github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/lib/encoder/internal/gen/main.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"log"
     7  	"math/rand"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/rclone/rclone/lib/encoder"
    13  )
    14  
    15  const (
    16  	edgeLeft = iota
    17  	edgeRight
    18  )
    19  
    20  type mapping struct {
    21  	mask     encoder.MultiEncoder
    22  	src, dst []rune
    23  }
    24  type stringPair struct {
    25  	a, b string
    26  }
    27  
    28  const header = `// Code generated by ./internal/gen/main.go. DO NOT EDIT.
    29  
    30  ` + `//go:generate go run ./internal/gen/main.go
    31  
    32  package encoder
    33  
    34  `
    35  
    36  var maskBits = []struct {
    37  	mask encoder.MultiEncoder
    38  	name string
    39  }{
    40  	{encoder.EncodeZero, "EncodeZero"},
    41  	{encoder.EncodeSlash, "EncodeSlash"},
    42  	{encoder.EncodeSingleQuote, "EncodeSingleQuote"},
    43  	{encoder.EncodeBackQuote, "EncodeBackQuote"},
    44  	{encoder.EncodeLtGt, "EncodeLtGt"},
    45  	{encoder.EncodeDollar, "EncodeDollar"},
    46  	{encoder.EncodeDoubleQuote, "EncodeDoubleQuote"},
    47  	{encoder.EncodeColon, "EncodeColon"},
    48  	{encoder.EncodeQuestion, "EncodeQuestion"},
    49  	{encoder.EncodeAsterisk, "EncodeAsterisk"},
    50  	{encoder.EncodePipe, "EncodePipe"},
    51  	{encoder.EncodeHash, "EncodeHash"},
    52  	{encoder.EncodePercent, "EncodePercent"},
    53  	{encoder.EncodeBackSlash, "EncodeBackSlash"},
    54  	{encoder.EncodeCrLf, "EncodeCrLf"},
    55  	{encoder.EncodeDel, "EncodeDel"},
    56  	{encoder.EncodeCtl, "EncodeCtl"},
    57  	{encoder.EncodeLeftSpace, "EncodeLeftSpace"},
    58  	{encoder.EncodeLeftPeriod, "EncodeLeftPeriod"},
    59  	{encoder.EncodeLeftTilde, "EncodeLeftTilde"},
    60  	{encoder.EncodeLeftCrLfHtVt, "EncodeLeftCrLfHtVt"},
    61  	{encoder.EncodeRightSpace, "EncodeRightSpace"},
    62  	{encoder.EncodeRightPeriod, "EncodeRightPeriod"},
    63  	{encoder.EncodeRightCrLfHtVt, "EncodeRightCrLfHtVt"},
    64  	{encoder.EncodeInvalidUtf8, "EncodeInvalidUtf8"},
    65  	{encoder.EncodeDot, "EncodeDot"},
    66  }
    67  
    68  type edge struct {
    69  	mask    encoder.MultiEncoder
    70  	name    string
    71  	edge    int
    72  	orig    []rune
    73  	replace []rune
    74  }
    75  
    76  var allEdges = []edge{
    77  	{encoder.EncodeLeftSpace, "EncodeLeftSpace", edgeLeft, []rune{' '}, []rune{'␠'}},
    78  	{encoder.EncodeLeftPeriod, "EncodeLeftPeriod", edgeLeft, []rune{'.'}, []rune{'.'}},
    79  	{encoder.EncodeLeftTilde, "EncodeLeftTilde", edgeLeft, []rune{'~'}, []rune{'~'}},
    80  	{encoder.EncodeLeftCrLfHtVt, "EncodeLeftCrLfHtVt", edgeLeft,
    81  		[]rune{'\t', '\n', '\v', '\r'},
    82  		[]rune{'␀' + '\t', '␀' + '\n', '␀' + '\v', '␀' + '\r'},
    83  	},
    84  	{encoder.EncodeRightSpace, "EncodeRightSpace", edgeRight, []rune{' '}, []rune{'␠'}},
    85  	{encoder.EncodeRightPeriod, "EncodeRightPeriod", edgeRight, []rune{'.'}, []rune{'.'}},
    86  	{encoder.EncodeRightCrLfHtVt, "EncodeRightCrLfHtVt", edgeRight,
    87  		[]rune{'\t', '\n', '\v', '\r'},
    88  		[]rune{'␀' + '\t', '␀' + '\n', '␀' + '\v', '␀' + '\r'},
    89  	},
    90  }
    91  
    92  var allMappings = []mapping{{
    93  	encoder.EncodeZero, []rune{
    94  		0,
    95  	}, []rune{
    96  		'␀',
    97  	}}, {
    98  	encoder.EncodeSlash, []rune{
    99  		'/',
   100  	}, []rune{
   101  		'/',
   102  	}}, {
   103  	encoder.EncodeLtGt, []rune{
   104  		'<', '>',
   105  	}, []rune{
   106  		'<', '>',
   107  	}}, {
   108  	encoder.EncodeDoubleQuote, []rune{
   109  		'"',
   110  	}, []rune{
   111  		'"',
   112  	}}, {
   113  	encoder.EncodeSingleQuote, []rune{
   114  		'\'',
   115  	}, []rune{
   116  		''',
   117  	}}, {
   118  	encoder.EncodeBackQuote, []rune{
   119  		'`',
   120  	}, []rune{
   121  		'`',
   122  	}}, {
   123  	encoder.EncodeDollar, []rune{
   124  		'$',
   125  	}, []rune{
   126  		'$',
   127  	}}, {
   128  	encoder.EncodeColon, []rune{
   129  		':',
   130  	}, []rune{
   131  		':',
   132  	}}, {
   133  	encoder.EncodeQuestion, []rune{
   134  		'?',
   135  	}, []rune{
   136  		'?',
   137  	}}, {
   138  	encoder.EncodeAsterisk, []rune{
   139  		'*',
   140  	}, []rune{
   141  		'*',
   142  	}}, {
   143  	encoder.EncodePipe, []rune{
   144  		'|',
   145  	}, []rune{
   146  		'|',
   147  	}}, {
   148  	encoder.EncodeHash, []rune{
   149  		'#',
   150  	}, []rune{
   151  		'#',
   152  	}}, {
   153  	encoder.EncodePercent, []rune{
   154  		'%',
   155  	}, []rune{
   156  		'%',
   157  	}}, {
   158  	encoder.EncodeSlash, []rune{
   159  		'/',
   160  	}, []rune{
   161  		'/',
   162  	}}, {
   163  	encoder.EncodeBackSlash, []rune{
   164  		'\\',
   165  	}, []rune{
   166  		'\',
   167  	}}, {
   168  	encoder.EncodeCrLf, []rune{
   169  		rune(0x0D), rune(0x0A),
   170  	}, []rune{
   171  		'␍', '␊',
   172  	}}, {
   173  	encoder.EncodeDel, []rune{
   174  		0x7F,
   175  	}, []rune{
   176  		'␡',
   177  	}}, {
   178  	encoder.EncodeCtl,
   179  	runeRange(0x01, 0x1F),
   180  	runeRange('␁', '␟'),
   181  }}
   182  
   183  var (
   184  	rng *rand.Rand
   185  
   186  	printables          = runeRange(0x20, 0x7E)
   187  	fullwidthPrintables = runeRange(0xFF00, 0xFF5E)
   188  	encodables          = collectEncodables(allMappings)
   189  	encoded             = collectEncoded(allMappings)
   190  	greek               = runeRange(0x03B1, 0x03C9)
   191  )
   192  
   193  func main() {
   194  	seed := flag.Int64("s", 42, "random seed")
   195  	flag.Parse()
   196  	rng = rand.New(rand.NewSource(*seed))
   197  
   198  	fd, err := os.Create("encoder_cases_test.go")
   199  	fatal(err, "Unable to open encoder_cases_test.go:")
   200  	defer func() {
   201  		fatal(fd.Close(), "Failed to close encoder_cases_test.go:")
   202  	}()
   203  	fatalW(fd.WriteString(header))("Failed to write header:")
   204  
   205  	fatalW(fd.WriteString("var testCasesSingle = []testCase{\n\t"))("Write:")
   206  	_i := 0
   207  	i := func() (r int) {
   208  		r, _i = _i, _i+1
   209  		return
   210  	}
   211  	for _, m := range maskBits {
   212  		if len(getMapping(m.mask).src) == 0 {
   213  			continue
   214  		}
   215  		if _i != 0 {
   216  			fatalW(fd.WriteString(" "))("Write:")
   217  		}
   218  		in, out := buildTestString(
   219  			[]mapping{getMapping(m.mask)},                               // pick
   220  			[]mapping{getMapping(0)},                                    // quote
   221  			printables, fullwidthPrintables, encodables, encoded, greek) // fill
   222  		fatalW(fmt.Fprintf(fd, `{ // %d
   223  		mask: %s,
   224  		in:   %s,
   225  		out:  %s,
   226  	},`, i(), m.name, strconv.Quote(in), strconv.Quote(out)))("Error writing test case:")
   227  	}
   228  	fatalW(fd.WriteString(`
   229  }
   230  
   231  var testCasesSingleEdge = []testCase{
   232  	`))("Write:")
   233  	_i = 0
   234  	for _, e := range allEdges {
   235  		for idx, orig := range e.orig {
   236  			if _i != 0 {
   237  				fatalW(fd.WriteString(" "))("Write:")
   238  			}
   239  			fatalW(fmt.Fprintf(fd, `{ // %d
   240  		mask: %s,
   241  		in:   %s,
   242  		out:  %s,
   243  	},`, i(), e.name, strconv.Quote(string(orig)), strconv.Quote(string(e.replace[idx]))))("Error writing test case:")
   244  		}
   245  		for _, m := range maskBits {
   246  			if len(getMapping(m.mask).src) == 0 || invalidMask(e.mask|m.mask) {
   247  				continue
   248  			}
   249  			for idx, orig := range e.orig {
   250  				replace := e.replace[idx]
   251  				pairs := buildEdgeTestString(
   252  					[]edge{e}, []mapping{getMapping(0), getMapping(m.mask)}, // quote
   253  					[][]rune{printables, fullwidthPrintables, encodables, encoded, greek}, // fill
   254  					func(rIn, rOut []rune, quoteOut []bool, testMappings []mapping) (out []stringPair) {
   255  						testL := len(rIn)
   256  						skipOrig := false
   257  						for _, m := range testMappings {
   258  							if runePos(orig, m.src) != -1 || runePos(orig, m.dst) != -1 {
   259  								skipOrig = true
   260  								break
   261  							}
   262  						}
   263  						if !skipOrig {
   264  							rIn[10], rOut[10], quoteOut[10] = orig, orig, false
   265  						}
   266  
   267  						out = append(out, stringPair{string(rIn), quotedToString(rOut, quoteOut)})
   268  						for _, i := range []int{0, 1, testL - 2, testL - 1} {
   269  							for _, j := range []int{1, testL - 2, testL - 1} {
   270  								if j < i {
   271  									continue
   272  								}
   273  								rIn := append([]rune{}, rIn...)
   274  								rOut := append([]rune{}, rOut...)
   275  								quoteOut := append([]bool{}, quoteOut...)
   276  
   277  								for _, in := range []rune{orig, replace} {
   278  									expect, quote := in, false
   279  									if i == 0 && e.edge == edgeLeft ||
   280  										i == testL-1 && e.edge == edgeRight {
   281  										expect, quote = replace, in == replace
   282  									}
   283  									rIn[i], rOut[i], quoteOut[i] = in, expect, quote
   284  
   285  									if i != j {
   286  										for _, in := range []rune{orig, replace} {
   287  											expect, quote = in, false
   288  											if j == testL-1 && e.edge == edgeRight {
   289  												expect, quote = replace, in == replace
   290  											}
   291  											rIn[j], rOut[j], quoteOut[j] = in, expect, quote
   292  										}
   293  									}
   294  									out = append(out, stringPair{string(rIn), quotedToString(rOut, quoteOut)})
   295  								}
   296  							}
   297  						}
   298  						return
   299  					})
   300  				for _, p := range pairs {
   301  					fatalW(fmt.Fprintf(fd, ` { // %d
   302  		mask: %s | %s,
   303  		in:   %s,
   304  		out:  %s,
   305  	},`, i(), m.name, e.name, strconv.Quote(p.a), strconv.Quote(p.b)))("Error writing test case:")
   306  				}
   307  			}
   308  		}
   309  	}
   310  	fatalW(fmt.Fprintf(fd, ` { // %d
   311  		mask: EncodeLeftSpace,
   312  		in:   "  ",
   313  		out:  "␠ ",
   314  	}, { // %d
   315  		mask: EncodeLeftPeriod,
   316  		in:   "..",
   317  		out:  "..",
   318  	}, { // %d
   319  		mask: EncodeLeftTilde,
   320  		in:   "~~",
   321  		out:  "~~",
   322  	}, { // %d
   323  		mask: EncodeRightSpace,
   324  		in:   "  ",
   325  		out:  " ␠",
   326  	}, { // %d
   327  		mask: EncodeRightPeriod,
   328  		in:   "..",
   329  		out:  "..",
   330  	}, { // %d
   331  		mask: EncodeLeftSpace | EncodeRightPeriod,
   332  		in:   " .",
   333  		out:  "␠.",
   334  	}, { // %d
   335  		mask: EncodeLeftSpace | EncodeRightSpace,
   336  		in:   " ",
   337  		out:  "␠",
   338  	}, { // %d
   339  		mask: EncodeLeftSpace | EncodeRightSpace,
   340  		in:   "  ",
   341  		out:  "␠␠",
   342  	}, { // %d
   343  		mask: EncodeLeftSpace | EncodeRightSpace,
   344  		in:   "   ",
   345  		out:  "␠ ␠",
   346  	}, { // %d
   347  		mask: EncodeLeftPeriod | EncodeRightPeriod,
   348  		in:   "...",
   349  		out:  "...",
   350  	}, { // %d
   351  		mask: EncodeRightPeriod | EncodeRightSpace,
   352  		in:   "a. ",
   353  		out:  "a.␠",
   354  	}, { // %d
   355  		mask: EncodeRightPeriod | EncodeRightSpace,
   356  		in:   "a .",
   357  		out:  "a .",
   358  	},
   359  }
   360  
   361  var testCasesDoubleEdge = []testCase{
   362  	`, i(), i(), i(), i(), i(), i(), i(), i(), i(), i(), i(), i()))("Error writing test case:")
   363  	_i = 0
   364  	for _, e1 := range allEdges {
   365  		for _, e2 := range allEdges {
   366  			if e1.mask == e2.mask {
   367  				continue
   368  			}
   369  			for _, m := range maskBits {
   370  				if len(getMapping(m.mask).src) == 0 || invalidMask(m.mask|e1.mask|e2.mask) {
   371  					continue
   372  				}
   373  				orig, replace := e1.orig[0], e1.replace[0]
   374  				edges := []edge{e1, e2}
   375  				pairs := buildEdgeTestString(
   376  					edges, []mapping{getMapping(0), getMapping(m.mask)}, // quote
   377  					[][]rune{printables, fullwidthPrintables, encodables, encoded, greek}, // fill
   378  					func(rIn, rOut []rune, quoteOut []bool, testMappings []mapping) (out []stringPair) {
   379  						testL := len(rIn)
   380  						for _, i := range []int{0, testL - 1} {
   381  							for _, secondOrig := range e2.orig {
   382  								rIn := append([]rune{}, rIn...)
   383  								rOut := append([]rune{}, rOut...)
   384  								quoteOut := append([]bool{}, quoteOut...)
   385  
   386  								rIn[1], rOut[1], quoteOut[1] = secondOrig, secondOrig, false
   387  								rIn[testL-2], rOut[testL-2], quoteOut[testL-2] = secondOrig, secondOrig, false
   388  
   389  								for _, in := range []rune{orig, replace} {
   390  									rIn[i], rOut[i], quoteOut[i] = in, in, false
   391  									fixEdges(rIn, rOut, quoteOut, edges)
   392  
   393  									out = append(out, stringPair{string(rIn), quotedToString(rOut, quoteOut)})
   394  								}
   395  							}
   396  						}
   397  						return
   398  					})
   399  
   400  				for _, p := range pairs {
   401  					if _i != 0 {
   402  						fatalW(fd.WriteString(" "))("Write:")
   403  					}
   404  					fatalW(fmt.Fprintf(fd, `{ // %d
   405  		mask: %s | %s | %s,
   406  		in:   %s,
   407  		out:  %s,
   408  	},`, i(), m.name, e1.name, e2.name, strconv.Quote(p.a), strconv.Quote(p.b)))("Error writing test case:")
   409  				}
   410  			}
   411  		}
   412  	}
   413  	fatalW(fmt.Fprint(fd, "\n}\n"))("Error writing test case:")
   414  }
   415  
   416  func fatal(err error, s ...interface{}) {
   417  	if err != nil {
   418  		log.Fatalln(append(s, err))
   419  	}
   420  }
   421  func fatalW(_ int, err error) func(...interface{}) {
   422  	if err != nil {
   423  		return func(s ...interface{}) {
   424  			log.Fatalln(append(s, err))
   425  		}
   426  	}
   427  	return func(s ...interface{}) {}
   428  }
   429  
   430  func invalidMask(mask encoder.MultiEncoder) bool {
   431  	return mask&(encoder.EncodeCtl|encoder.EncodeCrLf) != 0 && mask&(encoder.EncodeLeftCrLfHtVt|encoder.EncodeRightCrLfHtVt) != 0
   432  }
   433  
   434  // construct a slice containing the runes between (l)ow (inclusive) and (h)igh (inclusive)
   435  func runeRange(l, h rune) []rune {
   436  	if h < l {
   437  		panic("invalid range")
   438  	}
   439  	out := make([]rune, h-l+1)
   440  	for i := range out {
   441  		out[i] = l + rune(i)
   442  	}
   443  	return out
   444  }
   445  
   446  func getMapping(mask encoder.MultiEncoder) mapping {
   447  	for _, m := range allMappings {
   448  		if m.mask == mask {
   449  			return m
   450  		}
   451  	}
   452  	return mapping{}
   453  }
   454  func collectEncodables(m []mapping) (out []rune) {
   455  	for _, s := range m {
   456  		for _, r := range s.src {
   457  			out = append(out, r)
   458  		}
   459  	}
   460  	return
   461  }
   462  func collectEncoded(m []mapping) (out []rune) {
   463  	for _, s := range m {
   464  		for _, r := range s.dst {
   465  			out = append(out, r)
   466  		}
   467  	}
   468  	return
   469  }
   470  
   471  func buildTestString(mappings, testMappings []mapping, fill ...[]rune) (string, string) {
   472  	combinedMappings := append(mappings, testMappings...)
   473  	var (
   474  		rIn  []rune
   475  		rOut []rune
   476  	)
   477  	for _, m := range mappings {
   478  		if len(m.src) == 0 || len(m.src) != len(m.dst) {
   479  			panic("invalid length")
   480  		}
   481  		rIn = append(rIn, m.src...)
   482  		rOut = append(rOut, m.dst...)
   483  	}
   484  	inL := len(rIn)
   485  	testL := inL * 3
   486  	if testL < 30 {
   487  		testL = 30
   488  	}
   489  	rIn = append(rIn, make([]rune, testL-inL)...)
   490  	rOut = append(rOut, make([]rune, testL-inL)...)
   491  	quoteOut := make([]bool, testL)
   492  	set := func(i int, in, out rune, quote bool) {
   493  		rIn[i] = in
   494  		rOut[i] = out
   495  		quoteOut[i] = quote
   496  	}
   497  	for i, r := range rOut[:inL] {
   498  		set(inL+i, r, r, true)
   499  	}
   500  
   501  outer:
   502  	for pos := inL * 2; pos < testL; pos++ {
   503  		m := pos % len(fill)
   504  		i := rng.Intn(len(fill[m]))
   505  		r := fill[m][i]
   506  		for _, m := range combinedMappings {
   507  			if pSrc := runePos(r, m.src); pSrc != -1 {
   508  				set(pos, r, m.dst[pSrc], false)
   509  				continue outer
   510  			} else if pDst := runePos(r, m.dst); pDst != -1 {
   511  				set(pos, r, r, true)
   512  				continue outer
   513  			}
   514  		}
   515  		set(pos, r, r, false)
   516  	}
   517  
   518  	rng.Shuffle(testL, func(i, j int) {
   519  		rIn[i], rIn[j] = rIn[j], rIn[i]
   520  		rOut[i], rOut[j] = rOut[j], rOut[i]
   521  		quoteOut[i], quoteOut[j] = quoteOut[j], quoteOut[i]
   522  	})
   523  
   524  	var bOut strings.Builder
   525  	bOut.Grow(testL)
   526  	for i, r := range rOut {
   527  		if quoteOut[i] {
   528  			bOut.WriteRune(encoder.QuoteRune)
   529  		}
   530  		bOut.WriteRune(r)
   531  	}
   532  	return string(rIn), bOut.String()
   533  }
   534  
   535  func buildEdgeTestString(edges []edge, testMappings []mapping, fill [][]rune,
   536  	gen func(rIn, rOut []rune, quoteOut []bool, testMappings []mapping) []stringPair,
   537  ) []stringPair {
   538  	testL := 30
   539  	rIn := make([]rune, testL)      // test input string
   540  	rOut := make([]rune, testL)     // test output string without quote runes
   541  	quoteOut := make([]bool, testL) // if true insert quote rune before the output rune
   542  
   543  	set := func(i int, in, out rune, quote bool) {
   544  		rIn[i] = in
   545  		rOut[i] = out
   546  		quoteOut[i] = quote
   547  	}
   548  
   549  	// populate test strings with values from the `fill` set
   550  outer:
   551  	for pos := 0; pos < testL; pos++ {
   552  		m := pos % len(fill)
   553  		i := rng.Intn(len(fill[m]))
   554  		r := fill[m][i]
   555  		for _, m := range testMappings {
   556  			if pSrc := runePos(r, m.src); pSrc != -1 {
   557  				set(pos, r, m.dst[pSrc], false)
   558  				continue outer
   559  			} else if pDst := runePos(r, m.dst); pDst != -1 {
   560  				set(pos, r, r, true)
   561  				continue outer
   562  			}
   563  		}
   564  		set(pos, r, r, false)
   565  	}
   566  
   567  	rng.Shuffle(testL, func(i, j int) {
   568  		rIn[i], rIn[j] = rIn[j], rIn[i]
   569  		rOut[i], rOut[j] = rOut[j], rOut[i]
   570  		quoteOut[i], quoteOut[j] = quoteOut[j], quoteOut[i]
   571  	})
   572  	fixEdges(rIn, rOut, quoteOut, edges)
   573  	return gen(rIn, rOut, quoteOut, testMappings)
   574  }
   575  
   576  func fixEdges(rIn, rOut []rune, quoteOut []bool, edges []edge) {
   577  	testL := len(rIn)
   578  	for _, e := range edges {
   579  		for idx, o := range e.orig {
   580  			r := e.replace[idx]
   581  			if e.edge == edgeLeft && rIn[0] == o {
   582  				rOut[0], quoteOut[0] = r, false
   583  			} else if e.edge == edgeLeft && rIn[0] == r {
   584  				quoteOut[0] = true
   585  			} else if e.edge == edgeRight && rIn[testL-1] == o {
   586  				rOut[testL-1], quoteOut[testL-1] = r, false
   587  			} else if e.edge == edgeRight && rIn[testL-1] == r {
   588  				quoteOut[testL-1] = true
   589  			}
   590  		}
   591  	}
   592  }
   593  
   594  func runePos(r rune, s []rune) int {
   595  	for i, c := range s {
   596  		if c == r {
   597  			return i
   598  		}
   599  	}
   600  	return -1
   601  }
   602  
   603  // quotedToString returns a string for the chars slice where an encoder.QuoteRune is
   604  // inserted before a char[i] when quoted[i] is true.
   605  func quotedToString(chars []rune, quoted []bool) string {
   606  	var out strings.Builder
   607  	out.Grow(len(chars))
   608  	for i, r := range chars {
   609  		if quoted[i] {
   610  			out.WriteRune(encoder.QuoteRune)
   611  		}
   612  		out.WriteRune(r)
   613  	}
   614  	return out.String()
   615  }