    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}