github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/lib/encoder/internal/gen/main.go (about)

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