• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *    Marc R. Hoffmann - initial API and implementation
10  *
11  *******************************************************************************/
12 package org.jacoco.core.data;
13 
14 import java.io.ByteArrayOutputStream;
15 import java.io.IOException;
16 import java.io.OutputStream;
17 
18 import org.jacoco.core.internal.data.CompactDataOutput;
19 
20 /**
21  * Serialization of execution data into binary streams.
22  */
23 public class ExecutionDataWriter implements ISessionInfoVisitor,
24 		IExecutionDataVisitor {
25 
26 	/** File format version, will be incremented for each incompatible change. */
27 	public static final char FORMAT_VERSION;
28 
29 	static {
30 		// Runtime initialize to ensure javac does not inline the value.
31 		FORMAT_VERSION = 0x1007;
32 	}
33 
34 	/** Magic number in header for file format identification. */
35 	public static final char MAGIC_NUMBER = 0xC0C0;
36 
37 	/** Block identifier for file headers. */
38 	public static final byte BLOCK_HEADER = 0x01;
39 
40 	/** Block identifier for session information. */
41 	public static final byte BLOCK_SESSIONINFO = 0x10;
42 
43 	/** Block identifier for execution data of a single class. */
44 	public static final byte BLOCK_EXECUTIONDATA = 0x11;
45 
46 	/** Underlying data output */
47 	protected final CompactDataOutput out;
48 
49 	/**
50 	 * Creates a new writer based on the given output stream. Depending on the
51 	 * nature of the underlying stream output should be buffered as most data is
52 	 * written in single bytes.
53 	 *
54 	 * @param output
55 	 *            binary stream to write execution data to
56 	 * @throws IOException
57 	 *             if the header can't be written
58 	 */
ExecutionDataWriter(final OutputStream output)59 	public ExecutionDataWriter(final OutputStream output) throws IOException {
60 		this.out = new CompactDataOutput(output);
61 		writeHeader();
62 	}
63 
64 	/**
65 	 * Writes an file header to identify the stream and its protocol version.
66 	 *
67 	 * @throws IOException
68 	 *             if the header can't be written
69 	 */
writeHeader()70 	private void writeHeader() throws IOException {
71 		out.writeByte(BLOCK_HEADER);
72 		out.writeChar(MAGIC_NUMBER);
73 		out.writeChar(FORMAT_VERSION);
74 	}
75 
76 	/**
77 	 * Flushes the underlying stream.
78 	 *
79 	 * @throws IOException
80 	 *             if the underlying stream can't be flushed
81 	 */
flush()82 	public void flush() throws IOException {
83 		out.flush();
84 	}
85 
visitSessionInfo(final SessionInfo info)86 	public void visitSessionInfo(final SessionInfo info) {
87 		try {
88 			out.writeByte(BLOCK_SESSIONINFO);
89 			out.writeUTF(info.getId());
90 			out.writeLong(info.getStartTimeStamp());
91 			out.writeLong(info.getDumpTimeStamp());
92 		} catch (final IOException e) {
93 			throw new RuntimeException(e);
94 		}
95 	}
96 
visitClassExecution(final ExecutionData data)97 	public void visitClassExecution(final ExecutionData data) {
98 		if (data.hasHits()) {
99 			try {
100 				out.writeByte(BLOCK_EXECUTIONDATA);
101 				out.writeLong(data.getId());
102 				out.writeUTF(data.getName());
103 				out.writeBooleanArray(data.getProbes());
104 			} catch (final IOException e) {
105 				throw new RuntimeException(e);
106 			}
107 		}
108 	}
109 
110 	/**
111 	 * Returns the first bytes of a file that represents a valid execution data
112 	 * file. In any case every execution data file starts with the three bytes
113 	 * <code>0x01 0xC0 0xC0</code>.
114 	 *
115 	 * @return first bytes of a execution data file
116 	 */
getFileHeader()117 	public static final byte[] getFileHeader() {
118 		final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
119 		try {
120 			new ExecutionDataWriter(buffer);
121 		} catch (final IOException e) {
122 			// Must not happen with ByteArrayOutputStream
123 			throw new AssertionError(e);
124 		}
125 		return buffer.toByteArray();
126 	}
127 
128 }
129