| /* |
| * Copyright (c) 2014, 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 |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.tools.jlink.internal; |
| |
| import java.nio.ByteOrder; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| import jdk.internal.jimage.ImageHeader; |
| import jdk.internal.jimage.ImageStream; |
| import jdk.internal.jimage.ImageStringsReader; |
| |
| public final class BasicImageWriter { |
| public static final String MODULES_IMAGE_NAME = "modules"; |
| |
| private final static int RETRY_LIMIT = 1000; |
| |
| private ByteOrder byteOrder; |
| private ImageStringsWriter strings; |
| private int length; |
| private int[] redirect; |
| private ImageLocationWriter[] locations; |
| private List<ImageLocationWriter> input; |
| private ImageStream headerStream; |
| private ImageStream redirectStream; |
| private ImageStream locationOffsetStream; |
| private ImageStream locationStream; |
| private ImageStream allIndexStream; |
| |
| public BasicImageWriter() { |
| this(ByteOrder.nativeOrder()); |
| } |
| |
| public BasicImageWriter(ByteOrder byteOrder) { |
| this.byteOrder = Objects.requireNonNull(byteOrder); |
| this.input = new ArrayList<>(); |
| this.strings = new ImageStringsWriter(); |
| this.headerStream = new ImageStream(byteOrder); |
| this.redirectStream = new ImageStream(byteOrder); |
| this.locationOffsetStream = new ImageStream(byteOrder); |
| this.locationStream = new ImageStream(byteOrder); |
| this.allIndexStream = new ImageStream(byteOrder); |
| } |
| |
| public ByteOrder getByteOrder() { |
| return byteOrder; |
| } |
| |
| public int addString(String string) { |
| return strings.add(string); |
| } |
| |
| public String getString(int offset) { |
| return strings.get(offset); |
| } |
| |
| public void addLocation(String fullname, long contentOffset, |
| long compressedSize, long uncompressedSize) { |
| ImageLocationWriter location = |
| ImageLocationWriter.newLocation(fullname, strings, |
| contentOffset, compressedSize, uncompressedSize); |
| input.add(location); |
| length++; |
| } |
| |
| ImageLocationWriter[] getLocations() { |
| return locations; |
| } |
| |
| int getLocationsCount() { |
| return input.size(); |
| } |
| |
| private void generatePerfectHash() { |
| PerfectHashBuilder<ImageLocationWriter> builder = |
| new PerfectHashBuilder<>( |
| PerfectHashBuilder.Entry.class, |
| PerfectHashBuilder.Bucket.class); |
| |
| input.forEach((location) -> { |
| builder.put(location.getFullName(), location); |
| }); |
| |
| builder.generate(); |
| |
| length = builder.getCount(); |
| redirect = builder.getRedirect(); |
| PerfectHashBuilder.Entry<ImageLocationWriter>[] order = builder.getOrder(); |
| locations = new ImageLocationWriter[length]; |
| |
| for (int i = 0; i < length; i++) { |
| locations[i] = order[i].getValue(); |
| } |
| } |
| |
| private void prepareStringBytes() { |
| strings.getStream().align(2); |
| } |
| |
| private void prepareRedirectBytes() { |
| for (int i = 0; i < length; i++) { |
| redirectStream.putInt(redirect[i]); |
| } |
| } |
| |
| private void prepareLocationBytes() { |
| // Reserve location offset zero for empty locations |
| locationStream.put(ImageLocationWriter.ATTRIBUTE_END << 3); |
| |
| for (int i = 0; i < length; i++) { |
| ImageLocationWriter location = locations[i]; |
| |
| if (location != null) { |
| location.writeTo(locationStream); |
| } |
| } |
| |
| locationStream.align(2); |
| } |
| |
| private void prepareOffsetBytes() { |
| for (int i = 0; i < length; i++) { |
| ImageLocationWriter location = locations[i]; |
| int offset = location != null ? location.getLocationOffset() : 0; |
| locationOffsetStream.putInt(offset); |
| } |
| } |
| |
| private void prepareHeaderBytes() { |
| ImageHeader header = new ImageHeader(input.size(), length, |
| locationStream.getSize(), strings.getSize()); |
| header.writeTo(headerStream); |
| } |
| |
| private void prepareTableBytes() { |
| allIndexStream.put(headerStream); |
| allIndexStream.put(redirectStream); |
| allIndexStream.put(locationOffsetStream); |
| allIndexStream.put(locationStream); |
| allIndexStream.put(strings.getStream()); |
| } |
| |
| public byte[] getBytes() { |
| if (allIndexStream.getSize() == 0) { |
| generatePerfectHash(); |
| prepareStringBytes(); |
| prepareRedirectBytes(); |
| prepareLocationBytes(); |
| prepareOffsetBytes(); |
| prepareHeaderBytes(); |
| prepareTableBytes(); |
| } |
| |
| return allIndexStream.toArray(); |
| } |
| |
| ImageLocationWriter find(String key) { |
| int index = redirect[ImageStringsReader.hashCode(key) % length]; |
| |
| if (index < 0) { |
| index = -index - 1; |
| } else { |
| index = ImageStringsReader.hashCode(key, index) % length; |
| } |
| |
| return locations[index]; |
| } |
| } |