    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.pack200;
    020
    021import java.io.IOException;
    022import java.io.OutputStream;
    023
    024/**
    025 * SegmentHeader is the header band of a {@link Segment}. Corresponds to {@code segment_header} in the pack200 specification.
    026 */
    027public class SegmentHeader extends BandSet {
    028
    029    /**
    030     * Counter for major/minor class file numbers, so we can work out the default
    031     */
    032    private static final class Counter {
    033
    034        private final int[] objs = new int[8];
    035        private final int[] counts = new int[8];
    036        private int length;
    037
    038        public void add(final int obj) {
    039            boolean found = false;
    040            for (int i = 0; i < length; i++) {
    041                if (objs[i] == obj) {
    042                    counts[i]++;
    043                    found = true;
    044                }
    045            }
    046            if (!found) {
    047                objs[length] = obj;
    048                counts[length] = 1;
    049                length++;
    050                if (length > objs.length - 1) {
    051                    final Object[] newArray = new Object[objs.length + 8];
    052                    System.arraycopy(objs, 0, newArray, 0, length);
    053                }
    054            }
    055        }
    056
    057        public int getMostCommon() {
    058            int returnIndex = 0;
    059            for (int i = 0; i < length; i++) {
    060                if (counts[i] > counts[returnIndex]) {
    061                    returnIndex = i;
    062                }
    063            }
    064            return objs[returnIndex];
    065        }
    066    }
    067
    068    private static final int[] magic = { 0xCA, 0xFE, 0xD0, 0x0D };
    069    private static final int archive_minver = 7;
    070    private static final int archive_majver = 150;
    071
    072    private int archive_options;
    073
    074    private int cp_Utf8_count;
    075    private int cp_Int_count;
    076    private int cp_Float_count;
    077    private int cp_Long_count;
    078    private int cp_Double_count;
    079    private int cp_String_count;
    080    private int cp_Class_count;
    081    private int cp_Signature_count;
    082    private int cp_Descr_count;
    083    private int cp_Field_count;
    084    private int cp_Method_count;
    085    private int cp_Imethod_count;
    086
    087    private int attribute_definition_count;
    088    private final IntList band_headers = new IntList();
    089
    090    private boolean have_all_code_flags = true; // true by default
    091
    092    private int archive_size_hi;
    093    private int archive_size_lo;
    094    private int archive_next_count;
    095    private int archive_modtime;
    096    private int file_count;
    097
    098    private boolean deflate_hint;
    099    private final boolean have_file_modtime = true;
    100    private final boolean have_file_options = true;
    101    private boolean have_file_size_hi;
    102    private boolean have_class_flags_hi;
    103    private boolean have_field_flags_hi;
    104    private boolean have_method_flags_hi;
    105    private boolean have_code_flags_hi;
    106
    107    private int ic_count;
    108    private int class_count;
    109    private final Counter majverCounter = new Counter();
    110
    111    /**
    112     * Constructs a new SegmentHeader
    113     */
    114    public SegmentHeader() {
    115        super(1, null); // Pass 1 for effort because bands in the segment header
    116                        // should always use the default encoding
    117    }
    118
    119    public void addMajorVersion(final int major) {
    120        majverCounter.add(major);
    121    }
    122
    123    public void appendBandCodingSpecifier(final int specifier) {
    124        band_headers.add(specifier);
    125    }
    126
    127    private void calculateArchiveOptions() {
    128        if (attribute_definition_count > 0 || band_headers.size() > 0) {
    129            archive_options |= 1;
    130        }
    131        if (cp_Int_count > 0 || cp_Float_count > 0 || cp_Long_count > 0 || cp_Double_count > 0) {
    132            archive_options |= 1 << 1;
    133        }
    134        if (have_all_code_flags) {
    135            archive_options |= 1 << 2;
    136        }
    137        if (file_count > 0) {
    138            archive_options |= 1 << 4;
    139        }
    140        if (deflate_hint) {
    141            archive_options |= 1 << 5;
    142        }
    143        if (have_file_modtime) {
    144            archive_options |= 1 << 6;
    145        }
    146        if (have_file_options) {
    147            archive_options |= 1 << 7;
    148        }
    149        if (have_file_size_hi) {
    150            archive_options |= 1 << 8;
    151        }
    152        if (have_class_flags_hi) {
    153            archive_options |= 1 << 9;
    154        }
    155        if (have_field_flags_hi) {
    156            archive_options |= 1 << 10;
    157        }
    158        if (have_method_flags_hi) {
    159            archive_options |= 1 << 11;
    160        }
    161        if (have_code_flags_hi) {
    162            archive_options |= 1 << 12;
    163        }
    164    }
    165
    166    public int getArchive_modtime() {
    167        return archive_modtime;
    168    }
    169
    170    public int getDefaultMajorVersion() {
    171        return majverCounter.getMostCommon();
    172    }
    173
    174    public boolean have_all_code_flags() {
    175        return have_all_code_flags;
    176    }
    177
    178    public boolean have_class_flags_hi() {
    179        return have_class_flags_hi;
    180    }
    181
    182    public boolean have_code_flags_hi() {
    183        return have_code_flags_hi;
    184    }
    185
    186    public boolean have_field_flags_hi() {
    187        return have_field_flags_hi;
    188    }
    189
    190    public boolean have_file_modtime() {
    191        return have_file_modtime;
    192    }
    193
    194    public boolean have_file_options() {
    195        return have_file_options;
    196    }
    197
    198    public boolean have_file_size_hi() {
    199        return have_file_size_hi;
    200    }
    201
    202    public boolean have_method_flags_hi() {
    203        return have_method_flags_hi;
    204    }
    205
    206    /**
    207     * Encode and write the SegmentHeader bands to the OutputStream
    208     */
    209    @Override
    210    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    211        out.write(encodeScalar(magic, Codec.BYTE1));
    212        out.write(encodeScalar(archive_minver, Codec.UNSIGNED5));
    213        out.write(encodeScalar(archive_majver, Codec.UNSIGNED5));
    214        calculateArchiveOptions();
    215        out.write(encodeScalar(archive_options, Codec.UNSIGNED5));
    216        writeArchiveFileCounts(out);
    217        writeArchiveSpecialCounts(out);
    218        writeCpCounts(out);
    219        writeClassCounts(out);
    220        if (band_headers.size() > 0) {
    221            out.write(encodeScalar(band_headers.toArray(), Codec.BYTE1));
    222        }
    223    }
    224
    225    public void setAttribute_definition_count(final int attribute_definition_count) {
    226        this.attribute_definition_count = attribute_definition_count;
    227    }
    228
    229    public void setClass_count(final int class_count) {
    230        this.class_count = class_count;
    231    }
    232
    233    public void setCp_Class_count(final int count) {
    234        cp_Class_count = count;
    235    }
    236
    237    public void setCp_Descr_count(final int count) {
    238        cp_Descr_count = count;
    239    }
    240
    241    public void setCp_Double_count(final int count) {
    242        cp_Double_count = count;
    243    }
    244
    245    public void setCp_Field_count(final int count) {
    246        cp_Field_count = count;
    247    }
    248
    249    public void setCp_Float_count(final int count) {
    250        cp_Float_count = count;
    251    }
    252
    253    public void setCp_Imethod_count(final int count) {
    254        cp_Imethod_count = count;
    255    }
    256
    257    public void setCp_Int_count(final int count) {
    258        cp_Int_count = count;
    259    }
    260
    261    public void setCp_Long_count(final int count) {
    262        cp_Long_count = count;
    263    }
    264
    265    public void setCp_Method_count(final int count) {
    266        cp_Method_count = count;
    267    }
    268
    269    public void setCp_Signature_count(final int count) {
    270        cp_Signature_count = count;
    271    }
    272
    273    public void setCp_String_count(final int count) {
    274        cp_String_count = count;
    275    }
    276
    277    public void setCp_Utf8_count(final int count) {
    278        cp_Utf8_count = count;
    279    }
    280
    281    public void setDeflate_hint(final boolean deflate_hint) {
    282        this.deflate_hint = deflate_hint;
    283    }
    284
    285    public void setFile_count(final int file_count) {
    286        this.file_count = file_count;
    287    }
    288
    289    public void setHave_all_code_flags(final boolean have_all_code_flags) {
    290        this.have_all_code_flags = have_all_code_flags;
    291    }
    292
    293    public void setHave_class_flags_hi(final boolean have_class_flags_hi) {
    294        this.have_class_flags_hi = have_class_flags_hi;
    295    }
    296
    297    public void setHave_code_flags_hi(final boolean have_code_flags_hi) {
    298        this.have_code_flags_hi = have_code_flags_hi;
    299    }
    300
    301    public void setHave_field_flags_hi(final boolean have_field_flags_hi) {
    302        this.have_field_flags_hi = have_field_flags_hi;
    303    }
    304
    305    public void setHave_method_flags_hi(final boolean have_method_flags_hi) {
    306        this.have_method_flags_hi = have_method_flags_hi;
    307    }
    308
    309    public void setIc_count(final int ic_count) {
    310        this.ic_count = ic_count;
    311    }
    312
    313    private void writeArchiveFileCounts(final OutputStream out) throws IOException, Pack200Exception {
    314        if ((archive_options & 1 << 4) > 0) { // have_file_headers
    315            out.write(encodeScalar(archive_size_hi, Codec.UNSIGNED5));
    316            out.write(encodeScalar(archive_size_lo, Codec.UNSIGNED5));
    317            out.write(encodeScalar(archive_next_count, Codec.UNSIGNED5));
    318            out.write(encodeScalar(archive_modtime, Codec.UNSIGNED5));
    319            out.write(encodeScalar(file_count, Codec.UNSIGNED5));
    320        }
    321    }
    322
    323    private void writeArchiveSpecialCounts(final OutputStream out) throws IOException, Pack200Exception {
    324        if ((archive_options & 1) > 0) { // have_special_formats
    325            out.write(encodeScalar(band_headers.size(), Codec.UNSIGNED5));
    326            out.write(encodeScalar(attribute_definition_count, Codec.UNSIGNED5));
    327        }
    328    }
    329
    330    private void writeClassCounts(final OutputStream out) throws IOException, Pack200Exception {
    331        final int default_class_minver = 0;
    332        final int default_class_majver = majverCounter.getMostCommon();
    333        out.write(encodeScalar(ic_count, Codec.UNSIGNED5));
    334        out.write(encodeScalar(default_class_minver, Codec.UNSIGNED5));
    335        out.write(encodeScalar(default_class_majver, Codec.UNSIGNED5));
    336        out.write(encodeScalar(class_count, Codec.UNSIGNED5));
    337    }
    338
    339    private void writeCpCounts(final OutputStream out) throws IOException, Pack200Exception {
    340        out.write(encodeScalar(cp_Utf8_count, Codec.UNSIGNED5));
    341        if ((archive_options & 1 << 1) != 0) { // have_cp_numbers
    342            out.write(encodeScalar(cp_Int_count, Codec.UNSIGNED5));
    343            out.write(encodeScalar(cp_Float_count, Codec.UNSIGNED5));
    344            out.write(encodeScalar(cp_Long_count, Codec.UNSIGNED5));
    345            out.write(encodeScalar(cp_Double_count, Codec.UNSIGNED5));
    346        }
    347        out.write(encodeScalar(cp_String_count, Codec.UNSIGNED5));
    348        out.write(encodeScalar(cp_Class_count, Codec.UNSIGNED5));
    349        out.write(encodeScalar(cp_Signature_count, Codec.UNSIGNED5));
    350        out.write(encodeScalar(cp_Descr_count, Codec.UNSIGNED5));
    351        out.write(encodeScalar(cp_Field_count, Codec.UNSIGNED5));
    352        out.write(encodeScalar(cp_Method_count, Codec.UNSIGNED5));
    353        out.write(encodeScalar(cp_Imethod_count, Codec.UNSIGNED5));
    354    }
    355
    356}