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 26package jdk.tools.jlink.internal; 27 28import java.nio.ByteOrder; 29import java.util.ArrayList; 30import java.util.List; 31import java.util.Objects; 32import jdk.internal.jimage.ImageHeader; 33import jdk.internal.jimage.ImageStream; 34import jdk.internal.jimage.ImageStringsReader; 35 36public final class BasicImageWriter { 37 public static final String MODULES_IMAGE_NAME = "modules"; 38 39 private final static int RETRY_LIMIT = 1000; 40 41 private ByteOrder byteOrder; 42 private ImageStringsWriter strings; 43 private int length; 44 private int[] redirect; 45 private ImageLocationWriter[] locations; 46 private List<ImageLocationWriter> input; 47 private ImageStream headerStream; 48 private ImageStream redirectStream; 49 private ImageStream locationOffsetStream; 50 private ImageStream locationStream; 51 private ImageStream allIndexStream; 52 53 public BasicImageWriter() { 54 this(ByteOrder.nativeOrder()); 55 } 56 57 public BasicImageWriter(ByteOrder byteOrder) { 58 this.byteOrder = Objects.requireNonNull(byteOrder); 59 this.input = new ArrayList<>(); 60 this.strings = new ImageStringsWriter(); 61 this.headerStream = new ImageStream(byteOrder); 62 this.redirectStream = new ImageStream(byteOrder); 63 this.locationOffsetStream = new ImageStream(byteOrder); 64 this.locationStream = new ImageStream(byteOrder); 65 this.allIndexStream = new ImageStream(byteOrder); 66 } 67 68 public ByteOrder getByteOrder() { 69 return byteOrder; 70 } 71 72 public int addString(String string) { 73 return strings.add(string); 74 } 75 76 public String getString(int offset) { 77 return strings.get(offset); 78 } 79 80 public void addLocation(String fullname, long contentOffset, 81 long compressedSize, long uncompressedSize) { 82 ImageLocationWriter location = 83 ImageLocationWriter.newLocation(fullname, strings, 84 contentOffset, compressedSize, uncompressedSize); 85 input.add(location); 86 length++; 87 } 88 89 ImageLocationWriter[] getLocations() { 90 return locations; 91 } 92 93 int getLocationsCount() { 94 return input.size(); 95 } 96 97 private void generatePerfectHash() { 98 PerfectHashBuilder<ImageLocationWriter> builder = 99 new PerfectHashBuilder<>( 100 PerfectHashBuilder.Entry.class, 101 PerfectHashBuilder.Bucket.class); 102 103 input.forEach((location) -> { 104 builder.put(location.getFullName(), location); 105 }); 106 107 builder.generate(); 108 109 length = builder.getCount(); 110 redirect = builder.getRedirect(); 111 PerfectHashBuilder.Entry<ImageLocationWriter>[] order = builder.getOrder(); 112 locations = new ImageLocationWriter[length]; 113 114 for (int i = 0; i < length; i++) { 115 locations[i] = order[i].getValue(); 116 } 117 } 118 119 private void prepareStringBytes() { 120 strings.getStream().align(2); 121 } 122 123 private void prepareRedirectBytes() { 124 for (int i = 0; i < length; i++) { 125 redirectStream.putInt(redirect[i]); 126 } 127 } 128 129 private void prepareLocationBytes() { 130 // Reserve location offset zero for empty locations 131 locationStream.put(ImageLocationWriter.ATTRIBUTE_END << 3); 132 133 for (int i = 0; i < length; i++) { 134 ImageLocationWriter location = locations[i]; 135 136 if (location != null) { 137 location.writeTo(locationStream); 138 } 139 } 140 141 locationStream.align(2); 142 } 143 144 private void prepareOffsetBytes() { 145 for (int i = 0; i < length; i++) { 146 ImageLocationWriter location = locations[i]; 147 int offset = location != null ? location.getLocationOffset() : 0; 148 locationOffsetStream.putInt(offset); 149 } 150 } 151 152 private void prepareHeaderBytes() { 153 ImageHeader header = new ImageHeader(input.size(), length, 154 locationStream.getSize(), strings.getSize()); 155 header.writeTo(headerStream); 156 } 157 158 private void prepareTableBytes() { 159 allIndexStream.put(headerStream); 160 allIndexStream.put(redirectStream); 161 allIndexStream.put(locationOffsetStream); 162 allIndexStream.put(locationStream); 163 allIndexStream.put(strings.getStream()); 164 } 165 166 public byte[] getBytes() { 167 if (allIndexStream.getSize() == 0) { 168 generatePerfectHash(); 169 prepareStringBytes(); 170 prepareRedirectBytes(); 171 prepareLocationBytes(); 172 prepareOffsetBytes(); 173 prepareHeaderBytes(); 174 prepareTableBytes(); 175 } 176 177 return allIndexStream.toArray(); 178 } 179 180 ImageLocationWriter find(String key) { 181 int index = redirect[ImageStringsReader.hashCode(key) % length]; 182 183 if (index < 0) { 184 index = -index - 1; 185 } else { 186 index = ImageStringsReader.hashCode(key, index) % length; 187 } 188 189 return locations[index]; 190 } 191} 192