• 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.data;
14 
15 import java.io.ByteArrayOutputStream;
16 import java.io.IOException;
17 import java.io.OutputStream;
18 
19 import org.jacoco.core.internal.data.CompactDataOutput;
20 
21 /**
22  * Serialization of execution data into binary streams.
23  */
24 public class ExecutionDataWriter
25 		implements ISessionInfoVisitor, IExecutionDataVisitor {
26 
27 	/**
28 	 * File format version, will be incremented for each incompatible change.
29 	 */
30 	public static final char FORMAT_VERSION;
31 
32 	static {
33 		// Runtime initialize to ensure javac does not inline the value.
34 		FORMAT_VERSION = 0x1007;
35 	}
36 
37 	/** Magic number in header for file format identification. */
38 	public static final char MAGIC_NUMBER = 0xC0C0;
39 
40 	/** Block identifier for file headers. */
41 	public static final byte BLOCK_HEADER = 0x01;
42 
43 	/** Block identifier for session information. */
44 	public static final byte BLOCK_SESSIONINFO = 0x10;
45 
46 	/** Block identifier for execution data of a single class. */
47 	public static final byte BLOCK_EXECUTIONDATA = 0x11;
48 
49 	/** Underlying data output */
50 	protected final CompactDataOutput out;
51 
52 	/**
53 	 * Creates a new writer based on the given output stream. Depending on the
54 	 * nature of the underlying stream output should be buffered as most data is
55 	 * written in single bytes.
56 	 *
57 	 * @param output
58 	 *            binary stream to write execution data to
59 	 * @throws IOException
60 	 *             if the header can't be written
61 	 */
ExecutionDataWriter(final OutputStream output)62 	public ExecutionDataWriter(final OutputStream output) throws IOException {
63 		this.out = new CompactDataOutput(output);
64 		writeHeader();
65 	}
66 
67 	/**
68 	 * Writes an file header to identify the stream and its protocol version.
69 	 *
70 	 * @throws IOException
71 	 *             if the header can't be written
72 	 */
writeHeader()73 	private void writeHeader() throws IOException {
74 		out.writeByte(BLOCK_HEADER);
75 		out.writeChar(MAGIC_NUMBER);
76 		out.writeChar(FORMAT_VERSION);
77 	}
78 
79 	/**
80 	 * Flushes the underlying stream.
81 	 *
82 	 * @throws IOException
83 	 *             if the underlying stream can't be flushed
84 	 */
flush()85 	public void flush() throws IOException {
86 		out.flush();
87 	}
88 
visitSessionInfo(final SessionInfo info)89 	public void visitSessionInfo(final SessionInfo info) {
90 		try {
91 			out.writeByte(BLOCK_SESSIONINFO);
92 			out.writeUTF(info.getId());
93 			out.writeLong(info.getStartTimeStamp());
94 			out.writeLong(info.getDumpTimeStamp());
95 		} catch (final IOException e) {
96 			throw new RuntimeException(e);
97 		}
98 	}
99 
100 	// BEGIN android-change
visitClassExecution(final IExecutionData data)101 	public void visitClassExecution(final IExecutionData data) {
102 	// END android-change
103 		if (data.hasHits()) {
104 			try {
105 				out.writeByte(BLOCK_EXECUTIONDATA);
106 				out.writeLong(data.getId());
107 				out.writeUTF(data.getName());
108 				// BEGIN android-change
109 				out.writeBooleanArray(data.getProbesCopy());
110 				// END android-change
111 			} catch (final IOException e) {
112 				throw new RuntimeException(e);
113 			}
114 		}
115 	}
116 
117 	/**
118 	 * Returns the first bytes of a file that represents a valid execution data
119 	 * file. In any case every execution data file starts with the three bytes
120 	 * <code>0x01 0xC0 0xC0</code>.
121 	 *
122 	 * @return first bytes of a execution data file
123 	 */
getFileHeader()124 	public static final byte[] getFileHeader() {
125 		final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
126 		try {
127 			new ExecutionDataWriter(buffer);
128 		} catch (final IOException e) {
129 			// Must not happen with ByteArrayOutputStream
130 			throw new AssertionError(e);
131 		}
132 		return buffer.toByteArray();
133 	}
134 
135 }
136