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}