|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package jdk.internal.jimage; |
|
|
|
import java.nio.ByteBuffer; |
|
import java.util.Objects; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class ImageLocation { |
|
public static final int ATTRIBUTE_END = 0; |
|
public static final int ATTRIBUTE_MODULE = 1; |
|
public static final int ATTRIBUTE_PARENT = 2; |
|
public static final int ATTRIBUTE_BASE = 3; |
|
public static final int ATTRIBUTE_EXTENSION = 4; |
|
public static final int ATTRIBUTE_OFFSET = 5; |
|
public static final int ATTRIBUTE_COMPRESSED = 6; |
|
public static final int ATTRIBUTE_UNCOMPRESSED = 7; |
|
public static final int ATTRIBUTE_COUNT = 8; |
|
|
|
protected final long[] attributes; |
|
|
|
protected final ImageStrings strings; |
|
|
|
public ImageLocation(long[] attributes, ImageStrings strings) { |
|
this.attributes = Objects.requireNonNull(attributes); |
|
this.strings = Objects.requireNonNull(strings); |
|
} |
|
|
|
ImageStrings getStrings() { |
|
return strings; |
|
} |
|
|
|
static long[] decompress(ByteBuffer bytes, int offset) { |
|
Objects.requireNonNull(bytes); |
|
long[] attributes = new long[ATTRIBUTE_COUNT]; |
|
|
|
int limit = bytes.limit(); |
|
while (offset < limit) { |
|
int data = bytes.get(offset++) & 0xFF; |
|
if (data <= 0x7) { |
|
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; |
|
} |
|
|
|
public static byte[] compress(long[] attributes) { |
|
Objects.requireNonNull(attributes); |
|
ImageStream stream = new ImageStream(16); |
|
|
|
for (int kind = ATTRIBUTE_END + 1; kind < ATTRIBUTE_COUNT; kind++) { |
|
long value = attributes[kind]; |
|
|
|
if (value != 0) { |
|
int n = (63 - Long.numberOfLeadingZeros(value)) >> 3; |
|
stream.put((kind << 3) | n); |
|
|
|
for (int i = n; i >= 0; i--) { |
|
stream.put((int)(value >> (i << 3))); |
|
} |
|
} |
|
} |
|
|
|
stream.put(ATTRIBUTE_END << 3); |
|
|
|
return stream.toArray(); |
|
} |
|
|
|
public boolean verify(String name) { |
|
return verify(name, attributes, strings); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 && length >= 1) { |
|
int moduleLen = strings.match(moduleOffset, name, 1); |
|
index = moduleLen + 1; |
|
if (moduleLen < 0 |
|
|| length <= index |
|
|| name.charAt(0) != '/' |
|
|| 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); |
|
} |
|
|
|
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) { |
|
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); |
|
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 (strings.match(moduleOffset, module, 0) != module.length()) { |
|
return false; |
|
} |
|
} |
|
if (parentOffset != 0) { |
|
int parentLen = strings.match(parentOffset, name, index); |
|
if (parentLen < 0) { |
|
return false; |
|
} |
|
index += parentLen; |
|
if (length <= index || name.charAt(index++) != '/') { |
|
return false; |
|
} |
|
} |
|
int baseLen = strings.match(baseOffset, name, index); |
|
if (baseLen < 0) { |
|
return false; |
|
} |
|
index += baseLen; |
|
if (extOffset != 0) { |
|
if (length <= index |
|
|| name.charAt(index++) != '.') { |
|
return false; |
|
} |
|
|
|
int extLen = strings.match(extOffset, name, index); |
|
if (extLen < 0) { |
|
return false; |
|
} |
|
index += extLen; |
|
} |
|
return length == index; |
|
} |
|
|
|
long getAttribute(int kind) { |
|
if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { |
|
throw new InternalError( |
|
"Invalid jimage attribute kind: " + kind); |
|
} |
|
return attributes[kind]; |
|
} |
|
|
|
String getAttributeString(int kind) { |
|
if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { |
|
throw new InternalError( |
|
"Invalid jimage attribute kind: " + kind); |
|
} |
|
return getStrings().get((int)attributes[kind]); |
|
} |
|
|
|
public String getModule() { |
|
return getAttributeString(ATTRIBUTE_MODULE); |
|
} |
|
|
|
public int getModuleOffset() { |
|
return (int)getAttribute(ATTRIBUTE_MODULE); |
|
} |
|
|
|
public String getBase() { |
|
return getAttributeString(ATTRIBUTE_BASE); |
|
} |
|
|
|
public int getBaseOffset() { |
|
return (int)getAttribute(ATTRIBUTE_BASE); |
|
} |
|
|
|
public String getParent() { |
|
return getAttributeString(ATTRIBUTE_PARENT); |
|
} |
|
|
|
public int getParentOffset() { |
|
return (int)getAttribute(ATTRIBUTE_PARENT); |
|
} |
|
|
|
public String getExtension() { |
|
return getAttributeString(ATTRIBUTE_EXTENSION); |
|
} |
|
|
|
public int getExtensionOffset() { |
|
return (int)getAttribute(ATTRIBUTE_EXTENSION); |
|
} |
|
|
|
public String getFullName() { |
|
return getFullName(false); |
|
} |
|
|
|
public String getFullName(boolean modulesPrefix) { |
|
StringBuilder builder = new StringBuilder(); |
|
|
|
if (getModuleOffset() != 0) { |
|
if (modulesPrefix) { |
|
builder.append("/modules"); |
|
} |
|
|
|
builder.append('/'); |
|
builder.append(getModule()); |
|
builder.append('/'); |
|
} |
|
|
|
if (getParentOffset() != 0) { |
|
builder.append(getParent()); |
|
builder.append('/'); |
|
} |
|
|
|
builder.append(getBase()); |
|
|
|
if (getExtensionOffset() != 0) { |
|
builder.append('.'); |
|
builder.append(getExtension()); |
|
} |
|
|
|
return builder.toString(); |
|
} |
|
|
|
String buildName(boolean includeModule, boolean includeParent, |
|
boolean includeName) { |
|
StringBuilder builder = new StringBuilder(); |
|
|
|
if (includeModule && getModuleOffset() != 0) { |
|
builder.append("/modules/"); |
|
builder.append(getModule()); |
|
} |
|
|
|
if (includeParent && getParentOffset() != 0) { |
|
builder.append('/'); |
|
builder.append(getParent()); |
|
} |
|
|
|
if (includeName) { |
|
if (includeModule || includeParent) { |
|
builder.append('/'); |
|
} |
|
|
|
builder.append(getBase()); |
|
|
|
if (getExtensionOffset() != 0) { |
|
builder.append('.'); |
|
builder.append(getExtension()); |
|
} |
|
} |
|
|
|
return builder.toString(); |
|
} |
|
|
|
public long getContentOffset() { |
|
return getAttribute(ATTRIBUTE_OFFSET); |
|
} |
|
|
|
public long getCompressedSize() { |
|
return getAttribute(ATTRIBUTE_COMPRESSED); |
|
} |
|
|
|
public long getUncompressedSize() { |
|
return getAttribute(ATTRIBUTE_UNCOMPRESSED); |
|
} |
|
|
|
static ImageLocation readFrom(BasicImageReader reader, int offset) { |
|
Objects.requireNonNull(reader); |
|
long[] attributes = reader.getAttributes(offset); |
|
ImageStringsReader strings = reader.getStrings(); |
|
|
|
return new ImageLocation(attributes, strings); |
|
} |
|
} |