Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8260337: Optimize ImageReader lookup, used by Class.getResource
Reviewed-by: jlaskey, sundar
  • Loading branch information
cl4es committed Feb 9, 2021
1 parent f0bd9db commit 2f893c2
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 130 deletions.
12 changes: 8 additions & 4 deletions src/java.base/share/classes/java/lang/Class.java
Expand Up @@ -3048,15 +3048,19 @@ private static void checkPackageAccessForPermittedSubclasses(SecurityManager sm,
}

/**
* Add a package name prefix if the name is not absolute Remove leading "/"
* Add a package name prefix if the name is not absolute. Remove leading "/"
* if name is absolute
*/
private String resolveName(String name) {
if (!name.startsWith("/")) {
Class<?> c = isArray() ? elementType() : this;
String baseName = c.getPackageName();
String baseName = getPackageName();
if (baseName != null && !baseName.isEmpty()) {
name = baseName.replace('.', '/') + "/" + name;
int len = baseName.length() + 1 + name.length();
StringBuilder sb = new StringBuilder(len);
name = sb.append(baseName.replace('.', '/'))
.append('/')
.append(name)
.toString();
}
} else {
name = name.substring(1);
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -249,57 +249,69 @@ public ImageStringsReader getStrings() {
return stringsReader;
}

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);

public ImageLocation findLocation(String module, String name) {
int index = getLocationIndex(module, name);
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));

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.
public ImageLocation findLocation(String name) {
int index = getLocationIndex(name);
if (index < 0) {
return null;
}
long[] attributes = getAttributes(offsets.get(index));
if (!ImageLocation.verify(name, attributes, stringsReader)) {
return null;
}
return new ImageLocation(attributes, stringsReader);
}

public boolean verifyLocation(String module, String name) {
int index = getLocationIndex(module, name);
if (index < 0) {
return false;
}
int locationOffset = offsets.get(index);
return ImageLocation.verify(module, name, locations, locationOffset, stringsReader);
}

// Details of the algorithm used here can be found in
// jdk.tools.jlink.internal.PerfectHashBuilder.
public int getLocationIndex(String name) {
int count = header.getTableLength();
int index = redirect.get(ImageStringsReader.hashCode(name) % count);

if (index < 0) {
// index is twos complement of location attributes index.
index = -index - 1;
return -index - 1;
} else if (index > 0) {
// index is hash seed needed to compute location attributes index.
index = ImageStringsReader.hashCode(name, index) % count;
return ImageStringsReader.hashCode(name, index) % count;
} else {
// No entry.
return null;
return -1;
}
}

long[] attributes = getAttributes(offsets.get(index));

if (!ImageLocation.verify(name, attributes, stringsReader)) {
return null;
private int getLocationIndex(String module, String name) {
int count = header.getTableLength();
int index = redirect.get(ImageStringsReader.hashCode(module, name) % count);
if (index < 0) {
// index is twos complement of location attributes index.
return -index - 1;
} else if (index > 0) {
// index is hash seed needed to compute location attributes index.
return ImageStringsReader.hashCode(module, name, index) % count;
} else {
// No entry.
return -1;
}
return new ImageLocation(attributes, stringsReader);
}

public String[] getEntryNames() {
Expand All @@ -320,18 +332,21 @@ public long[] getAttributes(int offset) {
if (offset < 0 || offset >= locations.limit()) {
throw new IndexOutOfBoundsException("offset");
}

ByteBuffer buffer = slice(locations, offset, locations.limit() - offset);
return ImageLocation.decompress(buffer);
return ImageLocation.decompress(locations, offset);
}

public String getString(int offset) {
if (offset < 0 || offset >= strings.limit()) {
throw new IndexOutOfBoundsException("offset");
}
return ImageStringsReader.stringFromByteBuffer(strings, offset);
}

ByteBuffer buffer = slice(strings, offset, strings.limit() - offset);
return ImageStringsReader.stringFromByteBuffer(buffer);
public int match(int offset, String string, int stringOffset) {
if (offset < 0 || offset >= strings.limit()) {
throw new IndexOutOfBoundsException("offset");
}
return ImageStringsReader.stringFromByteBufferMatches(strings, offset, string, stringOffset);
}

private byte[] getBufferBytes(ByteBuffer buffer) {
Expand Down
161 changes: 99 additions & 62 deletions src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -59,41 +59,26 @@ ImageStrings getStrings() {
return strings;
}

static long[] decompress(ByteBuffer bytes) {
static long[] decompress(ByteBuffer bytes, int offset) {
Objects.requireNonNull(bytes);
long[] attributes = new long[ATTRIBUTE_COUNT];

if (bytes != null) {
while (bytes.hasRemaining()) {
int data = bytes.get() & 0xFF;
int kind = data >>> 3;

if (kind == ATTRIBUTE_END) {
break;
}

if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) {
throw new InternalError(
"Invalid jimage attribute kind: " + kind);
}

int length = (data & 0x7) + 1;
long value = 0;

for (int j = 0; j < length; j++) {
value <<= 8;

if (!bytes.hasRemaining()) {
throw new InternalError("Missing jimage attribute data");
}

value |= bytes.get() & 0xFF;
}

attributes[kind] = value;
int limit = bytes.limit();
while (offset < limit) {
int data = bytes.get(offset++) & 0xFF;
if (data <= 0x7) { // ATTRIBUTE_END
break;
}
int kind = data >>> 3;
if (ATTRIBUTE_COUNT <= kind) {
throw new InternalError(
"Invalid jimage attribute kind: " + kind);
}
}

int length = (data & 0x7) + 1;
attributes[kind] = readValue(length, bytes, offset, limit);
offset += length;
}
return attributes;
}

Expand Down Expand Up @@ -126,71 +111,125 @@ public boolean verify(String name) {
/**
* 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.
* fewer objects.
*/
static boolean verify(String name, long[] attributes, 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();
if (moduleOffset != 0 && length >= 1) {
int moduleLen = strings.match(moduleOffset, name, 1);
index = moduleLen + 1;
if (length <= index
if (moduleLen < 0
|| length <= index
|| name.charAt(0) != '/'
|| !name.regionMatches(1, module, 0, moduleLen)
|| name.charAt(index++) != '/') {
return false;
}
}
return verifyName(null, name, index, length, 0,
(int) attributes[ATTRIBUTE_PARENT],
(int) attributes[ATTRIBUTE_BASE],
(int) attributes[ATTRIBUTE_EXTENSION],
strings);
}

return verifyName(name, index, length, attributes, strings);
static boolean verify(String module, String name, ByteBuffer locations,
int locationOffset, ImageStrings strings) {
int moduleOffset = 0;
int parentOffset = 0;
int baseOffset = 0;
int extOffset = 0;

int limit = locations.limit();
while (locationOffset < limit) {
int data = locations.get(locationOffset++) & 0xFF;
if (data <= 0x7) { // ATTRIBUTE_END
break;
}
int kind = data >>> 3;
if (ATTRIBUTE_COUNT <= kind) {
throw new InternalError(
"Invalid jimage attribute kind: " + kind);
}

int length = (data & 0x7) + 1;
switch (kind) {
case ATTRIBUTE_MODULE:
moduleOffset = (int) readValue(length, locations, locationOffset, limit);
break;
case ATTRIBUTE_BASE:
baseOffset = (int) readValue(length, locations, locationOffset, limit);
break;
case ATTRIBUTE_PARENT:
parentOffset = (int) readValue(length, locations, locationOffset, limit);
break;
case ATTRIBUTE_EXTENSION:
extOffset = (int) readValue(length, locations, locationOffset, limit);
break;
}
locationOffset += length;
}
return verifyName(module, name, 0, name.length(),
moduleOffset, parentOffset, baseOffset, extOffset, strings);
}

private static long readValue(int length, ByteBuffer buffer, int offset, int limit) {
long value = 0;
for (int j = 0; j < length; j++) {
value <<= 8;
if (offset >= limit) {
throw new InternalError("Missing jimage attribute data");
}
value |= buffer.get(offset++) & 0xFF;
}
return value;
}

static boolean verify(String module, String name, long[] attributes,
ImageStrings strings) {
Objects.requireNonNull(module);
Objects.requireNonNull(name);
int moduleOffset = (int)attributes[ATTRIBUTE_MODULE];
return verifyName(module, name, 0, name.length(),
(int) attributes[ATTRIBUTE_MODULE],
(int) attributes[ATTRIBUTE_PARENT],
(int) attributes[ATTRIBUTE_BASE],
(int) attributes[ATTRIBUTE_EXTENSION],
strings);
}

private static boolean verifyName(String module, String name, int index, int length,
int moduleOffset, int parentOffset, int baseOffset, int extOffset, ImageStrings strings) {

if (moduleOffset != 0) {
if (!module.equals(strings.get(moduleOffset))) {
if (strings.match(moduleOffset, module, 0) != module.length()) {
return false;
}
}

return verifyName(name, 0, name.length(), attributes, strings);
}

private static boolean verifyName(String name, int index, int length,
long[] attributes, 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)) {
int parentLen = strings.match(parentOffset, name, index);
if (parentLen < 0) {
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)) {
int baseLen = strings.match(baseOffset, name, index);
if (baseLen < 0) {
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)) {
|| name.charAt(index++) != '.') {
return false;
}

int extLen = strings.match(extOffset, name, index);
if (extLen < 0) {
return false;
}
index += extLen;
Expand All @@ -203,7 +242,6 @@ long getAttribute(int kind) {
throw new InternalError(
"Invalid jimage attribute kind: " + kind);
}

return attributes[kind];
}

Expand All @@ -212,7 +250,6 @@ String getAttributeString(int kind) {
throw new InternalError(
"Invalid jimage attribute kind: " + kind);
}

return getStrings().get((int)attributes[kind]);
}

Expand Down

1 comment on commit 2f893c2

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.