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 }