--- old/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java 2017-02-27 14:02:25.388025495 +0100 +++ new/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java 2017-02-27 14:02:25.140024266 +0100 @@ -249,27 +249,46 @@ return stringsReader; } - public ImageLocation findLocation(String mn, String rn) { - Objects.requireNonNull(mn); - Objects.requireNonNull(rn); + public synchronized ImageLocation findLocation(String module, String name) { + Objects.requireNonNull(module); + Objects.requireNonNull(name); + // Details of the algorithm used here can be found in + // jdk.tools.jlink.internal.PerfectHashBuilder. + int count = header.getTableLength(); + int index = redirect.get(ImageStringsReader.hashCode(module, name) % count); + + if (index < 0) { + // index is twos complement of location attributes index. + index = -index - 1; + } else if (index > 0) { + // index is hash seed needed to compute location attributes index. + index = ImageStringsReader.hashCode(module, name, index) % count; + } else { + // No entry. + return null; + } + + long[] attributes = getAttributes(offsets.get(index)); - return findLocation("/" + mn + "/" + rn); + if (!ImageLocation.verify(module, name, attributes, stringsReader)) { + return null; + } + return new ImageLocation(attributes, stringsReader); } public synchronized ImageLocation findLocation(String name) { Objects.requireNonNull(name); // Details of the algorithm used here can be found in // jdk.tools.jlink.internal.PerfectHashBuilder. - byte[] bytes = ImageStringsReader.mutf8FromString(name); int count = header.getTableLength(); - int index = redirect.get(ImageStringsReader.hashCode(bytes) % count); + int index = redirect.get(ImageStringsReader.hashCode(name) % count); if (index < 0) { // index is twos complement of location attributes index. index = -index - 1; } else if (index > 0) { // index is hash seed needed to compute location attributes index. - index = ImageStringsReader.hashCode(bytes, index) % count; + index = ImageStringsReader.hashCode(name, index) % count; } else { // No entry. return null; @@ -277,13 +296,10 @@ long[] attributes = getAttributes(offsets.get(index)); - ImageLocation imageLocation = new ImageLocation(attributes, stringsReader); - - if (!imageLocation.verify(name)) { + if (!ImageLocation.verify(name, attributes, stringsReader)) { return null; } - - return imageLocation; + return new ImageLocation(attributes, stringsReader); } public String[] getEntryNames() { --- old/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java 2017-02-27 14:02:26.248029760 +0100 +++ new/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java 2017-02-27 14:02:26.000028530 +0100 @@ -59,14 +59,6 @@ return strings; } - private static int attributeLength(int data) { - return (data & 0x7) + 1; - } - - private static int attributeKind(int data) { - return data >>> 3; - } - static long[] decompress(ByteBuffer bytes) { Objects.requireNonNull(bytes); long[] attributes = new long[ATTRIBUTE_COUNT]; @@ -74,7 +66,7 @@ if (bytes != null) { while (bytes.hasRemaining()) { int data = bytes.get() & 0xFF; - int kind = attributeKind(data); + int kind = data >>> 3; if (kind == ATTRIBUTE_END) { break; @@ -85,7 +77,7 @@ "Invalid jimage attribute kind: " + kind); } - int length = attributeLength(data); + int length = (data & 0x7) + 1; long value = 0; for (int j = 0; j < length; j++) { @@ -128,9 +120,83 @@ } public boolean verify(String name) { + return verify(name, attributes, strings); + } + + /** + * A simpler verification would be {@code name.equals(getFullName())}, but + * by not creating the full name and enabling early returns we allocate + * fewer objects. Could possibly be made allocation free by extending + * ImageStrings to test if strings at an offset match the name region. + */ + static boolean verify(String name, final long[] attributes, + final ImageStrings strings) { Objects.requireNonNull(name); + final int length = name.length(); + int index = 0; + int moduleOffset = (int)attributes[ATTRIBUTE_MODULE]; + if (moduleOffset != 0) { + String module = strings.get(moduleOffset); + final int moduleLen = module.length(); + index = moduleLen + 1; + if (length <= index + || name.charAt(0) != '/' + || !name.regionMatches(1, module, 0, moduleLen) + || name.charAt(index++) != '/') { + return false; + } + } + + return verifyName(name, index, length, attributes, strings); + } - return name.equals(getFullName()); + static boolean verify(String module, String name, + final long[] attributes, final ImageStrings strings) { + Objects.requireNonNull(module); + Objects.requireNonNull(name); + int moduleOffset = (int)attributes[ATTRIBUTE_MODULE]; + if (moduleOffset != 0) { + if (!module.equals(strings.get(moduleOffset))) { + return false; + } + } + + return verifyName(name, 0, name.length(), attributes, strings); + } + + private static boolean verifyName(String name, int index, final int length, + final long[] attributes, final ImageStrings strings) { + + int parentOffset = (int) attributes[ATTRIBUTE_PARENT]; + if (parentOffset != 0) { + String parent = strings.get(parentOffset); + final int parentLen = parent.length(); + if (!name.regionMatches(index, parent, 0, parentLen)) { + return false; + } + index += parentLen; + if (length <= index || name.charAt(index++) != '/') { + return false; + } + } + String base = strings.get((int) attributes[ATTRIBUTE_BASE]); + final int baseLen = base.length(); + if (!name.regionMatches(index, base, 0, baseLen)) { + return false; + } + index += baseLen; + int extOffset = (int) attributes[ATTRIBUTE_EXTENSION]; + if (extOffset != 0) { + String extension = strings.get(extOffset); + int extLen = extension.length(); + if (length <= index + || name.charAt(index++) != '.' + || !name.regionMatches(index, extension, 0, extLen)) { + return false; + } + index += extLen; + } + return length == index; } long getAttribute(int kind) { --- old/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java 2017-02-27 14:02:27.112034044 +0100 +++ new/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java 2017-02-27 14:02:26.868032834 +0100 @@ -38,6 +38,8 @@ */ public class ImageStringsReader implements ImageStrings { public static final int HASH_MULTIPLIER = 0x01000193; + public static final int POSITIVE_MASK = 0x7FFFFFFF; + private final BasicImageReader reader; ImageStringsReader(BasicImageReader reader) { @@ -54,40 +56,60 @@ throw new InternalError("Can not add strings at runtime"); } - private static int hashCode(byte[] bytes, int offset, int count, int seed) { - Objects.requireNonNull(bytes); + public static int hashCode(String string) { + return hashCode(string, HASH_MULTIPLIER); + } - if (offset < 0 || count < 0 || offset > bytes.length - count) { - throw new IndexOutOfBoundsException("offset=" + offset + ", count=" + count); - } + public static int hashCode(String string, int seed) { + return unmaskedHashCode(string, seed) & POSITIVE_MASK; + } - int limit = offset + count; + public static int hashCode(String module, String string) { + return hashCode(module, string, HASH_MULTIPLIER); + } - if (limit < 0 || limit > bytes.length) { - throw new IndexOutOfBoundsException("limit=" + limit); - } + public static int hashCode(String module, String string, int seed) { + seed = unmaskedHashCode("/", seed); + seed = unmaskedHashCode(module, seed); + seed = unmaskedHashCode("/", seed); + seed = unmaskedHashCode(string, seed); + return seed & POSITIVE_MASK; + } - for (int i = offset; i < limit; i++) { - seed = (seed * HASH_MULTIPLIER) ^ (bytes[i] & 0xFF); - } + public static int unmaskedHashCode(String string, int seed) { + int slen = string.length(); + byte[] buffer = null; - return seed & 0x7FFFFFFF; - } + for (int i = 0; i < slen; i++) { + char ch = string.charAt(i); + int uch = ch & 0xFFFF; - public static int hashCode(byte[] bytes, int seed) { - return hashCode(bytes, 0, bytes.length, seed); - } + if ((uch & ~0x7F) != 0) { + if (buffer == null) { + buffer = new byte[8]; + } + int mask = ~0x3F; + int n = 0; - public static int hashCode(byte[] bytes) { - return hashCode(bytes, 0, bytes.length, HASH_MULTIPLIER); - } + do { + buffer[n++] = (byte)(0x80 | (uch & 0x3F)); + uch >>= 6; + mask >>= 1; + } while ((uch & mask) != 0); - public static int hashCode(String string, int seed) { - return hashCode(mutf8FromString(string), seed); - } + buffer[n] = (byte)((mask << 1) | uch); - public static int hashCode(String string) { - return hashCode(mutf8FromString(string), HASH_MULTIPLIER); + do { + seed = (seed * HASH_MULTIPLIER) ^ (buffer[n--] & 0xFF); + } while (0 <= n); + } else if (uch == 0) { + seed = (seed * HASH_MULTIPLIER) ^ (0xC0); + seed = (seed * HASH_MULTIPLIER) ^ (0x80); + } else { + seed = (seed * HASH_MULTIPLIER) ^ (uch); + } + } + return seed; } static int charsFromMUTF8Length(byte[] bytes, int offset, int count) { @@ -179,7 +201,7 @@ throw new InternalError("No terminating zero byte for modified UTF-8 byte sequence"); } - static void charsFromByteBuffer(char chars[], ByteBuffer buffer) { + static void charsFromByteBuffer(char[] chars, ByteBuffer buffer) { int j = 0; while(buffer.hasRemaining()) { @@ -228,10 +250,12 @@ return new String(chars); } - static int mutf8FromCharsLength(char chars[]) { + static int mutf8FromStringLength(String s) { int length = 0; + int slen = s.length(); - for (char ch : chars) { + for (int i = 0; i < slen; i++) { + char ch = s.charAt(i); int uch = ch & 0xFFFF; if ((uch & ~0x7F) != 0) { @@ -255,14 +279,19 @@ return length; } - static void mutf8FromChars(byte[] bytes, int offset, char chars[]) { + static void mutf8FromString(byte[] bytes, int offset, String s) { int j = offset; - byte[] buffer = new byte[8]; + byte[] buffer = null; + int slen = s.length(); - for (char ch : chars) { + for (int i = 0; i < slen; i++) { + char ch = s.charAt(i); int uch = ch & 0xFFFF; if ((uch & ~0x7F) != 0) { + if (buffer == null) { + buffer = new byte[8]; + } int mask = ~0x3F; int n = 0; @@ -287,10 +316,9 @@ } public static byte[] mutf8FromString(String string) { - char[] chars = string.toCharArray(); - int length = mutf8FromCharsLength(chars); + int length = mutf8FromStringLength(string); byte[] bytes = new byte[length]; - mutf8FromChars(bytes, 0, chars); + mutf8FromString(bytes, 0, string); return bytes; } --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageLocationWriter.java 2017-02-27 14:02:27.980038349 +0100 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageLocationWriter.java 2017-02-27 14:02:27.736037139 +0100 @@ -108,24 +108,24 @@ int hash = seed; if (getModuleOffset() != 0) { - hash = ImageStringsReader.hashCode("/", hash); - hash = ImageStringsReader.hashCode(getModule(), hash); - hash = ImageStringsReader.hashCode("/", hash); + hash = ImageStringsReader.unmaskedHashCode("/", hash); + hash = ImageStringsReader.unmaskedHashCode(getModule(), hash); + hash = ImageStringsReader.unmaskedHashCode("/", hash); } if (getParentOffset() != 0) { - hash = ImageStringsReader.hashCode(getParent(), hash); - hash = ImageStringsReader.hashCode("/", hash); + hash = ImageStringsReader.unmaskedHashCode(getParent(), hash); + hash = ImageStringsReader.unmaskedHashCode("/", hash); } - hash = ImageStringsReader.hashCode(getBase(), hash); + hash = ImageStringsReader.unmaskedHashCode(getBase(), hash); if (getExtensionOffset() != 0) { - hash = ImageStringsReader.hashCode(".", hash); - hash = ImageStringsReader.hashCode(getExtension(), hash); + hash = ImageStringsReader.unmaskedHashCode(".", hash); + hash = ImageStringsReader.unmaskedHashCode(getExtension(), hash); } - return hash; + return hash & ImageStringsReader.POSITIVE_MASK; } @Override