3131import java .io .DataInputStream ;
3232import java .io .IOException ;
3333import java .util .ArrayList ;
34+ import java .util .Arrays ;
3435import java .util .Collections ;
3536import java .util .List ;
3637
3940 */
4041public final class ClassHeaderMetadata implements FastClassAccessor {
4142
43+ public final byte [] classBytes ;
4244 public final int minorVersion ;
4345 public final int majorVersion ;
4446 public final int constantPoolEntryCount ;
@@ -47,6 +49,9 @@ public final class ClassHeaderMetadata implements FastClassAccessor {
4749 /** Type of each parsed constant pool entry, zero-indexed! */
4850 public final ConstantPoolEntryTypes @ NotNull [] constantPoolEntryTypes ;
4951
52+ /** Approximately only half of the entries are utf-8, so this array can be used to iterate over them more quickly */
53+ public final int @ NotNull [] constantPoolUtf8EntryOffsets ;
54+
5055 public final int constantPoolEndOffset ;
5156 public final int accessFlags ;
5257 public final int thisClassIndex ;
@@ -66,15 +71,17 @@ public ClassHeaderMetadata(byte @NotNull [] bytes) {
6671 if (!isValidClass (bytes )) {
6772 throw new IllegalArgumentException ("Invalid class detected" );
6873 }
74+ this .classBytes = bytes ;
6975 this .minorVersion = u16 (bytes , Offsets .minorVersionU16 );
7076 this .majorVersion = u16 (bytes , Offsets .majorVersionU16 );
7177 this .constantPoolEntryCount = u16 (bytes , Offsets .constantPoolCountU16 );
7278 this .constantPoolEntryOffsets = new int [constantPoolEntryCount ];
7379 this .constantPoolEntryTypes = new ConstantPoolEntryTypes [constantPoolEntryCount ];
7480 // scan through CP entries
75- final int cpOff ;
7681 {
7782 int off = Offsets .constantPoolStart ;
83+ final int [] utf8EntryOffsets = new int [constantPoolEntryCount ];
84+ int utf8Entries = 0 ;
7885 for (int entry = 0 ; entry < constantPoolEntryCount - 1 ; entry ++) {
7986 constantPoolEntryOffsets [entry ] = off ;
8087 ConstantPoolEntryTypes type = ConstantPoolEntryTypes .parse (bytes , off );
@@ -84,18 +91,18 @@ public ClassHeaderMetadata(byte @NotNull [] bytes) {
8491 entry ++;
8592 constantPoolEntryOffsets [entry ] = off ;
8693 constantPoolEntryTypes [entry ] = type ;
94+ } else if (type == ConstantPoolEntryTypes .Utf8 ) {
95+ utf8EntryOffsets [utf8Entries ++] = off ;
8796 }
8897 off += type .byteLength (bytes , off );
8998 }
90- cpOff = off ;
91- this .constantPoolEndOffset = cpOff ;
99+ this . constantPoolEndOffset = off ;
100+ this .constantPoolUtf8EntryOffsets = Arrays . copyOf ( utf8EntryOffsets , utf8Entries ) ;
92101 }
93- this .accessFlags = u16 (bytes , cpOff + Offsets .pastCpAccessFlagsU16 );
94- this .thisClassIndex = u16 (bytes , cpOff + Offsets .pastCpThisClassU16 );
95- this .superClassIndex = u16 (bytes , cpOff + Offsets .pastCpSuperClassU16 );
96- this .interfacesCount = u16 (bytes , cpOff + Offsets .pastCpInterfacesCountU16 );
97- this .interfaceIndices = new int [this .interfacesCount ];
98- List <String > interfaceNames = new ArrayList <>(this .interfacesCount );
102+ this .accessFlags = u16 (bytes , this .constantPoolEndOffset + Offsets .pastCpAccessFlagsU16 );
103+ this .thisClassIndex = u16 (bytes , this .constantPoolEndOffset + Offsets .pastCpThisClassU16 );
104+ this .superClassIndex = u16 (bytes , this .constantPoolEndOffset + Offsets .pastCpSuperClassU16 );
105+ this .interfacesCount = u16 (bytes , this .constantPoolEndOffset + Offsets .pastCpInterfacesCountU16 );
99106
100107 // Parse this&super names
101108 if (constantPoolEntryTypes [thisClassIndex - 1 ] != ConstantPoolEntryTypes .Class ) {
@@ -121,8 +128,10 @@ public ClassHeaderMetadata(byte @NotNull [] bytes) {
121128 }
122129
123130 // Parse interface names
131+ List <String > interfaceNames = new ArrayList <>(this .interfacesCount );
132+ this .interfaceIndices = new int [this .interfacesCount ];
124133 for (int i = 0 ; i < this .interfacesCount ; i ++) {
125- final int interfaceOffset = cpOff + Offsets .pastCpInterfacesList + i * 2 ;
134+ final int interfaceOffset = this . constantPoolEndOffset + Offsets .pastCpInterfacesList + i * 2 ;
126135 final int interfaceIndex = u16 (bytes , interfaceOffset );
127136 if (constantPoolEntryTypes [interfaceIndex - 1 ] != ConstantPoolEntryTypes .Class ) {
128137 throw new IllegalArgumentException ("Interface " + i + " index is not a class ref" );
@@ -314,11 +323,26 @@ public static int majorVersion(byte @NotNull [] classBytes) {
314323 }
315324
316325 /**
317- * Searches for a sub"string" (byte array) in a longer byte array. Not efficient for long search strings.
318- * @param classBytes The long byte string to search in.
319- * @param substring The short substring to search for.
320- * @return If the substring was found somewhere in the long string.
326+ * Searches for byte patterns in the constant pool.
327+ * @param matcher A configured byte matcher with patterns to search for.
328+ * @return {@code true} if there is a match for at least one constant pool entry.
321329 */
330+ public boolean matchesBytes (final BytePatternMatcher matcher ) {
331+ for (final int offset : constantPoolUtf8EntryOffsets ) {
332+ // first byte is entry type, second and third bytes are length
333+ final int length = u16 (classBytes , offset + 1 );
334+ final int start = offset + 3 ;
335+
336+ if (matcher .matches (classBytes , start , length )) {
337+ return true ;
338+ }
339+ }
340+
341+ return false ;
342+ }
343+
344+ /** @deprecated This method is very slow, use {@link #matchesBytes} instead */
345+ @ Deprecated
322346 public static boolean hasSubstring (final byte @ Nullable [] classBytes , final byte @ NotNull [] substring ) {
323347 if (classBytes == null ) {
324348 return false ;
0 commit comments