-
-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathBytePatternMatcher.java
More file actions
137 lines (110 loc) · 4.08 KB
/
BytePatternMatcher.java
File metadata and controls
137 lines (110 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package com.falsepattern.lib.turboasm;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class BytePatternMatcher {
final Mode mode;
// first byte -> matched patterns
final byte[][][] byFirst = new byte[256][][];
int minPatternLen = Integer.MAX_VALUE;
public enum Mode {
/** Checks if the constant pool entry contains a pattern */
Contains,
/** Checks if the whole constant pool entry equals a pattern */
Equals,
/** Checks if the constant pool entry starts with a pattern */
StartsWith
}
public BytePatternMatcher(String strPattern, Mode mode) {
this(new String[] {strPattern}, mode);
}
public BytePatternMatcher(String[] strPatterns, Mode mode) {
this.mode = mode;
final byte[][] patterns = new byte[strPatterns.length][];
final int[] bucketSizes = new int[256];
final int[] bucketIndices = new int[Math.min(strPatterns.length, 256)];
int bucketsCount = 0;
for (int i = 0; i < strPatterns.length; i++) {
final byte[] pattern = strPatterns[i].getBytes(StandardCharsets.UTF_8);
patterns[i] = pattern;
if (pattern.length < minPatternLen) {
minPatternLen = pattern.length;
}
final int bucketIndex = pattern[0] & 0xFF;
if (bucketSizes[bucketIndex]++ == 0) {
bucketIndices[bucketsCount++] = bucketIndex;
}
}
// Ascending sorting by length
Arrays.sort(patterns, (a, b) -> Integer.compare(a.length, b.length));
for (int i = 0; i < bucketsCount; i++) {
final int bucketIndex = bucketIndices[i];
byFirst[bucketIndex] = new byte[bucketSizes[bucketIndex]][];
bucketSizes[bucketIndex] = 0; // reuse as write index
}
for (final byte[] pattern : patterns) {
final int bucketIndex = pattern[0] & 0xFF;
byFirst[bucketIndex][bucketSizes[bucketIndex]++] = pattern;
}
}
public boolean matches(byte[] bytes, int start, int len) {
if (len < minPatternLen) {
return false;
}
return switch (mode) {
case Contains -> contains(bytes, start, len);
case Equals -> equals(bytes, start, len);
case StartsWith -> startsWith(bytes, start, len);
};
}
private boolean contains(byte[] bytes, int start, int len) {
final int end = start + len;
for (int pos = start; pos <= end - minPatternLen; pos++) {
final byte[][] patterns = byFirst[bytes[pos] & 0xFF];
if (patterns == null) {
continue;
}
for (final byte[] pattern : patterns) {
if (pattern.length > end - pos) {
break;
}
int k = pattern.length - 1;
while (k > 0 && bytes[pos + k] == pattern[k]) k--;
if (k == 0) return true;
}
}
return false;
}
private boolean equals(byte[] bytes, int start, int len) {
final byte[][] patterns = byFirst[bytes[start] & 0xFF];
if (patterns == null) {
return false;
}
for (final byte[] pattern : patterns) {
if (pattern.length < len) {
continue;
}
if (pattern.length > len) {
break;
}
int k = pattern.length - 1;
while (k > 0 && bytes[start + k] == pattern[k]) k--;
if (k == 0) return true;
}
return false;
}
private boolean startsWith(byte[] bytes, int start, int len) {
final byte[][] patterns = byFirst[bytes[start] & 0xFF];
if (patterns == null) {
return false;
}
for (final byte[] pattern : patterns) {
if (pattern.length > len) {
break;
}
int k = pattern.length - 1;
while (k > 0 && bytes[start + k] == pattern[k]) k--;
if (k == 0) return true;
}
return false;
}
}