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