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