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