github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/lib/encoder/internal/gen/main.go (about)

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