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