1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Support for testing against external disassembler program. 6 7 package x86asm 8 9 import ( 10 "bufio" 11 "bytes" 12 "encoding/hex" 13 "flag" 14 "fmt" 15 "io/ioutil" 16 "log" 17 "math/rand" 18 "os" 19 "os/exec" 20 "regexp" 21 "runtime" 22 "strings" 23 "testing" 24 "time" 25 ) 26 27 var ( 28 printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths") 29 dumpTest = flag.Bool("dump", false, "dump all encodings") 30 mismatch = flag.Bool("mismatch", false, "log allowed mismatches") 31 longTest = flag.Bool("long", false, "long test") 32 keep = flag.Bool("keep", false, "keep object files around") 33 debug = false 34 ) 35 36 // A ExtInst represents a single decoded instruction parsed 37 // from an external disassembler's output. 38 type ExtInst struct { 39 addr uint32 40 enc [32]byte 41 nenc int 42 text string 43 } 44 45 func (r ExtInst) String() string { 46 return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text) 47 } 48 49 // An ExtDis is a connection between an external disassembler and a test. 50 type ExtDis struct { 51 Arch int 52 Dec chan ExtInst 53 File *os.File 54 Size int 55 KeepFile bool 56 Cmd *exec.Cmd 57 } 58 59 // Run runs the given command - the external disassembler - and returns 60 // a buffered reader of its standard output. 61 func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) { 62 if *keep { 63 log.Printf("%s\n", strings.Join(cmd, " ")) 64 } 65 ext.Cmd = exec.Command(cmd[0], cmd[1:]...) 66 out, err := ext.Cmd.StdoutPipe() 67 if err != nil { 68 return nil, fmt.Errorf("stdoutpipe: %v", err) 69 } 70 if err := ext.Cmd.Start(); err != nil { 71 return nil, fmt.Errorf("exec: %v", err) 72 } 73 74 b := bufio.NewReaderSize(out, 1<<20) 75 return b, nil 76 } 77 78 // Wait waits for the command started with Run to exit. 79 func (ext *ExtDis) Wait() error { 80 return ext.Cmd.Wait() 81 } 82 83 // testExtDis tests a set of byte sequences against an external disassembler. 84 // The disassembler is expected to produce the given syntax and be run 85 // in the given architecture mode (16, 32, or 64-bit). 86 // The extdis function must start the external disassembler 87 // and then parse its output, sending the parsed instructions on ext.Dec. 88 // The generate function calls its argument f once for each byte sequence 89 // to be tested. The generate function itself will be called twice, and it must 90 // make the same sequence of calls to f each time. 91 // When a disassembly does not match the internal decoding, 92 // allowedMismatch determines whether this mismatch should be 93 // allowed, or else considered an error. 94 func testExtDis( 95 t *testing.T, 96 syntax string, 97 arch int, 98 extdis func(ext *ExtDis) error, 99 generate func(f func([]byte)), 100 allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool, 101 ) { 102 start := time.Now() 103 ext := &ExtDis{ 104 Dec: make(chan ExtInst), 105 Arch: arch, 106 } 107 errc := make(chan error) 108 109 // First pass: write instructions to input file for external disassembler. 110 file, f, size, err := writeInst(generate) 111 if err != nil { 112 t.Fatal(err) 113 } 114 ext.Size = size 115 ext.File = f 116 defer func() { 117 f.Close() 118 if !*keep { 119 os.Remove(file) 120 } 121 }() 122 123 // Second pass: compare disassembly against our decodings. 124 var ( 125 totalTests = 0 126 totalSkips = 0 127 totalErrors = 0 128 129 errors = make([]string, 0, 100) // sampled errors, at most cap 130 ) 131 go func() { 132 errc <- extdis(ext) 133 }() 134 generate(func(enc []byte) { 135 dec, ok := <-ext.Dec 136 if !ok { 137 t.Errorf("decoding stream ended early") 138 return 139 } 140 inst, text := disasm(syntax, arch, pad(enc)) 141 totalTests++ 142 if *dumpTest { 143 fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc) 144 } 145 if text != dec.text || inst.Len != dec.nenc { 146 suffix := "" 147 if allowedMismatch(text, size, &inst, dec) { 148 totalSkips++ 149 if !*mismatch { 150 return 151 } 152 suffix += " (allowed mismatch)" 153 } 154 totalErrors++ 155 if len(errors) >= cap(errors) { 156 j := rand.Intn(totalErrors) 157 if j >= cap(errors) { 158 return 159 } 160 errors = append(errors[:j], errors[j+1:]...) 161 } 162 errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix)) 163 } 164 }) 165 166 if *mismatch { 167 totalErrors -= totalSkips 168 } 169 170 for _, b := range errors { 171 t.Log(b) 172 } 173 174 if totalErrors > 0 { 175 t.Fail() 176 } 177 t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds()) 178 179 if err := <-errc; err != nil { 180 t.Fatalf("external disassembler: %v", err) 181 } 182 } 183 184 const start = 0x8000 // start address of text 185 186 // writeInst writes the generated byte sequences to a new file 187 // starting at offset start. That file is intended to be the input to 188 // the external disassembler. 189 func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) { 190 f, err = ioutil.TempFile("", "x86map") 191 if err != nil { 192 return 193 } 194 195 file = f.Name() 196 197 f.Seek(start, 0) 198 w := bufio.NewWriter(f) 199 defer w.Flush() 200 size = 0 201 generate(func(x []byte) { 202 if len(x) > 16 { 203 x = x[:16] 204 } 205 if debug { 206 fmt.Printf("%#x: %x%x\n", start+size, x, pops[len(x):]) 207 } 208 w.Write(x) 209 w.Write(pops[len(x):]) 210 size += len(pops) 211 }) 212 return file, f, size, nil 213 } 214 215 // 0x5F is a single-byte pop instruction. 216 // We pad the bytes we want decoded with enough 0x5Fs 217 // that no matter what state the instruction stream is in 218 // after reading our bytes, the pops will get us back to 219 // a forced instruction boundary. 220 var pops = []byte{ 221 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 222 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 223 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 224 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 225 } 226 227 // pad pads the code sequence with pops. 228 func pad(enc []byte) []byte { 229 return append(enc[:len(enc):len(enc)], pops...) 230 } 231 232 // disasm returns the decoded instruction and text 233 // for the given source bytes, using the given syntax and mode. 234 func disasm(syntax string, mode int, src []byte) (inst Inst, text string) { 235 // If printTests is set, we record the coverage value 236 // before and after, and we write out the inputs for which 237 // coverage went up, in the format expected in testdata/decode.text. 238 // This produces a fairly small set of test cases that exercise nearly 239 // all the code. 240 var cover float64 241 if *printTests { 242 cover -= coverage() 243 } 244 245 inst, err := decode1(src, mode, syntax == "gnu") 246 if err != nil { 247 text = "error: " + err.Error() 248 } else { 249 switch syntax { 250 case "gnu": 251 text = GNUSyntax(inst) 252 case "intel": 253 text = IntelSyntax(inst) 254 case "plan9": // [sic] 255 text = GoSyntax(inst, 0, nil) 256 default: 257 text = "error: unknown syntax " + syntax 258 } 259 } 260 261 if *printTests { 262 cover += coverage() 263 if cover > 0 { 264 max := len(src) 265 if max > 16 && inst.Len <= 16 { 266 max = 16 267 } 268 fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text) 269 } 270 } 271 272 return 273 } 274 275 // coverage returns a floating point number denoting the 276 // test coverage until now. The number increases when new code paths are exercised, 277 // both in the Go program and in the decoder byte code. 278 func coverage() float64 { 279 /* 280 testing.Coverage is not in the main distribution. 281 The implementation, which must go in package testing, is: 282 283 // Coverage reports the current code coverage as a fraction in the range [0, 1]. 284 func Coverage() float64 { 285 var n, d int64 286 for _, counters := range cover.Counters { 287 for _, c := range counters { 288 if c > 0 { 289 n++ 290 } 291 d++ 292 } 293 } 294 if d == 0 { 295 return 0 296 } 297 return float64(n) / float64(d) 298 } 299 */ 300 301 var f float64 302 // f += testing.Coverage() 303 f += decodeCoverage() 304 return f 305 } 306 307 func decodeCoverage() float64 { 308 n := 0 309 for _, t := range decoderCover { 310 if t { 311 n++ 312 } 313 } 314 return float64(1+n) / float64(1+len(decoderCover)) 315 } 316 317 // Helpers for writing disassembler output parsers. 318 319 // isPrefix reports whether text is the name of an instruction prefix. 320 func isPrefix(text string) bool { 321 return prefixByte[text] > 0 322 } 323 324 // prefixByte maps instruction prefix text to actual prefix byte values. 325 var prefixByte = map[string]byte{ 326 "es": 0x26, 327 "cs": 0x2e, 328 "ss": 0x36, 329 "ds": 0x3e, 330 "fs": 0x64, 331 "gs": 0x65, 332 "data16": 0x66, 333 "addr16": 0x67, 334 "lock": 0xf0, 335 "repn": 0xf2, 336 "repne": 0xf2, 337 "rep": 0xf3, 338 "repe": 0xf3, 339 "xacquire": 0xf2, 340 "xrelease": 0xf3, 341 "bnd": 0xf2, 342 "addr32": 0x66, 343 "data32": 0x67, 344 } 345 346 // hasPrefix reports whether any of the space-separated words in the text s 347 // begins with any of the given prefixes. 348 func hasPrefix(s string, prefixes ...string) bool { 349 for _, prefix := range prefixes { 350 for s := s; s != ""; { 351 if strings.HasPrefix(s, prefix) { 352 return true 353 } 354 i := strings.Index(s, " ") 355 if i < 0 { 356 break 357 } 358 s = s[i+1:] 359 } 360 } 361 return false 362 } 363 364 // contains reports whether the text s contains any of the given substrings. 365 func contains(s string, substrings ...string) bool { 366 for _, sub := range substrings { 367 if strings.Contains(s, sub) { 368 return true 369 } 370 } 371 return false 372 } 373 374 // isHex reports whether b is a hexadecimal character (0-9A-Fa-f). 375 func isHex(b byte) bool { return b == '0' || unhex[b] > 0 } 376 377 // parseHex parses the hexadecimal byte dump in hex, 378 // appending the parsed bytes to raw and returning the updated slice. 379 // The returned bool signals whether any invalid hex was found. 380 // Spaces and tabs between bytes are okay but any other non-hex is not. 381 func parseHex(hex []byte, raw []byte) ([]byte, bool) { 382 hex = trimSpace(hex) 383 for j := 0; j < len(hex); { 384 for hex[j] == ' ' || hex[j] == '\t' { 385 j++ 386 } 387 if j >= len(hex) { 388 break 389 } 390 if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) { 391 return nil, false 392 } 393 raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]]) 394 j += 2 395 } 396 return raw, true 397 } 398 399 var unhex = [256]byte{ 400 '0': 0, 401 '1': 1, 402 '2': 2, 403 '3': 3, 404 '4': 4, 405 '5': 5, 406 '6': 6, 407 '7': 7, 408 '8': 8, 409 '9': 9, 410 'A': 10, 411 'B': 11, 412 'C': 12, 413 'D': 13, 414 'E': 14, 415 'F': 15, 416 'a': 10, 417 'b': 11, 418 'c': 12, 419 'd': 13, 420 'e': 14, 421 'f': 15, 422 } 423 424 // index is like bytes.Index(s, []byte(t)) but avoids the allocation. 425 func index(s []byte, t string) int { 426 i := 0 427 for { 428 j := bytes.IndexByte(s[i:], t[0]) 429 if j < 0 { 430 return -1 431 } 432 i = i + j 433 if i+len(t) > len(s) { 434 return -1 435 } 436 for k := 1; k < len(t); k++ { 437 if s[i+k] != t[k] { 438 goto nomatch 439 } 440 } 441 return i 442 nomatch: 443 i++ 444 } 445 } 446 447 // fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s. 448 // If s must be rewritten, it is rewritten in place. 449 func fixSpace(s []byte) []byte { 450 s = trimSpace(s) 451 for i := 0; i < len(s); i++ { 452 if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' { 453 goto Fix 454 } 455 } 456 return s 457 458 Fix: 459 b := s 460 w := 0 461 for i := 0; i < len(s); i++ { 462 c := s[i] 463 if c == '\t' || c == '\n' { 464 c = ' ' 465 } 466 if c == ' ' && w > 0 && b[w-1] == ' ' { 467 continue 468 } 469 b[w] = c 470 w++ 471 } 472 if w > 0 && b[w-1] == ' ' { 473 w-- 474 } 475 return b[:w] 476 } 477 478 // trimSpace trims leading and trailing space from s, returning a subslice of s. 479 func trimSpace(s []byte) []byte { 480 j := len(s) 481 for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') { 482 j-- 483 } 484 i := 0 485 for i < j && (s[i] == ' ' || s[i] == '\t') { 486 i++ 487 } 488 return s[i:j] 489 } 490 491 // pcrel and pcrelw match instructions using relative addressing mode. 492 var ( 493 pcrel = regexp.MustCompile(`^((?:.* )?(?:j[a-z]+|call|ljmp|loopn?e?w?|xbegin)q?(?:,p[nt])?) 0x([0-9a-f]+)$`) 494 pcrelw = regexp.MustCompile(`^((?:.* )?(?:callw|jmpw|xbeginw|ljmpw)(?:,p[nt])?) 0x([0-9a-f]+)$`) 495 ) 496 497 // Generators. 498 // 499 // The test cases are described as functions that invoke a callback repeatedly, 500 // with a new input sequence each time. These helpers make writing those 501 // a little easier. 502 503 // hexCases generates the cases written in hexadecimal in the encoded string. 504 // Spaces in 'encoded' separate entire test cases, not individual bytes. 505 func hexCases(t *testing.T, encoded string) func(func([]byte)) { 506 return func(try func([]byte)) { 507 for _, x := range strings.Fields(encoded) { 508 src, err := hex.DecodeString(x) 509 if err != nil { 510 t.Errorf("parsing %q: %v", x, err) 511 } 512 try(src) 513 } 514 } 515 } 516 517 // testdataCases generates the test cases recorded in testdata/decode.txt. 518 // It only uses the inputs; it ignores the answers recorded in that file. 519 func testdataCases(t *testing.T) func(func([]byte)) { 520 var codes [][]byte 521 data, err := ioutil.ReadFile("testdata/decode.txt") 522 if err != nil { 523 t.Fatal(err) 524 } 525 for _, line := range strings.Split(string(data), "\n") { 526 line = strings.TrimSpace(line) 527 if line == "" || strings.HasPrefix(line, "#") { 528 continue 529 } 530 f := strings.Fields(line)[0] 531 i := strings.Index(f, "|") 532 if i < 0 { 533 t.Errorf("parsing %q: missing | separator", f) 534 continue 535 } 536 if i%2 != 0 { 537 t.Errorf("parsing %q: misaligned | separator", f) 538 } 539 code, err := hex.DecodeString(f[:i] + f[i+1:]) 540 if err != nil { 541 t.Errorf("parsing %q: %v", f, err) 542 continue 543 } 544 codes = append(codes, code) 545 } 546 547 return func(try func([]byte)) { 548 for _, code := range codes { 549 try(code) 550 } 551 } 552 } 553 554 // manyPrefixes generates all possible 2 combinations of nine chosen prefixes. 555 // The relative ordering of the prefixes within the combinations varies deterministically. 556 func manyPrefixes(try func([]byte)) { 557 var prefixBytes = []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36, 0x66, 0x67} 558 var enc []byte 559 for i := 0; i < 1<<uint(len(prefixBytes)); i++ { 560 enc = enc[:0] 561 for j, p := range prefixBytes { 562 if i&(1<<uint(j)) != 0 { 563 enc = append(enc, p) 564 } 565 } 566 if len(enc) > 0 { 567 k := i % len(enc) 568 enc[0], enc[k] = enc[k], enc[0] 569 } 570 try(enc) 571 } 572 } 573 574 // basicPrefixes geneartes 8 different possible prefix cases: no prefix 575 // and then one each of seven different prefix bytes. 576 func basicPrefixes(try func([]byte)) { 577 try(nil) 578 for _, b := range []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36} { 579 try([]byte{b}) 580 } 581 } 582 583 func rexPrefixes(try func([]byte)) { 584 try(nil) 585 for _, b := range []byte{0x40, 0x48, 0x43, 0x4C} { 586 try([]byte{b}) 587 } 588 } 589 590 // concat takes two generators and returns a generator for the 591 // cross product of the two, concatenating the results from each. 592 func concat(gen1, gen2 func(func([]byte))) func(func([]byte)) { 593 return func(try func([]byte)) { 594 gen1(func(enc1 []byte) { 595 gen2(func(enc2 []byte) { 596 try(append(enc1[:len(enc1):len(enc1)], enc2...)) 597 }) 598 }) 599 } 600 } 601 602 // concat3 takes three generators and returns a generator for the 603 // cross product of the three, concatenating the results from each. 604 func concat3(gen1, gen2, gen3 func(func([]byte))) func(func([]byte)) { 605 return func(try func([]byte)) { 606 gen1(func(enc1 []byte) { 607 gen2(func(enc2 []byte) { 608 gen3(func(enc3 []byte) { 609 try(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...)) 610 }) 611 }) 612 }) 613 } 614 } 615 616 // concat4 takes four generators and returns a generator for the 617 // cross product of the four, concatenating the results from each. 618 func concat4(gen1, gen2, gen3, gen4 func(func([]byte))) func(func([]byte)) { 619 return func(try func([]byte)) { 620 gen1(func(enc1 []byte) { 621 gen2(func(enc2 []byte) { 622 gen3(func(enc3 []byte) { 623 gen4(func(enc4 []byte) { 624 try(append(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...), enc4...)) 625 }) 626 }) 627 }) 628 }) 629 } 630 } 631 632 // filter generates the sequences from gen that satisfy ok. 633 func filter(gen func(func([]byte)), ok func([]byte) bool) func(func([]byte)) { 634 return func(try func([]byte)) { 635 gen(func(enc []byte) { 636 if ok(enc) { 637 try(enc) 638 } 639 }) 640 } 641 } 642 643 // enum8bit generates all possible 1-byte sequences, followed by distinctive padding. 644 func enum8bit(try func([]byte)) { 645 for i := 0; i < 1<<8; i++ { 646 try([]byte{byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) 647 } 648 } 649 650 // enum8bit generates all possible 2-byte sequences, followed by distinctive padding. 651 func enum16bit(try func([]byte)) { 652 for i := 0; i < 1<<16; i++ { 653 try([]byte{byte(i), byte(i >> 8), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) 654 } 655 } 656 657 // enum24bit generates all possible 3-byte sequences, followed by distinctive padding. 658 func enum24bit(try func([]byte)) { 659 for i := 0; i < 1<<24; i++ { 660 try([]byte{byte(i), byte(i >> 8), byte(i >> 16), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) 661 } 662 } 663 664 // enumModRM generates all possible modrm bytes and, for modrm values that indicate 665 // a following sib byte, all possible modrm, sib combinations. 666 func enumModRM(try func([]byte)) { 667 for i := 0; i < 256; i++ { 668 if (i>>3)&07 == 04 && i>>6 != 3 { // has sib 669 for j := 0; j < 256; j++ { 670 try([]byte{0, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings 671 try([]byte{1, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings 672 } 673 } else { 674 try([]byte{0, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings 675 try([]byte{1, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings 676 } 677 } 678 } 679 680 // fixed generates the single case b. 681 // It's mainly useful to prepare an argument for concat or concat3. 682 func fixed(b ...byte) func(func([]byte)) { 683 return func(try func([]byte)) { 684 try(b) 685 } 686 } 687 688 // testBasic runs the given test function with cases all using opcode as the initial opcode bytes. 689 // It runs three phases: 690 // 691 // First, zero-or-one prefixes followed by opcode followed by all possible 1-byte values. 692 // If in -short mode, that's all. 693 // 694 // Second, zero-or-one prefixes followed by opcode followed by all possible 2-byte values. 695 // If not in -long mode, that's all. This phase and the next run in parallel with other tests 696 // (using t.Parallel). 697 // 698 // Finally, opcode followed by all possible 3-byte values. The test can take a very long time 699 // and prints progress messages to package log. 700 func testBasic(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) { 701 testfn(t, concat3(basicPrefixes, fixed(opcode...), enum8bit)) 702 if testing.Short() { 703 return 704 } 705 706 t.Parallel() 707 testfn(t, concat3(basicPrefixes, fixed(opcode...), enum16bit)) 708 if !*longTest { 709 return 710 } 711 712 name := caller(2) 713 op1 := make([]byte, len(opcode)+1) 714 copy(op1, opcode) 715 for i := 0; i < 256; i++ { 716 log.Printf("%s 24-bit: %d/256\n", name, i) 717 op1[len(opcode)] = byte(i) 718 testfn(t, concat(fixed(op1...), enum16bit)) 719 } 720 } 721 722 func testBasicREX(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) { 723 testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum8bit), isValidREX)) 724 if testing.Short() { 725 return 726 } 727 728 t.Parallel() 729 testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum16bit), isValidREX)) 730 if !*longTest { 731 return 732 } 733 734 name := caller(2) 735 op1 := make([]byte, len(opcode)+1) 736 copy(op1, opcode) 737 for i := 0; i < 256; i++ { 738 log.Printf("%s 24-bit: %d/256\n", name, i) 739 op1[len(opcode)] = byte(i) 740 testfn(t, filter(concat3(rexPrefixes, fixed(op1...), enum16bit), isValidREX)) 741 } 742 } 743 744 // testPrefix runs the given test function for all many prefix possibilities 745 // followed by all possible 1-byte sequences. 746 // 747 // If in -long mode, it then runs a test of all the prefix possibilities followed 748 // by all possible 2-byte sequences. 749 func testPrefix(t *testing.T, testfn func(*testing.T, func(func([]byte)))) { 750 t.Parallel() 751 testfn(t, concat(manyPrefixes, enum8bit)) 752 if testing.Short() || !*longTest { 753 return 754 } 755 756 name := caller(2) 757 for i := 0; i < 256; i++ { 758 log.Printf("%s 16-bit: %d/256\n", name, i) 759 testfn(t, concat3(manyPrefixes, fixed(byte(i)), enum8bit)) 760 } 761 } 762 763 func testPrefixREX(t *testing.T, testfn func(*testing.T, func(func([]byte)))) { 764 t.Parallel() 765 testfn(t, filter(concat3(manyPrefixes, rexPrefixes, enum8bit), isValidREX)) 766 if testing.Short() || !*longTest { 767 return 768 } 769 770 name := caller(2) 771 for i := 0; i < 256; i++ { 772 log.Printf("%s 16-bit: %d/256\n", name, i) 773 testfn(t, filter(concat4(manyPrefixes, rexPrefixes, fixed(byte(i)), enum8bit), isValidREX)) 774 } 775 } 776 777 func caller(skip int) string { 778 pc, _, _, _ := runtime.Caller(skip) 779 f := runtime.FuncForPC(pc) 780 name := "?" 781 if f != nil { 782 name = f.Name() 783 if i := strings.LastIndex(name, "."); i >= 0 { 784 name = name[i+1:] 785 } 786 } 787 return name 788 } 789 790 func isValidREX(x []byte) bool { 791 i := 0 792 for i < len(x) && isPrefixByte(x[i]) { 793 i++ 794 } 795 if i < len(x) && Prefix(x[i]).IsREX() { 796 i++ 797 if i < len(x) { 798 return !isPrefixByte(x[i]) && !Prefix(x[i]).IsREX() 799 } 800 } 801 return true 802 } 803 804 func isPrefixByte(b byte) bool { 805 switch b { 806 case 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x66, 0x67, 0xF0, 0xF2, 0xF3: 807 return true 808 } 809 return false 810 } 811