benchclass.mx raw
1 package iskra
2
3 import "bytes"
4
5 type BenchClass uint8
6
7 const (
8 BenchSkip BenchClass = 0
9 BenchSimple BenchClass = 1
10 BenchComplex BenchClass = 2
11 )
12
13 // ClassifyBenchCost determines if a function warrants benchmarking
14 // based on its AST dump structure.
15 func ClassifyBenchCost(astDump string, funcName string) BenchClass {
16 data := []byte(astDump)
17 lines := bytes.Split(data, []byte("\n"))
18
19 hasLoop := false
20 hasSwitch := false
21 maxDepth := int32(0)
22 blockCount := int32(0)
23 hasSliceParam := false
24 isRecursive := false
25 funcNameBytes := []byte(funcName)
26
27 for _, line := range lines {
28 trimmed := bytes.TrimSpace(line)
29 if len(trimmed) == 0 {
30 continue
31 }
32
33 depth := indentDepth(line)
34 if depth > maxDepth {
35 maxDepth = depth
36 }
37
38 if bytes.HasPrefix(trimmed, []byte("For")) || bytes.HasPrefix(trimmed, []byte("Range")) {
39 hasLoop = true
40 }
41 if bytes.HasPrefix(trimmed, []byte("Switch")) || bytes.HasPrefix(trimmed, []byte("Select")) {
42 hasSwitch = true
43 }
44 if bytes.HasPrefix(trimmed, []byte("Block")) {
45 blockCount++
46 }
47 if bytes.HasPrefix(trimmed, []byte("Params")) {
48 hasSliceParam = checkSliceParams(lines)
49 }
50 if bytes.Contains(trimmed, []byte("[")) && bytes.Contains(trimmed, funcNameBytes) {
51 isRecursive = true
52 }
53 }
54
55 if hasLoop || isRecursive {
56 return BenchComplex
57 }
58 if hasSwitch || hasSliceParam || maxDepth > 4 || blockCount > 2 {
59 return BenchSimple
60 }
61 if maxDepth <= 2 && blockCount <= 1 {
62 return BenchSkip
63 }
64 return BenchSimple
65 }
66
67 func indentDepth(line []byte) int32 {
68 d := int32(0)
69 for _, b := range string(line) {
70 if b == ' ' {
71 d++
72 } else if b == '\t' {
73 d += 2
74 } else {
75 break
76 }
77 }
78 return d / 2
79 }
80
81 func checkSliceParams(lines [][]byte) bool {
82 inParams := false
83 for _, line := range lines {
84 trimmed := bytes.TrimSpace(line)
85 if bytes.HasPrefix(trimmed, []byte("Params")) {
86 inParams = true
87 continue
88 }
89 if inParams {
90 if len(trimmed) > 0 && trimmed[0] != ' ' && !bytes.HasPrefix(line, []byte(" ")) {
91 break
92 }
93 if bytes.Contains(trimmed, []byte("[]")) || bytes.Contains(trimmed, []byte("string")) {
94 return true
95 }
96 }
97 }
98 return false
99 }
100
101 func (c BenchClass) String() string {
102 switch c {
103 case BenchSkip:
104 return "skip"
105 case BenchSimple:
106 return "simple"
107 case BenchComplex:
108 return "complex"
109 default:
110 return "unknown"
111 }
112 }
113