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 }