1 /******************************************************************************* 2 * Copyright (c) 2009, 2015 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 = 0x1007; 28 29 /** Magic number in header for file format identification. */ 30 public static final char MAGIC_NUMBER = 0xC0C0; 31 32 /** Block identifier for file headers. */ 33 public static final byte BLOCK_HEADER = 0x01; 34 35 /** Block identifier for session information. */ 36 public static final byte BLOCK_SESSIONINFO = 0x10; 37 38 /** Block identifier for execution data of a single class. */ 39 public static final byte BLOCK_EXECUTIONDATA = 0x11; 40 41 /** Underlying data output */ 42 protected final CompactDataOutput out; 43 44 /** 45 * Creates a new writer based on the given output stream. Depending on the 46 * nature of the underlying stream output should be buffered as most data is 47 * written in single bytes. 48 * 49 * @param output 50 * binary stream to write execution data to 51 * @throws IOException 52 * if the header can't be written 53 */ ExecutionDataWriter(final OutputStream output)54 public ExecutionDataWriter(final OutputStream output) throws IOException { 55 this.out = new CompactDataOutput(output); 56 writeHeader(); 57 } 58 59 /** 60 * Writes an file header to identify the stream and its protocol version. 61 * 62 * @throws IOException 63 * if the header can't be written 64 */ writeHeader()65 private void writeHeader() throws IOException { 66 out.writeByte(BLOCK_HEADER); 67 out.writeChar(MAGIC_NUMBER); 68 out.writeChar(FORMAT_VERSION); 69 } 70 71 /** 72 * Flushes the underlying stream. 73 * 74 * @throws IOException 75 * if the underlying stream can't be flushed 76 */ flush()77 public void flush() throws IOException { 78 out.flush(); 79 } 80 visitSessionInfo(final SessionInfo info)81 public void visitSessionInfo(final SessionInfo info) { 82 try { 83 out.writeByte(BLOCK_SESSIONINFO); 84 out.writeUTF(info.getId()); 85 out.writeLong(info.getStartTimeStamp()); 86 out.writeLong(info.getDumpTimeStamp()); 87 } catch (final IOException e) { 88 throw new RuntimeException(e); 89 } 90 } 91 visitClassExecution(final ExecutionData data)92 public void visitClassExecution(final ExecutionData data) { 93 try { 94 out.writeByte(BLOCK_EXECUTIONDATA); 95 out.writeLong(data.getId()); 96 out.writeUTF(data.getName()); 97 out.writeBooleanArray(data.getProbes()); 98 } catch (final IOException e) { 99 throw new RuntimeException(e); 100 } 101 } 102 103 /** 104 * Returns the first bytes of a file that represents a valid execution data 105 * file. In any case every execution data file starts with the three bytes 106 * <code>0x01 0xC0 0xC0</code>. 107 * 108 * @return first bytes of a execution data file 109 */ getFileHeader()110 public static final byte[] getFileHeader() { 111 final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 112 try { 113 new ExecutionDataWriter(buffer); 114 } catch (final IOException e) { 115 // Must not happen with ByteArrayOutputStream 116 throw new AssertionError(e); 117 } 118 return buffer.toByteArray(); 119 } 120 121 } 122