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