• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors
3  * This program and the accompanying materials are made available under
4  * the terms of the Eclipse Public License 2.0 which is available at
5  * http://www.eclipse.org/legal/epl-2.0
6  *
7  * SPDX-License-Identifier: EPL-2.0
8  *
9  * Contributors:
10  *    Marc R. Hoffmann - initial API and implementation
11  *
12  *******************************************************************************/
13 package org.jacoco.core.internal;
14 
15 import java.io.BufferedInputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 
19 /**
20  * Detector for content types of binary streams based on a magic headers.
21  */
22 public class ContentTypeDetector {
23 
24 	/** Unknown file type */
25 	public static final int UNKNOWN = -1;
26 
27 	/** File type Java class */
28 	public static final int CLASSFILE = 0xcafebabe;
29 
30 	/** File type ZIP archive */
31 	public static final int ZIPFILE = 0x504b0304;
32 
33 	/** File type GZIP compressed Data */
34 	public static final int GZFILE = 0x1f8b0000;
35 
36 	/** File type Pack200 archive */
37 	public static final int PACK200FILE = 0xcafed00d;
38 
39 	private static final int BUFFER_SIZE = 8;
40 
41 	private final InputStream in;
42 
43 	private final int type;
44 
45 	/**
46 	 * Creates a new detector based on the given input. To process the complete
47 	 * original input afterwards use the stream returned by
48 	 * {@link #getInputStream()}.
49 	 *
50 	 * @param in
51 	 *            input to read the header from
52 	 * @throws IOException
53 	 *             if the stream can't be read
54 	 */
ContentTypeDetector(final InputStream in)55 	public ContentTypeDetector(final InputStream in) throws IOException {
56 		if (in.markSupported()) {
57 			this.in = in;
58 		} else {
59 			this.in = new BufferedInputStream(in, BUFFER_SIZE);
60 		}
61 		this.in.mark(BUFFER_SIZE);
62 		this.type = determineType(this.in);
63 		this.in.reset();
64 	}
65 
determineType(final InputStream in)66 	private static int determineType(final InputStream in) throws IOException {
67 		final int header = readInt(in);
68 		switch (header) {
69 		case ZIPFILE:
70 			return ZIPFILE;
71 		case PACK200FILE:
72 			return PACK200FILE;
73 		case CLASSFILE:
74 			// Mach-O fat/universal binaries have the same magic header as Java
75 			// class files, number of architectures is stored in unsigned 4
76 			// bytes in the same place and in the same big-endian order as major
77 			// and minor version of class file. Hopefully on practice number of
78 			// architectures in single executable is less than 45, which is
79 			// major version of Java 1.1 class files:
80 			final int majorVersion = readInt(in) & 0xFFFF;
81 			if (majorVersion >= 45) {
82 				return CLASSFILE;
83 			}
84 		}
85 		if ((header & 0xffff0000) == GZFILE) {
86 			return GZFILE;
87 		}
88 		return UNKNOWN;
89 	}
90 
readInt(final InputStream in)91 	private static int readInt(final InputStream in) throws IOException {
92 		return in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read();
93 	}
94 
95 	/**
96 	 * Returns an input stream instance to read the complete content (including
97 	 * the header) of the underlying stream.
98 	 *
99 	 * @return input stream containing the complete content
100 	 */
getInputStream()101 	public InputStream getInputStream() {
102 		return in;
103 	}
104 
105 	/**
106 	 * Returns the detected file type.
107 	 *
108 	 * @return file type
109 	 */
getType()110 	public int getType() {
111 		return type;
112 	}
113 
114 }
115