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