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 }