001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.harmony.unpack200;
020
021import java.io.ByteArrayInputStream;
022import java.io.IOException;
023import java.io.InputStream;
024
025import org.apache.commons.compress.harmony.pack200.BHSDCodec;
026import org.apache.commons.compress.harmony.pack200.Codec;
027import org.apache.commons.compress.harmony.pack200.Pack200Exception;
028import org.apache.commons.compress.utils.IOUtils;
029
030/**
031 * SegmentHeader is the header band of a {@link Segment}
032 */
033public class SegmentHeader {
034
035    private static final byte[] EMPTY_BYTE_ARRAY = {};
036
037    /**
038     * The magic header for a Pack200 Segment is 0xCAFED00D. I wonder where they get their inspiration from ...
039     */
040    private static final int[] magic = { 0xCA, 0xFE, 0xD0, 0x0D };
041
042    private int archiveMajor;
043
044    private int archiveMinor;
045
046    private long archiveModtime;
047
048    private long archiveSize;
049
050    private int attributeDefinitionCount;
051
052    private InputStream bandHeadersInputStream;
053
054    private int bandHeadersSize;
055
056    private int classCount;
057
058    private int cpClassCount;
059
060    private int cpDescriptorCount;
061
062    private int cpDoubleCount;
063
064    private int cpFieldCount;
065
066    private int cpFloatCount;
067
068    private int cpIMethodCount;
069
070    private int cpIntCount;
071
072    private int cpLongCount;
073
074    private int cpMethodCount;
075
076    private int cpSignatureCount;
077
078    private int cpStringCount;
079
080    private int cpUTF8Count;
081
082    private int defaultClassMajorVersion;
083
084    private int defaultClassMinorVersion;
085
086    private int innerClassCount;
087
088    private int numberOfFiles;
089
090    private int segmentsRemaining;
091
092    private SegmentOptions options;
093
094    private final Segment segment;
095
096    private int archiveSizeOffset;
097
098    public SegmentHeader(final Segment segment) {
099        this.segment = segment;
100    }
101
102    /**
103     * Decode a scalar from the band file. A scalar is like a band, but does not perform any band code switching.
104     *
105     * @param name  the name of the scalar (primarily for logging/debugging purposes)
106     * @param in    the input stream to read from
107     * @param codec the codec for this scalar
108     * @return the decoded value
109     * @throws IOException      if there is a problem reading from the underlying input stream
110     * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
111     */
112    private int decodeScalar(final String name, final InputStream in, final BHSDCodec codec) throws IOException, Pack200Exception {
113        final int ret = codec.decode(in);
114        segment.log(Segment.LOG_LEVEL_VERBOSE, "Parsed #" + name + " as " + ret);
115        return ret;
116    }
117
118    /**
119     * Decode a number of scalars from the band file. A scalar is like a band, but does not perform any band code switching.
120     *
121     * @param name  the name of the scalar (primarily for logging/debugging purposes)
122     * @param in    the input stream to read from
123     * @param codec the codec for this scalar
124     * @return an array of decoded {@code long[]} values
125     * @throws IOException      if there is a problem reading from the underlying input stream
126     * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
127     */
128    private int[] decodeScalar(final String name, final InputStream in, final BHSDCodec codec, final int n) throws IOException, Pack200Exception {
129        segment.log(Segment.LOG_LEVEL_VERBOSE, "Parsed #" + name + " (" + n + ")");
130        return codec.decodeInts(n, in);
131    }
132
133    public long getArchiveModtime() {
134        return archiveModtime;
135    }
136
137    public long getArchiveSize() {
138        return archiveSize;
139    }
140
141    public int getArchiveSizeOffset() {
142        return archiveSizeOffset;
143    }
144
145    public int getAttributeDefinitionCount() {
146        return attributeDefinitionCount;
147    }
148
149    /**
150     * Obtain the band headers data as an input stream. If no band headers are present, this will return an empty input stream to prevent any further reads
151     * taking place.
152     *
153     * Note that as a stream, data consumed from this input stream can't be re-used. Data is only read from this stream if the encoding is such that additional
154     * information needs to be decoded from the stream itself.
155     *
156     * @return the band headers input stream
157     */
158    public InputStream getBandHeadersInputStream() {
159        if (bandHeadersInputStream == null) {
160            bandHeadersInputStream = new ByteArrayInputStream(EMPTY_BYTE_ARRAY);
161        }
162        return bandHeadersInputStream;
163
164    }
165
166    public int getBandHeadersSize() {
167        return bandHeadersSize;
168    }
169
170    public int getClassCount() {
171        return classCount;
172    }
173
174    public int getCpClassCount() {
175        return cpClassCount;
176    }
177
178    public int getCpDescriptorCount() {
179        return cpDescriptorCount;
180    }
181
182    public int getCpDoubleCount() {
183        return cpDoubleCount;
184    }
185
186    public int getCpFieldCount() {
187        return cpFieldCount;
188    }
189
190    public int getCpFloatCount() {
191        return cpFloatCount;
192    }
193
194    public int getCpIMethodCount() {
195        return cpIMethodCount;
196    }
197
198    public int getCpIntCount() {
199        return cpIntCount;
200    }
201
202    public int getCpLongCount() {
203        return cpLongCount;
204    }
205
206    public int getCpMethodCount() {
207        return cpMethodCount;
208    }
209
210    public int getCpSignatureCount() {
211        return cpSignatureCount;
212    }
213
214    public int getCpStringCount() {
215        return cpStringCount;
216    }
217
218    public int getCpUTF8Count() {
219        return cpUTF8Count;
220    }
221
222    public int getDefaultClassMajorVersion() {
223        return defaultClassMajorVersion;
224    }
225
226    public int getDefaultClassMinorVersion() {
227        return defaultClassMinorVersion;
228    }
229
230    public int getInnerClassCount() {
231        return innerClassCount;
232    }
233
234    public int getNumberOfFiles() {
235        return numberOfFiles;
236    }
237
238    public SegmentOptions getOptions() {
239        return options;
240    }
241
242    public int getSegmentsRemaining() {
243        return segmentsRemaining;
244    }
245
246    private void parseArchiveFileCounts(final InputStream in) throws IOException, Pack200Exception {
247        if (options.hasArchiveFileCounts()) {
248            setArchiveSize((long) decodeScalar("archive_size_hi", in, Codec.UNSIGNED5) << 32 | decodeScalar("archive_size_lo", in, Codec.UNSIGNED5));
249            archiveSizeOffset = in.available();
250            setSegmentsRemaining(decodeScalar("archive_next_count", in, Codec.UNSIGNED5));
251            setArchiveModtime(decodeScalar("archive_modtime", in, Codec.UNSIGNED5));
252            numberOfFiles = decodeScalar("file_count", in, Codec.UNSIGNED5);
253        }
254    }
255
256    private void parseArchiveSpecialCounts(final InputStream in) throws IOException, Pack200Exception {
257        if (getOptions().hasSpecialFormats()) {
258            bandHeadersSize = decodeScalar("band_headers_size", in, Codec.UNSIGNED5);
259            setAttributeDefinitionCount(decodeScalar("attr_definition_count", in, Codec.UNSIGNED5));
260        }
261    }
262
263    private void parseClassCounts(final InputStream in) throws IOException, Pack200Exception {
264        innerClassCount = decodeScalar("ic_count", in, Codec.UNSIGNED5);
265        defaultClassMinorVersion = decodeScalar("default_class_minver", in, Codec.UNSIGNED5);
266        defaultClassMajorVersion = decodeScalar("default_class_majver", in, Codec.UNSIGNED5);
267        classCount = decodeScalar("class_count", in, Codec.UNSIGNED5);
268    }
269
270    private void parseCpCounts(final InputStream in) throws IOException, Pack200Exception {
271        cpUTF8Count = decodeScalar("cp_Utf8_count", in, Codec.UNSIGNED5);
272        if (getOptions().hasCPNumberCounts()) {
273            cpIntCount = decodeScalar("cp_Int_count", in, Codec.UNSIGNED5);
274            cpFloatCount = decodeScalar("cp_Float_count", in, Codec.UNSIGNED5);
275            cpLongCount = decodeScalar("cp_Long_count", in, Codec.UNSIGNED5);
276            cpDoubleCount = decodeScalar("cp_Double_count", in, Codec.UNSIGNED5);
277        }
278        cpStringCount = decodeScalar("cp_String_count", in, Codec.UNSIGNED5);
279        cpClassCount = decodeScalar("cp_Class_count", in, Codec.UNSIGNED5);
280        cpSignatureCount = decodeScalar("cp_Signature_count", in, Codec.UNSIGNED5);
281        cpDescriptorCount = decodeScalar("cp_Descr_count", in, Codec.UNSIGNED5);
282        cpFieldCount = decodeScalar("cp_Field_count", in, Codec.UNSIGNED5);
283        cpMethodCount = decodeScalar("cp_Method_count", in, Codec.UNSIGNED5);
284        cpIMethodCount = decodeScalar("cp_Imethod_count", in, Codec.UNSIGNED5);
285    }
286
287    public void read(final InputStream in) throws IOException, Error, Pack200Exception {
288
289        final int[] word = decodeScalar("archive_magic_word", in, Codec.BYTE1, magic.length);
290        for (int m = 0; m < magic.length; m++) {
291            if (word[m] != magic[m]) {
292                throw new Error("Bad header");
293            }
294        }
295        setArchiveMinorVersion(decodeScalar("archive_minver", in, Codec.UNSIGNED5));
296        setArchiveMajorVersion(decodeScalar("archive_majver", in, Codec.UNSIGNED5));
297        options = new SegmentOptions(decodeScalar("archive_options", in, Codec.UNSIGNED5));
298        parseArchiveFileCounts(in);
299        parseArchiveSpecialCounts(in);
300        parseCpCounts(in);
301        parseClassCounts(in);
302
303        if (getBandHeadersSize() > 0) {
304            setBandHeadersData(IOUtils.readRange(in, getBandHeadersSize()));
305        }
306
307        archiveSizeOffset -= in.available();
308    }
309
310    /**
311     * Sets the major version of this archive.
312     *
313     * @param version the minor version of the archive
314     * @throws Pack200Exception if the major version is not 150
315     */
316    private void setArchiveMajorVersion(final int version) throws Pack200Exception {
317        if (version != 150) {
318            throw new Pack200Exception("Invalid segment major version: " + version);
319        }
320        archiveMajor = version;
321    }
322
323    /**
324     * Sets the minor version of this archive
325     *
326     * @param version the minor version of the archive
327     * @throws Pack200Exception if the minor version is not 7
328     */
329    private void setArchiveMinorVersion(final int version) throws Pack200Exception {
330        if (version != 7) {
331            throw new Pack200Exception("Invalid segment minor version");
332        }
333        archiveMinor = version;
334    }
335
336    public void setArchiveModtime(final long archiveModtime) {
337        this.archiveModtime = archiveModtime;
338    }
339
340    public void setArchiveSize(final long archiveSize) {
341        this.archiveSize = archiveSize;
342    }
343
344    private void setAttributeDefinitionCount(final long valuie) {
345        this.attributeDefinitionCount = (int) valuie;
346    }
347
348    private void setBandHeadersData(final byte[] bandHeaders) {
349        this.bandHeadersInputStream = new ByteArrayInputStream(bandHeaders);
350    }
351
352    public void setSegmentsRemaining(final long value) {
353        segmentsRemaining = (int) value;
354    }
355
356    public void unpack() {
357
358    }
359}