blob: 37f09160b87fe2b822662020b59857fd006efe12 [file] [log] [blame]
/*
* 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];
}
}