|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  */ | 
|  |  | 
|  | 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); | 
|  |     } | 
|  | } |