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