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}