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.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 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 
BasicImageWriter()53     public BasicImageWriter() {
54         this(ByteOrder.nativeOrder());
55     }
56 
BasicImageWriter(ByteOrder byteOrder)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 
getByteOrder()68     public ByteOrder getByteOrder() {
69         return byteOrder;
70     }
71 
addString(String string)72     public int addString(String string) {
73         return strings.add(string);
74     }
75 
getString(int offset)76     public String getString(int offset) {
77         return strings.get(offset);
78     }
79 
addLocation(String fullname, long contentOffset, long compressedSize, long uncompressedSize)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 
getLocations()89     ImageLocationWriter[] getLocations() {
90         return locations;
91     }
92 
getLocationsCount()93     int getLocationsCount() {
94         return input.size();
95     }
96 
generatePerfectHash()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 
prepareStringBytes()119     private void prepareStringBytes() {
120         strings.getStream().align(2);
121     }
122 
prepareRedirectBytes()123     private void prepareRedirectBytes() {
124         for (int i = 0; i < length; i++) {
125             redirectStream.putInt(redirect[i]);
126         }
127     }
128 
prepareLocationBytes()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 
prepareOffsetBytes()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 
prepareHeaderBytes()152     private void prepareHeaderBytes() {
153         ImageHeader header = new ImageHeader(input.size(), length,
154                 locationStream.getSize(), strings.getSize());
155         header.writeTo(headerStream);
156     }
157 
prepareTableBytes()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 
getBytes()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 
find(String key)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