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