1579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/* 2579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Copyright (C) 2007 The Android Open Source Project 3579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 4579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License"); 5579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * you may not use this file except in compliance with the License. 6579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * You may obtain a copy of the License at 7579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 8579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0 9579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 10579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Unless required by applicable law or agreed to in writing, software 11579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS, 12579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * See the License for the specific language governing permissions and 14579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * limitations under the License. 15579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 16579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 17579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpackage com.android.dx.util; 18579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 19579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.io.IOException; 20579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.io.Writer; 21579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.ArrayList; 22579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 23579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/** 24579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Implementation of {@link AnnotatedOutput} which stores the written data 25579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * into a {@code byte[]}. 26579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 27579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p><b>Note:</b> As per the {@link Output} interface, multi-byte 28579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * writes all use little-endian order.</p> 29579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 30579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpublic final class ByteArrayAnnotatedOutput 31579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson implements AnnotatedOutput, ByteOutput { 32579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** default size for stretchy instances */ 33579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private static final int DEFAULT_SIZE = 1000; 34579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 35579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 36579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * whether the instance is stretchy, that is, whether its array 37579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * may be resized to increase capacity 38579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 39579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private final boolean stretchy; 40579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 41579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@code non-null;} the data itself */ 42579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private byte[] data; 43579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 44579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@code >= 0;} current output cursor */ 45579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private int cursor; 46579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 47579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** whether annotations are to be verbose */ 48579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private boolean verbose; 49579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 50579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 51579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@code null-ok;} list of annotations, or {@code null} if this instance 52579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * isn't keeping them 53579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 54579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private ArrayList<Annotation> annotations; 55579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 56579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@code >= 40 (if used);} the desired maximum annotation width */ 57579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private int annotationWidth; 58579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 59579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 60579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@code >= 8 (if used);} the number of bytes of hex output to use 61579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * in annotations 62579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 63579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private int hexCols; 64579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 65579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 66579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Constructs an instance with a fixed maximum size. Note that the 67579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * given array is the only one that will be used to store data. In 68579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * particular, no reallocation will occur in order to expand the 69579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * capacity of the resulting instance. Also, the constructed 70579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * instance does not keep annotations by default. 71579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 72579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param data {@code non-null;} data array to use for output 73579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 74579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public ByteArrayAnnotatedOutput(byte[] data) { 75579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this(data, false); 76579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 77579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 78579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 79579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Constructs a "stretchy" instance. The underlying array may be 80579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * reallocated. The constructed instance does not keep annotations 81579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * by default. 82579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 83579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public ByteArrayAnnotatedOutput() { 84579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this(DEFAULT_SIZE); 85579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 86579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 87579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 88579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Constructs a "stretchy" instance with initial size {@code size}. The 89579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * underlying array may be reallocated. The constructed instance does not 90579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * keep annotations by default. 91579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 92579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public ByteArrayAnnotatedOutput(int size) { 93579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this(new byte[size], true); 94579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 95579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 96579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 97579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Internal constructor. 98579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 99579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param data {@code non-null;} data array to use for output 100579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param stretchy whether the instance is to be stretchy 101579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 102579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) { 103579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (data == null) { 104579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new NullPointerException("data == null"); 105579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 106579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 107579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.stretchy = stretchy; 108579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.data = data; 109579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.cursor = 0; 110579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.verbose = false; 111579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.annotations = null; 112579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.annotationWidth = 0; 113579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.hexCols = 0; 114579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 115579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 116579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 117579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Gets the underlying {@code byte[]} of this instance, which 118579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * may be larger than the number of bytes written 119579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 120579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @see #toByteArray 121579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 122579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @return {@code non-null;} the {@code byte[]} 123579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 124579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public byte[] getArray() { 125579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return data; 126579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 127579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 128579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 129579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Constructs and returns a new {@code byte[]} that contains 130579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * the written contents exactly (that is, with no extra unwritten 131579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * bytes at the end). 132579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 133579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @see #getArray 134579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 135579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @return {@code non-null;} an appropriately-constructed array 136579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 137579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public byte[] toByteArray() { 138579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson byte[] result = new byte[cursor]; 139579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson System.arraycopy(data, 0, result, 0, cursor); 140579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return result; 141579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 142579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 143579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 144579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public int getCursor() { 145579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return cursor; 146579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 147579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 148579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 149579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void assertCursor(int expectedCursor) { 150579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (cursor != expectedCursor) { 151579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new ExceptionWithContext("expected cursor " + 152579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson expectedCursor + "; actual value: " + cursor); 153579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 154579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 155579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 156579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 157579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void writeByte(int value) { 158579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int writeAt = cursor; 159579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int end = writeAt + 1; 160579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 161579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (stretchy) { 162579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ensureCapacity(end); 163579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else if (end > data.length) { 164579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwBounds(); 165579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 166579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 167579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 168579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt] = (byte) value; 169579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson cursor = end; 170579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 171579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 172579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 173579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void writeShort(int value) { 174579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int writeAt = cursor; 175579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int end = writeAt + 2; 176579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 177579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (stretchy) { 178579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ensureCapacity(end); 179579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else if (end > data.length) { 180579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwBounds(); 181579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 182579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 183579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 184579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt] = (byte) value; 185579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 1] = (byte) (value >> 8); 186579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson cursor = end; 187579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 188579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 189579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 190579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void writeInt(int value) { 191579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int writeAt = cursor; 192579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int end = writeAt + 4; 193579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 194579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (stretchy) { 195579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ensureCapacity(end); 196579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else if (end > data.length) { 197579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwBounds(); 198579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 199579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 200579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 201579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt] = (byte) value; 202579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 1] = (byte) (value >> 8); 203579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 2] = (byte) (value >> 16); 204579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 3] = (byte) (value >> 24); 205579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson cursor = end; 206579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 207579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 208579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 209579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void writeLong(long value) { 210579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int writeAt = cursor; 211579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int end = writeAt + 8; 212579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 213579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (stretchy) { 214579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ensureCapacity(end); 215579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else if (end > data.length) { 216579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwBounds(); 217579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 218579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 219579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 220579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int half = (int) value; 221579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt] = (byte) half; 222579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 1] = (byte) (half >> 8); 223579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 2] = (byte) (half >> 16); 224579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 3] = (byte) (half >> 24); 225579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 226579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson half = (int) (value >> 32); 227579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 4] = (byte) half; 228579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 5] = (byte) (half >> 8); 229579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 6] = (byte) (half >> 16); 230579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data[writeAt + 7] = (byte) (half >> 24); 231579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 232579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson cursor = end; 233579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 234579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 235579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 236579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public int writeUleb128(int value) { 237579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (stretchy) { 238579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ensureCapacity(cursor + 5); // pessimistic 239579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 240579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int cursorBefore = cursor; 241579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson Leb128Utils.writeUnsignedLeb128(this, value); 242579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return (cursor - cursorBefore); 243579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 244579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 245579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 246579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public int writeSleb128(int value) { 247579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (stretchy) { 248579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ensureCapacity(cursor + 5); // pessimistic 249579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 250579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int cursorBefore = cursor; 251579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson Leb128Utils.writeSignedLeb128(this, value); 252579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return (cursor - cursorBefore); 253579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 254579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 255579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 256579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void write(ByteArray bytes) { 257579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int blen = bytes.size(); 258579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int writeAt = cursor; 259579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int end = writeAt + blen; 260579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 261579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (stretchy) { 262579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ensureCapacity(end); 263579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else if (end > data.length) { 264579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwBounds(); 265579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 266579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 267579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 268579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson bytes.getBytes(data, writeAt); 269579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson cursor = end; 270579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 271579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 272579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 273579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void write(byte[] bytes, int offset, int length) { 274579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int writeAt = cursor; 275579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int end = writeAt + length; 276579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int bytesEnd = offset + length; 277579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 278579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0) 279579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) { 280579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IndexOutOfBoundsException("bytes.length " + 281579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson bytes.length + "; " + 282579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson offset + "..!" + end); 283579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 284579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 285579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (stretchy) { 286579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ensureCapacity(end); 287579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else if (end > data.length) { 288579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwBounds(); 289579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 290579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 291579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 292579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson System.arraycopy(bytes, offset, data, writeAt, length); 293579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson cursor = end; 294579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 295579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 296579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 297579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void write(byte[] bytes) { 298579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson write(bytes, 0, bytes.length); 299579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 300579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 301579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 302579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void writeZeroes(int count) { 303579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (count < 0) { 304579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IllegalArgumentException("count < 0"); 305579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 306579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 307579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int end = cursor + count; 308579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 309579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (stretchy) { 310579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ensureCapacity(end); 311579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else if (end > data.length) { 312579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwBounds(); 313579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 314579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 315579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 316579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /* 317579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * There is no need to actually write zeroes, since the array is 318579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * already preinitialized with zeroes. 319579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 320579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 321579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson cursor = end; 322579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 323579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 324579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 325579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void alignTo(int alignment) { 326579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int mask = alignment - 1; 327579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 328579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if ((alignment < 0) || ((mask & alignment) != 0)) { 329579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IllegalArgumentException("bogus alignment"); 330579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 331579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 332579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int end = (cursor + mask) & ~mask; 333579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 334579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (stretchy) { 335579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ensureCapacity(end); 336579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else if (end > data.length) { 337579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwBounds(); 338579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 339579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 340579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 341579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /* 342579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * There is no need to actually write zeroes, since the array is 343579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * already preinitialized with zeroes. 344579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 345579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 346579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson cursor = end; 347579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 348579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 349579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 350579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public boolean annotates() { 351579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return (annotations != null); 352579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 353579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 354579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 355579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public boolean isVerbose() { 356579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return verbose; 357579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 358579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 359579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 360579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void annotate(String msg) { 361579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (annotations == null) { 362579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 363579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 364579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 365579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson endAnnotation(); 366579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson annotations.add(new Annotation(cursor, msg)); 367579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 368579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 369579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 370579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void annotate(int amt, String msg) { 371579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (annotations == null) { 372579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 373579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 374579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 375579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson endAnnotation(); 376579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 377579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int asz = annotations.size(); 378579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd(); 379579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int startAt; 380579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 381579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (lastEnd <= cursor) { 382579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson startAt = cursor; 383579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else { 384579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson startAt = lastEnd; 385579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 386579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 387579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson annotations.add(new Annotation(startAt, startAt + amt, msg)); 388579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 389579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 390579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 391579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void endAnnotation() { 392579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (annotations == null) { 393579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 394579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 395579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 396579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int sz = annotations.size(); 397579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 398579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (sz != 0) { 399579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson annotations.get(sz - 1).setEndIfUnset(cursor); 400579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 401579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 402579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 403579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 404579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public int getAnnotationWidth() { 405579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int leftWidth = 8 + (hexCols * 2) + (hexCols / 2); 406579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 407579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return annotationWidth - leftWidth; 408579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 409579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 410579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 411579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Indicates that this instance should keep annotations. This method may 412579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * be called only once per instance, and only before any data has been 413579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * written to the it. 414579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 415579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param annotationWidth {@code >= 40;} the desired maximum annotation width 416579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param verbose whether or not to indicate verbose annotations 417579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 418579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void enableAnnotations(int annotationWidth, boolean verbose) { 419579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if ((annotations != null) || (cursor != 0)) { 420579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new RuntimeException("cannot enable annotations"); 421579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 422579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 423579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (annotationWidth < 40) { 424579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IllegalArgumentException("annotationWidth < 40"); 425579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 426579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 427579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1; 428579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (hexCols < 6) { 429579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson hexCols = 6; 430579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else if (hexCols > 10) { 431579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson hexCols = 10; 432579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 433579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 434579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.annotations = new ArrayList<Annotation>(1000); 435579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.annotationWidth = annotationWidth; 436579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.hexCols = hexCols; 437579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.verbose = verbose; 438579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 439579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 440579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 441579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Finishes up annotation processing. This closes off any open 442579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * annotations and removes annotations that don't refer to written 443579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * data. 444579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 445579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void finishAnnotating() { 446579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson // Close off the final annotation, if any. 447579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson endAnnotation(); 448579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 449579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson // Remove annotations that refer to unwritten data. 450579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (annotations != null) { 451579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int asz = annotations.size(); 452579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson while (asz > 0) { 453579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson Annotation last = annotations.get(asz - 1); 454579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (last.getStart() > cursor) { 455579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson annotations.remove(asz - 1); 456579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson asz--; 457579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else if (last.getEnd() > cursor) { 458579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson last.setEnd(cursor); 459579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson break; 460579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else { 461579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson break; 462579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 463579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 464579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 465579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 466579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 467579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 468579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Writes the annotated content of this instance to the given writer. 469579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 470579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param out {@code non-null;} where to write to 471579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 472579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void writeAnnotationsTo(Writer out) throws IOException { 473579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int width2 = getAnnotationWidth(); 474579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int width1 = annotationWidth - width2 - 1; 475579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 476579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|"); 477579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson Writer left = twoc.getLeft(); 478579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson Writer right = twoc.getRight(); 479579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int leftAt = 0; // left-hand byte output cursor 480579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int rightAt = 0; // right-hand annotation index 481579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int rightSz = annotations.size(); 482579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 483579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson while ((leftAt < cursor) && (rightAt < rightSz)) { 484579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson Annotation a = annotations.get(rightAt); 485579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int start = a.getStart(); 486579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int end; 487579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson String text; 488579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 489579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (leftAt < start) { 490579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson // This is an area with no annotation. 491579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson end = start; 492579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson start = leftAt; 493579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson text = ""; 494579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else { 495579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson // This is an area with an annotation. 496579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson end = a.getEnd(); 497579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson text = a.getText(); 498579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson rightAt++; 499579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 500579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 501579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson left.write(Hex.dump(data, start, end - start, start, hexCols, 6)); 502579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson right.write(text); 503579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson twoc.flush(); 504579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson leftAt = end; 505579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 506579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 507579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (leftAt < cursor) { 508579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson // There is unannotated output at the end. 509579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt, 510579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson hexCols, 6)); 511579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 512579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 513579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson while (rightAt < rightSz) { 514579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson // There are zero-byte annotations at the end. 515579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson right.write(annotations.get(rightAt).getText()); 516579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson rightAt++; 517579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 518579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 519579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson twoc.flush(); 520579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 521579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 522579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 523579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Throws the excpetion for when an attempt is made to write past the 524579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * end of the instance. 525579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 526579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private static void throwBounds() { 527579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IndexOutOfBoundsException("attempt to write past the end"); 528579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 529579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 530579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 531579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Reallocates the underlying array if necessary. Calls to this method 532579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * should be guarded by a test of {@link #stretchy}. 533579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 534579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param desiredSize {@code >= 0;} the desired minimum total size of the array 535579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 536579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private void ensureCapacity(int desiredSize) { 537579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (data.length < desiredSize) { 538579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson byte[] newData = new byte[desiredSize * 2 + 1000]; 539579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson System.arraycopy(data, 0, newData, 0, cursor); 540579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson data = newData; 541579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 542579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 543579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 544579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 545579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Annotation on output. 546579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 547579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private static class Annotation { 548579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@code >= 0;} start of annotated range (inclusive) */ 549579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private final int start; 550579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 551579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 552579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@code >= 0;} end of annotated range (exclusive); 553579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@code Integer.MAX_VALUE} if unclosed 554579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 555579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private int end; 556579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 557579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@code non-null;} annotation text */ 558579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private final String text; 559579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 560579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 561579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Constructs an instance. 562579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 563579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param start {@code >= 0;} start of annotated range 564579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param end {@code >= start;} end of annotated range (exclusive) or 565579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@code Integer.MAX_VALUE} if unclosed 566579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param text {@code non-null;} annotation text 567579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 568579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public Annotation(int start, int end, String text) { 569579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.start = start; 570579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.end = end; 571579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.text = text; 572579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 573579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 574579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 575579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Constructs an instance. It is initally unclosed. 576579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 577579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param start {@code >= 0;} start of annotated range 578579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param text {@code non-null;} annotation text 579579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 580579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public Annotation(int start, String text) { 581579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this(start, Integer.MAX_VALUE, text); 582579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 583579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 584579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 585579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Sets the end as given, but only if the instance is unclosed; 586579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * otherwise, do nothing. 587579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 588579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param end {@code >= start;} the end 589579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 590579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void setEndIfUnset(int end) { 591579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (this.end == Integer.MAX_VALUE) { 592579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.end = end; 593579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 594579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 595579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 596579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 597579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Sets the end as given. 598579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 599579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param end {@code >= start;} the end 600579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 601579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void setEnd(int end) { 602579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.end = end; 603579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 604579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 605579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 606579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Gets the start. 607579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 608579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @return the start 609579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 610579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public int getStart() { 611579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return start; 612579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 613579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 614579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 615579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Gets the end. 616579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 617579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @return the end 618579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 619579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public int getEnd() { 620579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return end; 621579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 622579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 623579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 624579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Gets the text. 625579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 626579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @return {@code non-null;} the text 627579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 628579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public String getText() { 629579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return text; 630579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 631579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 632579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson} 633