1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.internal.jimage; 26 27 import java.io.IOException; 28 import java.nio.ByteBuffer; 29 import java.nio.ByteOrder; 30 import java.nio.IntBuffer; 31 import java.nio.MappedByteBuffer; 32 import java.nio.channels.FileChannel; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.List; 36 37 public class BasicImageReader { 38 private final String imagePath; 39 private final PReader preader; 40 private final ByteOrder byteOrder; 41 private final ImageHeader header; 42 private final int indexSize; 43 private final IntBuffer redirectBuffer; 44 private final IntBuffer offsetsBuffer; 45 private final ByteBuffer locationsBuffer; 46 private final ByteBuffer stringsBuffer; 47 private final ImageStrings strings; 48 49 protected BasicImageReader(String imagePath, ByteOrder byteOrder) throws IOException { 50 this.imagePath = imagePath; 51 this.preader = PReader.open(imagePath); 52 this.byteOrder = byteOrder; 53 this.header = ImageHeader.readFrom(byteOrder, getIntBuffer(0, ImageHeader.getHeaderSize())); 54 this.indexSize = header.getIndexSize(); 55 this.redirectBuffer = getIntBuffer(header.getRedirectOffset(), header.getRedirectSize()); 56 this.offsetsBuffer = getIntBuffer(header.getOffsetsOffset(), header.getOffsetsSize()); 57 this.locationsBuffer = getByteBuffer(header.getLocationsOffset(), header.getLocationsSize()); 58 this.stringsBuffer = getByteBuffer(header.getStringsOffset(), header.getStringsSize()); 59 this.strings = new ImageStrings(new ImageStream(stringsBuffer)); 60 } 61 62 protected BasicImageReader(String imagePath) throws IOException { 63 this(imagePath, ByteOrder.nativeOrder()); 64 } 65 66 public static BasicImageReader open(String imagePath) throws IOException { 67 return new BasicImageReader(imagePath, ByteOrder.nativeOrder()); 68 } 69 70 public String imagePath() { 71 return imagePath; 72 } 73 74 public boolean isOpen() { 75 return preader.isOpen(); 76 } 77 78 public void close() throws IOException { 79 preader.close(); 80 } 81 82 public ImageHeader getHeader() { 83 return header; 84 } 85 86 public ImageLocation findLocation(String name) { 87 return findLocation(new UTF8String(name)); 88 } 89 90 public ImageLocation findLocation(byte[] name) { 91 return findLocation(new UTF8String(name)); 92 } 93 94 public synchronized ImageLocation findLocation(UTF8String name) { 95 int count = header.getLocationCount(); 96 int hash = name.hashCode() % count; 97 int redirect = getRedirect(hash); 98 99 if (redirect == 0) { 100 return null; 101 } 102 103 int index; 104 105 if (redirect < 0) { 106 // If no collision. 107 index = -redirect - 1; 108 } else { 109 // If collision, recompute hash code. 110 index = name.hashCode(redirect) % count; 111 } 112 113 int offset = getOffset(index); 114 115 if (offset == 0) { 116 return null; 117 } 118 119 ImageLocation location = getLocation(offset); 120 121 return location.verify(name) ? location : null; 122 } 123 124 public String[] getEntryNames() { 125 return getEntryNames(true); 126 } 127 128 public String[] getEntryNames(boolean sorted) { 129 int count = header.getLocationCount(); 130 List<String> list = new ArrayList<>(); 131 132 for (int i = 0; i < count; i++) { 133 int offset = getOffset(i); 134 135 if (offset != 0) { 136 ImageLocation location = ImageLocation.readFrom(locationsBuffer, offset, strings); 137 list.add(location.getFullnameString()); 138 } 139 } 140 141 String[] array = list.toArray(new String[0]); 142 143 if (sorted) { 144 Arrays.sort(array); 145 } 146 147 return array; 148 } 149 150 protected ImageLocation[] getAllLocations(boolean sorted) { 151 int count = header.getLocationCount(); 152 List<ImageLocation> list = new ArrayList<>(); 153 154 for (int i = 0; i < count; i++) { 155 int offset = getOffset(i); 156 157 if (offset != 0) { 158 ImageLocation location = ImageLocation.readFrom(locationsBuffer, offset, strings); 159 list.add(location); 160 } 161 } 162 163 ImageLocation[] array = list.toArray(new ImageLocation[0]); 164 165 if (sorted) { 166 Arrays.sort(array, (ImageLocation loc1, ImageLocation loc2) -> 167 loc1.getFullnameString().compareTo(loc2.getFullnameString())); 168 } 169 170 return array; 171 } 172 173 private IntBuffer getIntBuffer(long offset, long size) throws IOException { 174 MappedByteBuffer buffer = preader.channel().map(FileChannel.MapMode.READ_ONLY, offset, size); 175 buffer.order(byteOrder); 176 177 return buffer.asIntBuffer(); 178 } 179 180 private ByteBuffer getByteBuffer(long offset, long size) throws IOException { 181 MappedByteBuffer buffer = preader.channel().map(FileChannel.MapMode.READ_ONLY, offset, size); 182 // order is not copied into the readonly copy. 183 ByteBuffer readOnly = buffer.asReadOnlyBuffer(); 184 readOnly.order(byteOrder); 185 return readOnly; 186 } 187 188 private int getRedirect(int index) { 189 return redirectBuffer.get(index); 190 } 191 192 private int getOffset(int index) { 193 return offsetsBuffer.get(index); 194 } 195 196 private ImageLocation getLocation(int offset) { 197 return ImageLocation.readFrom(locationsBuffer, offset, strings); 198 } 199 200 public String getString(int offset) { 201 return strings.get(offset).toString(); 202 } 203 204 public byte[] getResource(ImageLocation loc) throws IOException { 205 long compressedSize = loc.getCompressedSize(); 206 assert compressedSize < Integer.MAX_VALUE; 207 208 if (compressedSize == 0) { 209 return preader.read((int)loc.getUncompressedSize(), 210 indexSize + loc.getContentOffset()); 211 } else { 212 byte[] buf = preader.read((int)compressedSize, 213 indexSize + loc.getContentOffset()); 214 return ImageFile.Compressor.decompress(buf); 215 } 216 } 217 218 public byte[] getResource(String name) throws IOException { 219 ImageLocation location = findLocation(name); 220 221 return location != null ? getResource(location) : null; 222 } 223 224 public List<String> getNames(String name) throws IOException { 225 return getNames(getResource(name)); 226 } 227 228 public List<String> getNames(byte[] bytes) { 229 IntBuffer buffer = ByteBuffer.wrap(bytes).asIntBuffer(); 230 List<String> names = new ArrayList<>(); 231 232 while (buffer.hasRemaining()) { 233 int offset = buffer.get(); 234 names.add(getString(offset)); 235 } 236 237 return names; 238 } 239 }