1917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul/* 2917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Copyright (C) 2007 The Android Open Source Project 3917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 4917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Licensed under the Apache License, Version 2.0 (the "License"); 5917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * you may not use this file except in compliance with the License. 6917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * You may obtain a copy of the License at 7917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 8917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * http://www.apache.org/licenses/LICENSE-2.0 9917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 10917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Unless required by applicable law or agreed to in writing, software 11917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * distributed under the License is distributed on an "AS IS" BASIS, 12917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * See the License for the specific language governing permissions and 14917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * limitations under the License. 15917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 16917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 17917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulpackage com.android.dexgen.util; 18917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 19917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.io.IOException; 20917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.io.Writer; 21917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.util.ArrayList; 22917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 23917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul/** 24917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Implementation of {@link AnnotatedOutput} which stores the written data 25917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * into a {@code byte[]}. 26917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 27917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * <p><b>Note:</b> As per the {@link Output} interface, multi-byte 28917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * writes all use little-endian order.</p> 29917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 30917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulpublic final class ByteArrayAnnotatedOutput 31917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul implements AnnotatedOutput { 32917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** default size for stretchy instances */ 33917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private static final int DEFAULT_SIZE = 1000; 34917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 35917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 36917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * whether the instance is stretchy, that is, whether its array 37917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * may be resized to increase capacity 38917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 39917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private final boolean stretchy; 40917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 41917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@code non-null;} the data itself */ 42917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private byte[] data; 43917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 44917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@code >= 0;} current output cursor */ 45917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private int cursor; 46917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 47917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** whether annotations are to be verbose */ 48917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private boolean verbose; 49917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 50917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 51917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * {@code null-ok;} list of annotations, or {@code null} if this instance 52917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * isn't keeping them 53917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 54917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private ArrayList<Annotation> annotations; 55917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 56917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@code >= 40 (if used);} the desired maximum annotation width */ 57917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private int annotationWidth; 58917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 59917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 60917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * {@code >= 8 (if used);} the number of bytes of hex output to use 61917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * in annotations 62917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 63917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private int hexCols; 64917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 65917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 66917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Constructs an instance with a fixed maximum size. Note that the 67917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * given array is the only one that will be used to store data. In 68917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * particular, no reallocation will occur in order to expand the 69917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * capacity of the resulting instance. Also, the constructed 70917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * instance does not keep annotations by default. 71917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 72917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param data {@code non-null;} data array to use for output 73917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 74917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public ByteArrayAnnotatedOutput(byte[] data) { 75917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this(data, false); 76917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 77917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 78917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 79917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Constructs a "stretchy" instance. The underlying array may be 80917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * reallocated. The constructed instance does not keep annotations 81917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * by default. 82917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 83917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public ByteArrayAnnotatedOutput() { 84917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this(new byte[DEFAULT_SIZE], true); 85917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 86917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 87917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 88917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Internal constructor. 89917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 90917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param data {@code non-null;} data array to use for output 91917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param stretchy whether the instance is to be stretchy 92917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 93917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) { 94917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (data == null) { 95917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new NullPointerException("data == null"); 96917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 97917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 98917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.stretchy = stretchy; 99917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.data = data; 100917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.cursor = 0; 101917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.verbose = false; 102917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.annotations = null; 103917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.annotationWidth = 0; 104917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.hexCols = 0; 105917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 106917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 107917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 108917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Gets the underlying {@code byte[]} of this instance, which 109917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * may be larger than the number of bytes written 110917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 111917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @see #toByteArray 112917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 113917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code non-null;} the {@code byte[]} 114917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 115917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public byte[] getArray() { 116917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return data; 117917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 118917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 119917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 120917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Constructs and returns a new {@code byte[]} that contains 121917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * the written contents exactly (that is, with no extra unwritten 122917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * bytes at the end). 123917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 124917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @see #getArray 125917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 126917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code non-null;} an appropriately-constructed array 127917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 128917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public byte[] toByteArray() { 129917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul byte[] result = new byte[cursor]; 130917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul System.arraycopy(data, 0, result, 0, cursor); 131917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return result; 132917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 133917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 134917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 135917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public int getCursor() { 136917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return cursor; 137917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 138917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 139917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 140917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void assertCursor(int expectedCursor) { 141917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (cursor != expectedCursor) { 142917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new ExceptionWithContext("expected cursor " + 143917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul expectedCursor + "; actual value: " + cursor); 144917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 145917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 146917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 147917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 148917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void writeByte(int value) { 149917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int writeAt = cursor; 150917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int end = writeAt + 1; 151917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 152917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (stretchy) { 153917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul ensureCapacity(end); 154917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (end > data.length) { 155917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throwBounds(); 156917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 157917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 158917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 159917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt] = (byte) value; 160917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul cursor = end; 161917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 162917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 163917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 164917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void writeShort(int value) { 165917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int writeAt = cursor; 166917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int end = writeAt + 2; 167917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 168917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (stretchy) { 169917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul ensureCapacity(end); 170917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (end > data.length) { 171917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throwBounds(); 172917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 173917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 174917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 175917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt] = (byte) value; 176917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 1] = (byte) (value >> 8); 177917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul cursor = end; 178917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 179917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 180917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 181917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void writeInt(int value) { 182917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int writeAt = cursor; 183917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int end = writeAt + 4; 184917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 185917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (stretchy) { 186917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul ensureCapacity(end); 187917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (end > data.length) { 188917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throwBounds(); 189917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 190917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 191917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 192917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt] = (byte) value; 193917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 1] = (byte) (value >> 8); 194917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 2] = (byte) (value >> 16); 195917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 3] = (byte) (value >> 24); 196917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul cursor = end; 197917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 198917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 199917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 200917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void writeLong(long value) { 201917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int writeAt = cursor; 202917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int end = writeAt + 8; 203917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 204917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (stretchy) { 205917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul ensureCapacity(end); 206917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (end > data.length) { 207917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throwBounds(); 208917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 209917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 210917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 211917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int half = (int) value; 212917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt] = (byte) half; 213917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 1] = (byte) (half >> 8); 214917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 2] = (byte) (half >> 16); 215917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 3] = (byte) (half >> 24); 216917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 217917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul half = (int) (value >> 32); 218917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 4] = (byte) half; 219917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 5] = (byte) (half >> 8); 220917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 6] = (byte) (half >> 16); 221917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data[writeAt + 7] = (byte) (half >> 24); 222917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 223917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul cursor = end; 224917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 225917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 226917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 227917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public int writeUnsignedLeb128(int value) { 228917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int remaining = value >> 7; 229917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int count = 0; 230917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 231917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul while (remaining != 0) { 232917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul writeByte((value & 0x7f) | 0x80); 233917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul value = remaining; 234917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul remaining >>= 7; 235917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul count++; 236917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 237917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 238917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul writeByte(value & 0x7f); 239917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return count + 1; 240917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 241917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 242917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 243917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public int writeSignedLeb128(int value) { 244917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int remaining = value >> 7; 245917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int count = 0; 246917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul boolean hasMore = true; 247917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; 248917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 249917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul while (hasMore) { 250917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul hasMore = (remaining != end) 251917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul || ((remaining & 1) != ((value >> 6) & 1)); 252917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 253917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul writeByte((value & 0x7f) | (hasMore ? 0x80 : 0)); 254917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul value = remaining; 255917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul remaining >>= 7; 256917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul count++; 257917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 258917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 259917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return count; 260917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 261917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 262917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 263917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void write(ByteArray bytes) { 264917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int blen = bytes.size(); 265917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int writeAt = cursor; 266917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int end = writeAt + blen; 267917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 268917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (stretchy) { 269917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul ensureCapacity(end); 270917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (end > data.length) { 271917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throwBounds(); 272917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 273917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 274917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 275917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul bytes.getBytes(data, writeAt); 276917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul cursor = end; 277917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 278917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 279917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 280917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void write(byte[] bytes, int offset, int length) { 281917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int writeAt = cursor; 282917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int end = writeAt + length; 283917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int bytesEnd = offset + length; 284917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 285917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0) 286917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) { 287917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new IndexOutOfBoundsException("bytes.length " + 288917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul bytes.length + "; " + 289917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul offset + "..!" + end); 290917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 291917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 292917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (stretchy) { 293917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul ensureCapacity(end); 294917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (end > data.length) { 295917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throwBounds(); 296917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 297917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 298917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 299917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul System.arraycopy(bytes, offset, data, writeAt, length); 300917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul cursor = end; 301917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 302917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 303917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 304917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void write(byte[] bytes) { 305917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul write(bytes, 0, bytes.length); 306917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 307917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 308917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 309917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void writeZeroes(int count) { 310917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (count < 0) { 311917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new IllegalArgumentException("count < 0"); 312917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 313917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 314917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int end = cursor + count; 315917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 316917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (stretchy) { 317917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul ensureCapacity(end); 318917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (end > data.length) { 319917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throwBounds(); 320917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 321917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 322917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 323917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /* 324917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * There is no need to actually write zeroes, since the array is 325917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * already preinitialized with zeroes. 326917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 327917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 328917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul cursor = end; 329917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 330917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 331917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 332917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void alignTo(int alignment) { 333917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int mask = alignment - 1; 334917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 335917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if ((alignment < 0) || ((mask & alignment) != 0)) { 336917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new IllegalArgumentException("bogus alignment"); 337917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 338917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 339917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int end = (cursor + mask) & ~mask; 340917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 341917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (stretchy) { 342917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul ensureCapacity(end); 343917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (end > data.length) { 344917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throwBounds(); 345917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 346917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 347917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 348917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /* 349917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * There is no need to actually write zeroes, since the array is 350917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * already preinitialized with zeroes. 351917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 352917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 353917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul cursor = end; 354917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 355917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 356917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 357917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public boolean annotates() { 358917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return (annotations != null); 359917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 360917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 361917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 362917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public boolean isVerbose() { 363917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return verbose; 364917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 365917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 366917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 367917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void annotate(String msg) { 368917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (annotations == null) { 369917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 370917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 371917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 372917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul endAnnotation(); 373917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul annotations.add(new Annotation(cursor, msg)); 374917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 375917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 376917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 377917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void annotate(int amt, String msg) { 378917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (annotations == null) { 379917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 380917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 381917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 382917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul endAnnotation(); 383917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 384917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int asz = annotations.size(); 385917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd(); 386917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int startAt; 387917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 388917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (lastEnd <= cursor) { 389917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul startAt = cursor; 390917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else { 391917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul startAt = lastEnd; 392917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 393917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 394917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul annotations.add(new Annotation(startAt, startAt + amt, msg)); 395917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 396917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 397917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 398917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void endAnnotation() { 399917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (annotations == null) { 400917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return; 401917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 402917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 403917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int sz = annotations.size(); 404917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 405917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (sz != 0) { 406917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul annotations.get(sz - 1).setEndIfUnset(cursor); 407917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 408917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 409917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 410917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 411917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public int getAnnotationWidth() { 412917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int leftWidth = 8 + (hexCols * 2) + (hexCols / 2); 413917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 414917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return annotationWidth - leftWidth; 415917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 416917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 417917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 418917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Indicates that this instance should keep annotations. This method may 419917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * be called only once per instance, and only before any data has been 420917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * written to the it. 421917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 422917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param annotationWidth {@code >= 40;} the desired maximum annotation width 423917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param verbose whether or not to indicate verbose annotations 424917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 425917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void enableAnnotations(int annotationWidth, boolean verbose) { 426917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if ((annotations != null) || (cursor != 0)) { 427917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new RuntimeException("cannot enable annotations"); 428917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 429917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 430917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (annotationWidth < 40) { 431917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new IllegalArgumentException("annotationWidth < 40"); 432917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 433917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 434917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1; 435917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (hexCols < 6) { 436917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul hexCols = 6; 437917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (hexCols > 10) { 438917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul hexCols = 10; 439917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 440917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 441917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.annotations = new ArrayList<Annotation>(1000); 442917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.annotationWidth = annotationWidth; 443917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.hexCols = hexCols; 444917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.verbose = verbose; 445917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 446917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 447917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 448917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Finishes up annotation processing. This closes off any open 449917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * annotations and removes annotations that don't refer to written 450917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * data. 451917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 452917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void finishAnnotating() { 453917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // Close off the final annotation, if any. 454917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul endAnnotation(); 455917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 456917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // Remove annotations that refer to unwritten data. 457917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (annotations != null) { 458917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int asz = annotations.size(); 459917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul while (asz > 0) { 460917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul Annotation last = annotations.get(asz - 1); 461917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (last.getStart() > cursor) { 462917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul annotations.remove(asz - 1); 463917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul asz--; 464917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (last.getEnd() > cursor) { 465917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul last.setEnd(cursor); 466917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul break; 467917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else { 468917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul break; 469917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 470917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 471917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 472917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 473917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 474917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 475917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Writes the annotated content of this instance to the given writer. 476917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 477917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param out {@code non-null;} where to write to 478917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 479917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void writeAnnotationsTo(Writer out) throws IOException { 480917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int width2 = getAnnotationWidth(); 481917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int width1 = annotationWidth - width2 - 1; 482917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 483917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|"); 484917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul Writer left = twoc.getLeft(); 485917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul Writer right = twoc.getRight(); 486917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int leftAt = 0; // left-hand byte output cursor 487917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int rightAt = 0; // right-hand annotation index 488917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int rightSz = annotations.size(); 489917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 490917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul while ((leftAt < cursor) && (rightAt < rightSz)) { 491917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul Annotation a = annotations.get(rightAt); 492917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int start = a.getStart(); 493917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int end; 494917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul String text; 495917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 496917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (leftAt < start) { 497917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // This is an area with no annotation. 498917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul end = start; 499917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul start = leftAt; 500917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul text = ""; 501917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else { 502917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // This is an area with an annotation. 503917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul end = a.getEnd(); 504917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul text = a.getText(); 505917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul rightAt++; 506917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 507917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 508917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul left.write(Hex.dump(data, start, end - start, start, hexCols, 6)); 509917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul right.write(text); 510917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul twoc.flush(); 511917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul leftAt = end; 512917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 513917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 514917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (leftAt < cursor) { 515917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // There is unannotated output at the end. 516917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt, 517917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul hexCols, 6)); 518917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 519917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 520917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul while (rightAt < rightSz) { 521917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // There are zero-byte annotations at the end. 522917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul right.write(annotations.get(rightAt).getText()); 523917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul rightAt++; 524917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 525917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 526917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul twoc.flush(); 527917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 528917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 529917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 530917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Throws the excpetion for when an attempt is made to write past the 531917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * end of the instance. 532917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 533917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private static void throwBounds() { 534917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new IndexOutOfBoundsException("attempt to write past the end"); 535917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 536917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 537917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 538917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Reallocates the underlying array if necessary. Calls to this method 539917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * should be guarded by a test of {@link #stretchy}. 540917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 541917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param desiredSize {@code >= 0;} the desired minimum total size of the array 542917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 543917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private void ensureCapacity(int desiredSize) { 544917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (data.length < desiredSize) { 545917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul byte[] newData = new byte[desiredSize * 2 + 1000]; 546917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul System.arraycopy(data, 0, newData, 0, cursor); 547917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul data = newData; 548917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 549917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 550917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 551917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 552917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Annotation on output. 553917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 554917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private static class Annotation { 555917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@code >= 0;} start of annotated range (inclusive) */ 556917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private final int start; 557917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 558917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 559917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * {@code >= 0;} end of annotated range (exclusive); 560917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * {@code Integer.MAX_VALUE} if unclosed 561917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 562917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private int end; 563917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 564917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@code non-null;} annotation text */ 565917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private final String text; 566917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 567917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 568917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Constructs an instance. 569917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 570917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param start {@code >= 0;} start of annotated range 571917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param end {@code >= start;} end of annotated range (exclusive) or 572917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * {@code Integer.MAX_VALUE} if unclosed 573917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param text {@code non-null;} annotation text 574917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 575917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public Annotation(int start, int end, String text) { 576917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.start = start; 577917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.end = end; 578917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.text = text; 579917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 580917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 581917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 582917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Constructs an instance. It is initally unclosed. 583917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 584917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param start {@code >= 0;} start of annotated range 585917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param text {@code non-null;} annotation text 586917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 587917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public Annotation(int start, String text) { 588917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this(start, Integer.MAX_VALUE, text); 589917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 590917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 591917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 592917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Sets the end as given, but only if the instance is unclosed; 593917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * otherwise, do nothing. 594917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 595917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param end {@code >= start;} the end 596917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 597917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void setEndIfUnset(int end) { 598917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (this.end == Integer.MAX_VALUE) { 599917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.end = end; 600917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 601917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 602917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 603917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 604917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Sets the end as given. 605917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 606917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param end {@code >= start;} the end 607917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 608917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public void setEnd(int end) { 609917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.end = end; 610917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 611917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 612917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 613917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Gets the start. 614917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 615917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return the start 616917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 617917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public int getStart() { 618917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return start; 619917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 620917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 621917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 622917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Gets the end. 623917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 624917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return the end 625917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 626917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public int getEnd() { 627917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return end; 628917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 629917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 630917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 631917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Gets the text. 632917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 633917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code non-null;} the text 634917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 635917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public String getText() { 636917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return text; 637917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 638917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 639917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul} 640