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.Fatal("external disassembler: %v", err) 181 } 182 183 } 184 185 const start = 0x8000 // start address of text 186 187 // writeInst writes the generated byte sequences to a new file 188 // starting at offset start. That file is intended to be the input to 189 // the external disassembler. 190 func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) { 191 f, err = ioutil.TempFile("", "x86map") 192 if err != nil { 193 return 194 } 195 196 file = f.Name() 197 198 f.Seek(start, 0) 199 w := bufio.NewWriter(f) 200 defer w.Flush() 201 size = 0 202 generate(func(x []byte) { 203 if len(x) > 16 { 204 x = x[:16] 205 } 206 if debug { 207 fmt.Printf("%#x: %x%x\n", start+size, x, pops[len(x):]) 208 } 209 w.Write(x) 210 w.Write(pops[len(x):]) 211 size += len(pops) 212 }) 213 return file, f, size, nil 214 } 215 216 // 0x5F is a single-byte pop instruction. 217 // We pad the bytes we want decoded with enough 0x5Fs 218 // that no matter what state the instruction stream is in 219 // after reading our bytes, the pops will get us back to 220 // a forced instruction boundary. 221 var pops = []byte{ 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 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 226 } 227 228 // pad pads the code sequence with pops. 229 func pad(enc []byte) []byte { 230 return append(enc[:len(enc):len(enc)], pops...) 231 } 232 233 // disasm returns the decoded instruction and text 234 // for the given source bytes, using the given syntax and mode. 235 func disasm(syntax string, mode int, src []byte) (inst Inst, text string) { 236 // If printTests is set, we record the coverage value 237 // before and after, and we write out the inputs for which 238 // coverage went up, in the format expected in testdata/decode.text. 239 // This produces a fairly small set of test cases that exercise nearly 240 // all the code. 241 var cover float64 242 if *printTests { 243 cover -= coverage() 244 } 245 246 inst, err := decode1(src, mode, syntax == "gnu") 247 if err != nil { 248 text = "error: " + err.Error() 249 } else { 250 switch syntax { 251 case "gnu": 252 text = GNUSyntax(inst) 253 case "intel": 254 text = IntelSyntax(inst) 255 case "plan9": 256 text = Plan9Syntax(inst, 0, nil) 257 default: 258 text = "error: unknown syntax " + syntax 259 } 260 } 261 262 if *printTests { 263 cover += coverage() 264 if cover > 0 { 265 max := len(src) 266 if max > 16 && inst.Len <= 16 { 267 max = 16 268 } 269 fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text) 270 } 271 } 272 273 return 274 } 275 276 // coverage returns a floating point number denoting the 277 // test coverage until now. The number increases when new code paths are exercised, 278 // both in the Go program and in the decoder byte code. 279 func coverage() float64 { 280 /* 281 testing.Coverage is not in the main distribution. 282 The implementation, which must go in package testing, is: 283 284 // Coverage reports the current code coverage as a fraction in the range [0, 1]. 285 func Coverage() float64 { 286 var n, d int64 287 for _, counters := range cover.Counters { 288 for _, c := range counters { 289 if c > 0 { 290 n++ 291 } 292 d++ 293 } 294 } 295 if d == 0 { 296 return 0 297 } 298 return float64(n) / float64(d) 299 } 300 */ 301 302 var f float64 303 // f += testing.Coverage() 304 f += decodeCoverage() 305 return f 306 } 307 308 func decodeCoverage() float64 { 309 n := 0 310 for _, t := range decoderCover { 311 if t { 312 n++ 313 } 314 } 315 return float64(1+n) / float64(1+len(decoderCover)) 316 } 317 318 // Helpers for writing disassembler output parsers. 319 320 // isPrefix reports whether text is the name of an instruction prefix. 321 func isPrefix(text string) bool { 322 return prefixByte[text] > 0 323 } 324 325 // prefixByte maps instruction prefix text to actual prefix byte values. 326 var prefixByte = map[string]byte{ 327 "es": 0x26, 328 "cs": 0x2e, 329 "ss": 0x36, 330 "ds": 0x3e, 331 "fs": 0x64, 332 "gs": 0x65, 333 "data16": 0x66, 334 "addr16": 0x67, 335 "lock": 0xf0, 336 "repn": 0xf2, 337 "repne": 0xf2, 338 "rep": 0xf3, 339 "repe": 0xf3, 340 "xacquire": 0xf2, 341 "xrelease": 0xf3, 342 "bnd": 0xf2, 343 "addr32": 0x66, 344 "data32": 0x67, 345 } 346 347 // hasPrefix reports whether any of the space-separated words in the text s 348 // begins with any of the given prefixes. 349 func hasPrefix(s string, prefixes ...string) bool { 350 for _, prefix := range prefixes { 351 for s := s; s != ""; { 352 if strings.HasPrefix(s, prefix) { 353 return true 354 } 355 i := strings.Index(s, " ") 356 if i < 0 { 357 break 358 } 359 s = s[i+1:] 360 } 361 } 362 return false 363 } 364 365 // contains reports whether the text s contains any of the given substrings. 366 func contains(s string, substrings ...string) bool { 367 for _, sub := range substrings { 368 if strings.Contains(s, sub) { 369 return true 370 } 371 } 372 return false 373 } 374 375 // isHex reports whether b is a hexadecimal character (0-9A-Fa-f). 376 func isHex(b byte) bool { return b == '0' || unhex[b] > 0 } 377 378 // parseHex parses the hexadecimal byte dump in hex, 379 // appending the parsed bytes to raw and returning the updated slice. 380 // The returned bool signals whether any invalid hex was found. 381 // Spaces and tabs between bytes are okay but any other non-hex is not. 382 func parseHex(hex []byte, raw []byte) ([]byte, bool) { 383 hex = trimSpace(hex) 384 for j := 0; j < len(hex); { 385 for hex[j] == ' ' || hex[j] == '\t' { 386 j++ 387 } 388 if j >= len(hex) { 389 break 390 } 391 if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) { 392 return nil, false 393 } 394 raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]]) 395 j += 2 396 } 397 return raw, true 398 } 399 400 var unhex = [256]byte{ 401 '0': 0, 402 '1': 1, 403 '2': 2, 404 '3': 3, 405 '4': 4, 406 '5': 5, 407 '6': 6, 408 '7': 7, 409 '8': 8, 410 '9': 9, 411 'A': 10, 412 'B': 11, 413 'C': 12, 414 'D': 13, 415 'E': 14, 416 'F': 15, 417 'a': 10, 418 'b': 11, 419 'c': 12, 420 'd': 13, 421 'e': 14, 422 'f': 15, 423 } 424 425 // index is like bytes.Index(s, []byte(t)) but avoids the allocation. 426 func index(s []byte, t string) int { 427 i := 0 428 for { 429 j := bytes.IndexByte(s[i:], t[0]) 430 if j < 0 { 431 return -1 432 } 433 i = i + j 434 if i+len(t) > len(s) { 435 return -1 436 } 437 for k := 1; k < len(t); k++ { 438 if s[i+k] != t[k] { 439 goto nomatch 440 } 441 } 442 return i 443 nomatch: 444 i++ 445 } 446 } 447 448 // fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s. 449 // If s must be rewritten, it is rewritten in place. 450 func fixSpace(s []byte) []byte { 451 s = trimSpace(s) 452 for i := 0; i < len(s); i++ { 453 if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' { 454 goto Fix 455 } 456 } 457 return s 458 459 Fix: 460 b := s 461 w := 0 462 for i := 0; i < len(s); i++ { 463 c := s[i] 464 if c == '\t' || c == '\n' { 465 c = ' ' 466 } 467 if c == ' ' && w > 0 && b[w-1] == ' ' { 468 continue 469 } 470 b[w] = c 471 w++ 472 } 473 if w > 0 && b[w-1] == ' ' { 474 w-- 475 } 476 return b[:w] 477 } 478 479 // trimSpace trims leading and trailing space from s, returning a subslice of s. 480 func trimSpace(s []byte) []byte { 481 j := len(s) 482 for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') { 483 j-- 484 } 485 i := 0 486 for i < j && (s[i] == ' ' || s[i] == '\t') { 487 i++ 488 } 489 return s[i:j] 490 } 491 492 // pcrel and pcrelw match instructions using relative addressing mode. 493 var ( 494 pcrel = regexp.MustCompile(`^((?:.* )?(?:j[a-z]+|call|ljmp|loopn?e?w?|xbegin)q?(?:,p[nt])?) 0x([0-9a-f]+)$`) 495 pcrelw = regexp.MustCompile(`^((?:.* )?(?:callw|jmpw|xbeginw|ljmpw)(?:,p[nt])?) 0x([0-9a-f]+)$`) 496 ) 497 498 // Generators. 499 // 500 // The test cases are described as functions that invoke a callback repeatedly, 501 // with a new input sequence each time. These helpers make writing those 502 // a little easier. 503 504 // hexCases generates the cases written in hexadecimal in the encoded string. 505 // Spaces in 'encoded' separate entire test cases, not individual bytes. 506 func hexCases(t *testing.T, encoded string) func(func([]byte)) { 507 return func(try func([]byte)) { 508 for _, x := range strings.Fields(encoded) { 509 src, err := hex.DecodeString(x) 510 if err != nil { 511 t.Errorf("parsing %q: %v", x, err) 512 } 513 try(src) 514 } 515 } 516 } 517 518 // testdataCases generates the test cases recorded in testdata/decode.txt. 519 // It only uses the inputs; it ignores the answers recorded in that file. 520 func testdataCases(t *testing.T) func(func([]byte)) { 521 var codes [][]byte 522 data, err := ioutil.ReadFile("testdata/decode.txt") 523 if err != nil { 524 t.Fatal(err) 525 } 526 for _, line := range strings.Split(string(data), "\n") { 527 line = strings.TrimSpace(line) 528 if line == "" || strings.HasPrefix(line, "#") { 529 continue 530 } 531 f := strings.Fields(line)[0] 532 i := strings.Index(f, "|") 533 if i < 0 { 534 t.Errorf("parsing %q: missing | separator", f) 535 continue 536 } 537 if i%2 != 0 { 538 t.Errorf("parsing %q: misaligned | separator", f) 539 } 540 code, err := hex.DecodeString(f[:i] + f[i+1:]) 541 if err != nil { 542 t.Errorf("parsing %q: %v", f, err) 543 continue 544 } 545 codes = append(codes, code) 546 } 547 548 return func(try func([]byte)) { 549 for _, code := range codes { 550 try(code) 551 } 552 } 553 } 554 555 // manyPrefixes generates all possible 2 combinations of nine chosen prefixes. 556 // The relative ordering of the prefixes within the combinations varies deterministically. 557 func manyPrefixes(try func([]byte)) { 558 var prefixBytes = []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36, 0x66, 0x67} 559 var enc []byte 560 for i := 0; i < 1<<uint(len(prefixBytes)); i++ { 561 enc = enc[:0] 562 for j, p := range prefixBytes { 563 if i&(1<<uint(j)) != 0 { 564 enc = append(enc, p) 565 } 566 } 567 if len(enc) > 0 { 568 k := i % len(enc) 569 enc[0], enc[k] = enc[k], enc[0] 570 } 571 try(enc) 572 } 573 } 574 575 // basicPrefixes geneartes 8 different possible prefix cases: no prefix 576 // and then one each of seven different prefix bytes. 577 func basicPrefixes(try func([]byte)) { 578 try(nil) 579 for _, b := range []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36} { 580 try([]byte{b}) 581 } 582 } 583 584 func rexPrefixes(try func([]byte)) { 585 try(nil) 586 for _, b := range []byte{0x40, 0x48, 0x43, 0x4C} { 587 try([]byte{b}) 588 } 589 } 590 591 // concat takes two generators and returns a generator for the 592 // cross product of the two, concatenating the results from each. 593 func concat(gen1, gen2 func(func([]byte))) func(func([]byte)) { 594 return func(try func([]byte)) { 595 gen1(func(enc1 []byte) { 596 gen2(func(enc2 []byte) { 597 try(append(enc1[:len(enc1):len(enc1)], enc2...)) 598 }) 599 }) 600 } 601 } 602 603 // concat3 takes three generators and returns a generator for the 604 // cross product of the three, concatenating the results from each. 605 func concat3(gen1, gen2, gen3 func(func([]byte))) func(func([]byte)) { 606 return func(try func([]byte)) { 607 gen1(func(enc1 []byte) { 608 gen2(func(enc2 []byte) { 609 gen3(func(enc3 []byte) { 610 try(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...)) 611 }) 612 }) 613 }) 614 } 615 } 616 617 // concat4 takes four generators and returns a generator for the 618 // cross product of the four, concatenating the results from each. 619 func concat4(gen1, gen2, gen3, gen4 func(func([]byte))) func(func([]byte)) { 620 return func(try func([]byte)) { 621 gen1(func(enc1 []byte) { 622 gen2(func(enc2 []byte) { 623 gen3(func(enc3 []byte) { 624 gen4(func(enc4 []byte) { 625 try(append(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...), enc4...)) 626 }) 627 }) 628 }) 629 }) 630 } 631 } 632 633 // filter generates the sequences from gen that satisfy ok. 634 func filter(gen func(func([]byte)), ok func([]byte) bool) func(func([]byte)) { 635 return func(try func([]byte)) { 636 gen(func(enc []byte) { 637 if ok(enc) { 638 try(enc) 639 } 640 }) 641 } 642 } 643 644 // enum8bit generates all possible 1-byte sequences, followed by distinctive padding. 645 func enum8bit(try func([]byte)) { 646 for i := 0; i < 1<<8; i++ { 647 try([]byte{byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) 648 } 649 } 650 651 // enum8bit generates all possible 2-byte sequences, followed by distinctive padding. 652 func enum16bit(try func([]byte)) { 653 for i := 0; i < 1<<16; i++ { 654 try([]byte{byte(i), byte(i >> 8), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) 655 } 656 } 657 658 // enum24bit generates all possible 3-byte sequences, followed by distinctive padding. 659 func enum24bit(try func([]byte)) { 660 for i := 0; i < 1<<24; i++ { 661 try([]byte{byte(i), byte(i >> 8), byte(i >> 16), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) 662 } 663 } 664 665 // enumModRM generates all possible modrm bytes and, for modrm values that indicate 666 // a following sib byte, all possible modrm, sib combinations. 667 func enumModRM(try func([]byte)) { 668 for i := 0; i < 256; i++ { 669 if (i>>3)&07 == 04 && i>>6 != 3 { // has sib 670 for j := 0; j < 256; j++ { 671 try([]byte{0, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings 672 try([]byte{1, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings 673 } 674 } else { 675 try([]byte{0, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings 676 try([]byte{1, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings 677 } 678 } 679 } 680 681 // fixed generates the single case b. 682 // It's mainly useful to prepare an argument for concat or concat3. 683 func fixed(b ...byte) func(func([]byte)) { 684 return func(try func([]byte)) { 685 try(b) 686 } 687 } 688 689 // testBasic runs the given test function with cases all using opcode as the initial opcode bytes. 690 // It runs three phases: 691 // 692 // First, zero-or-one prefixes followed by opcode followed by all possible 1-byte values. 693 // If in -short mode, that's all. 694 // 695 // Second, zero-or-one prefixes followed by opcode followed by all possible 2-byte values. 696 // If not in -long mode, that's all. This phase and the next run in parallel with other tests 697 // (using t.Parallel). 698 // 699 // Finally, opcode followed by all possible 3-byte values. The test can take a very long time 700 // and prints progress messages to package log. 701 func testBasic(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) { 702 testfn(t, concat3(basicPrefixes, fixed(opcode...), enum8bit)) 703 if testing.Short() { 704 return 705 } 706 707 t.Parallel() 708 testfn(t, concat3(basicPrefixes, fixed(opcode...), enum16bit)) 709 if !*longTest { 710 return 711 } 712 713 name := caller(2) 714 op1 := make([]byte, len(opcode)+1) 715 copy(op1, opcode) 716 for i := 0; i < 256; i++ { 717 log.Printf("%s 24-bit: %d/256\n", name, i) 718 op1[len(opcode)] = byte(i) 719 testfn(t, concat(fixed(op1...), enum16bit)) 720 } 721 } 722 723 func testBasicREX(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) { 724 testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum8bit), isValidREX)) 725 if testing.Short() { 726 return 727 } 728 729 t.Parallel() 730 testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum16bit), isValidREX)) 731 if !*longTest { 732 return 733 } 734 735 name := caller(2) 736 op1 := make([]byte, len(opcode)+1) 737 copy(op1, opcode) 738 for i := 0; i < 256; i++ { 739 log.Printf("%s 24-bit: %d/256\n", name, i) 740 op1[len(opcode)] = byte(i) 741 testfn(t, filter(concat3(rexPrefixes, fixed(op1...), enum16bit), isValidREX)) 742 } 743 } 744 745 // testPrefix runs the given test function for all many prefix possibilities 746 // followed by all possible 1-byte sequences. 747 // 748 // If in -long mode, it then runs a test of all the prefix possibilities followed 749 // by all possible 2-byte sequences. 750 func testPrefix(t *testing.T, testfn func(*testing.T, func(func([]byte)))) { 751 t.Parallel() 752 testfn(t, concat(manyPrefixes, enum8bit)) 753 if testing.Short() || !*longTest { 754 return 755 } 756 757 name := caller(2) 758 for i := 0; i < 256; i++ { 759 log.Printf("%s 16-bit: %d/256\n", name, i) 760 testfn(t, concat3(manyPrefixes, fixed(byte(i)), enum8bit)) 761 } 762 } 763 764 func testPrefixREX(t *testing.T, testfn func(*testing.T, func(func([]byte)))) { 765 t.Parallel() 766 testfn(t, filter(concat3(manyPrefixes, rexPrefixes, enum8bit), isValidREX)) 767 if testing.Short() || !*longTest { 768 return 769 } 770 771 name := caller(2) 772 for i := 0; i < 256; i++ { 773 log.Printf("%s 16-bit: %d/256\n", name, i) 774 testfn(t, filter(concat4(manyPrefixes, rexPrefixes, fixed(byte(i)), enum8bit), isValidREX)) 775 } 776 } 777 778 func caller(skip int) string { 779 pc, _, _, _ := runtime.Caller(skip) 780 f := runtime.FuncForPC(pc) 781 name := "?" 782 if f != nil { 783 name = f.Name() 784 if i := strings.LastIndex(name, "."); i >= 0 { 785 name = name[i+1:] 786 } 787 } 788 return name 789 } 790 791 func isValidREX(x []byte) bool { 792 i := 0 793 for i < len(x) && isPrefixByte(x[i]) { 794 i++ 795 } 796 if i < len(x) && Prefix(x[i]).IsREX() { 797 i++ 798 if i < len(x) { 799 return !isPrefixByte(x[i]) && !Prefix(x[i]).IsREX() 800 } 801 } 802 return true 803 } 804 805 func isPrefixByte(b byte) bool { 806 switch b { 807 case 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x66, 0x67, 0xF0, 0xF2, 0xF3: 808 return true 809 } 810 return false 811 } 812