FileDocCategorySizeDatePackage
ByteArrayAnnotatedOutput.javaAPI DocAndroid 5.1 API17798Thu Mar 12 22:18:30 GMT 2015com.android.dx.util

ByteArrayAnnotatedOutput

public final class ByteArrayAnnotatedOutput extends Object implements AnnotatedOutput, com.android.dex.util.ByteOutput
Implementation of {@link AnnotatedOutput} which stores the written data into a {@code byte[]}.

Note: As per the {@link Output} interface, multi-byte writes all use little-endian order.

Fields Summary
private static final int
DEFAULT_SIZE
default size for stretchy instances
private final boolean
stretchy
whether the instance is stretchy, that is, whether its array may be resized to increase capacity
private byte[]
data
{@code non-null;} the data itself
private int
cursor
{@code >= 0;} current output cursor
private boolean
verbose
whether annotations are to be verbose
private ArrayList
annotations
{@code null-ok;} list of annotations, or {@code null} if this instance isn't keeping them
private int
annotationWidth
{@code >= 40 (if used);} the desired maximum annotation width
private int
hexCols
{@code >= 8 (if used);} the number of bytes of hex output to use in annotations
Constructors Summary
public ByteArrayAnnotatedOutput(byte[] data)
Constructs an instance with a fixed maximum size. Note that the given array is the only one that will be used to store data. In particular, no reallocation will occur in order to expand the capacity of the resulting instance. Also, the constructed instance does not keep annotations by default.

param
data {@code non-null;} data array to use for output


                                                                     
       
        this(data, false);
    
public ByteArrayAnnotatedOutput()
Constructs a "stretchy" instance. The underlying array may be reallocated. The constructed instance does not keep annotations by default.

        this(DEFAULT_SIZE);
    
public ByteArrayAnnotatedOutput(int size)
Constructs a "stretchy" instance with initial size {@code size}. The underlying array may be reallocated. The constructed instance does not keep annotations by default.

        this(new byte[size], true);
    
private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy)
Internal constructor.

param
data {@code non-null;} data array to use for output
param
stretchy whether the instance is to be stretchy

        if (data == null) {
            throw new NullPointerException("data == null");
        }

        this.stretchy = stretchy;
        this.data = data;
        this.cursor = 0;
        this.verbose = false;
        this.annotations = null;
        this.annotationWidth = 0;
        this.hexCols = 0;
    
Methods Summary
public voidalignTo(int alignment)
{@inheritDoc}

        int mask = alignment - 1;

        if ((alignment < 0) || ((mask & alignment) != 0)) {
            throw new IllegalArgumentException("bogus alignment");
        }

        int end = (cursor + mask) & ~mask;

        if (stretchy) {
            ensureCapacity(end);
        } else if (end > data.length) {
            throwBounds();
            return;
        }

        /*
         * There is no need to actually write zeroes, since the array is
         * already preinitialized with zeroes.
         */

        cursor = end;
    
public voidannotate(java.lang.String msg)
{@inheritDoc}

        if (annotations == null) {
            return;
        }

        endAnnotation();
        annotations.add(new Annotation(cursor, msg));
    
public voidannotate(int amt, java.lang.String msg)
{@inheritDoc}

        if (annotations == null) {
            return;
        }

        endAnnotation();

        int asz = annotations.size();
        int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd();
        int startAt;

        if (lastEnd <= cursor) {
            startAt = cursor;
        } else {
            startAt = lastEnd;
        }

        annotations.add(new Annotation(startAt, startAt + amt, msg));
    
public booleanannotates()
{@inheritDoc}

        return (annotations != null);
    
public voidassertCursor(int expectedCursor)
{@inheritDoc}

        if (cursor != expectedCursor) {
            throw new ExceptionWithContext("expected cursor " +
                    expectedCursor + "; actual value: " + cursor);
        }
    
public voidenableAnnotations(int annotationWidth, boolean verbose)
Indicates that this instance should keep annotations. This method may be called only once per instance, and only before any data has been written to the it.

param
annotationWidth {@code >= 40;} the desired maximum annotation width
param
verbose whether or not to indicate verbose annotations

        if ((annotations != null) || (cursor != 0)) {
            throw new RuntimeException("cannot enable annotations");
        }

        if (annotationWidth < 40) {
            throw new IllegalArgumentException("annotationWidth < 40");
        }

        int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1;
        if (hexCols < 6) {
            hexCols = 6;
        } else if (hexCols > 10) {
            hexCols = 10;
        }

        this.annotations = new ArrayList<Annotation>(1000);
        this.annotationWidth = annotationWidth;
        this.hexCols = hexCols;
        this.verbose = verbose;
    
public voidendAnnotation()
{@inheritDoc}

        if (annotations == null) {
            return;
        }

        int sz = annotations.size();

        if (sz != 0) {
            annotations.get(sz - 1).setEndIfUnset(cursor);
        }
    
private voidensureCapacity(int desiredSize)
Reallocates the underlying array if necessary. Calls to this method should be guarded by a test of {@link #stretchy}.

param
desiredSize {@code >= 0;} the desired minimum total size of the array

        if (data.length < desiredSize) {
            byte[] newData = new byte[desiredSize * 2 + 1000];
            System.arraycopy(data, 0, newData, 0, cursor);
            data = newData;
        }
    
public voidfinishAnnotating()
Finishes up annotation processing. This closes off any open annotations and removes annotations that don't refer to written data.

        // Close off the final annotation, if any.
        endAnnotation();

        // Remove annotations that refer to unwritten data.
        if (annotations != null) {
            int asz = annotations.size();
            while (asz > 0) {
                Annotation last = annotations.get(asz - 1);
                if (last.getStart() > cursor) {
                    annotations.remove(asz - 1);
                    asz--;
                } else if (last.getEnd() > cursor) {
                    last.setEnd(cursor);
                    break;
                } else {
                    break;
                }
            }
        }
    
public intgetAnnotationWidth()
{@inheritDoc}

        int leftWidth = 8 + (hexCols * 2) + (hexCols / 2);

        return annotationWidth - leftWidth;
    
public byte[]getArray()
Gets the underlying {@code byte[]} of this instance, which may be larger than the number of bytes written

see
#toByteArray
return
{@code non-null;} the {@code byte[]}

        return data;
    
public intgetCursor()
{@inheritDoc}

        return cursor;
    
public booleanisVerbose()
{@inheritDoc}

        return verbose;
    
private static voidthrowBounds()
Throws the excpetion for when an attempt is made to write past the end of the instance.

        throw new IndexOutOfBoundsException("attempt to write past the end");
    
public byte[]toByteArray()
Constructs and returns a new {@code byte[]} that contains the written contents exactly (that is, with no extra unwritten bytes at the end).

see
#getArray
return
{@code non-null;} an appropriately-constructed array

        byte[] result = new byte[cursor];
        System.arraycopy(data, 0, result, 0, cursor);
        return result;
    
public voidwrite(ByteArray bytes)
{@inheritDoc}

        int blen = bytes.size();
        int writeAt = cursor;
        int end = writeAt + blen;

        if (stretchy) {
            ensureCapacity(end);
        } else if (end > data.length) {
            throwBounds();
            return;
        }

        bytes.getBytes(data, writeAt);
        cursor = end;
    
public voidwrite(byte[] bytes, int offset, int length)
{@inheritDoc}

        int writeAt = cursor;
        int end = writeAt + length;
        int bytesEnd = offset + length;

        // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
        if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) {
            throw new IndexOutOfBoundsException("bytes.length " +
                                                bytes.length + "; " +
                                                offset + "..!" + end);
        }

        if (stretchy) {
            ensureCapacity(end);
        } else if (end > data.length) {
            throwBounds();
            return;
        }

        System.arraycopy(bytes, offset, data, writeAt, length);
        cursor = end;
    
public voidwrite(byte[] bytes)
{@inheritDoc}

        write(bytes, 0, bytes.length);
    
public voidwriteAnnotationsTo(java.io.Writer out)
Writes the annotated content of this instance to the given writer.

param
out {@code non-null;} where to write to

        int width2 = getAnnotationWidth();
        int width1 = annotationWidth - width2 - 1;

        TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|");
        Writer left = twoc.getLeft();
        Writer right = twoc.getRight();
        int leftAt = 0; // left-hand byte output cursor
        int rightAt = 0; // right-hand annotation index
        int rightSz = annotations.size();

        while ((leftAt < cursor) && (rightAt < rightSz)) {
            Annotation a = annotations.get(rightAt);
            int start = a.getStart();
            int end;
            String text;

            if (leftAt < start) {
                // This is an area with no annotation.
                end = start;
                start = leftAt;
                text = "";
            } else {
                // This is an area with an annotation.
                end = a.getEnd();
                text = a.getText();
                rightAt++;
            }

            left.write(Hex.dump(data, start, end - start, start, hexCols, 6));
            right.write(text);
            twoc.flush();
            leftAt = end;
        }

        if (leftAt < cursor) {
            // There is unannotated output at the end.
            left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt,
                                hexCols, 6));
        }

        while (rightAt < rightSz) {
            // There are zero-byte annotations at the end.
            right.write(annotations.get(rightAt).getText());
            rightAt++;
        }

        twoc.flush();
    
public voidwriteByte(int value)
{@inheritDoc}

        int writeAt = cursor;
        int end = writeAt + 1;

        if (stretchy) {
            ensureCapacity(end);
        } else if (end > data.length) {
            throwBounds();
            return;
        }

        data[writeAt] = (byte) value;
        cursor = end;
    
public voidwriteInt(int value)
{@inheritDoc}

        int writeAt = cursor;
        int end = writeAt + 4;

        if (stretchy) {
            ensureCapacity(end);
        } else if (end > data.length) {
            throwBounds();
            return;
        }

        data[writeAt] = (byte) value;
        data[writeAt + 1] = (byte) (value >> 8);
        data[writeAt + 2] = (byte) (value >> 16);
        data[writeAt + 3] = (byte) (value >> 24);
        cursor = end;
    
public voidwriteLong(long value)
{@inheritDoc}

        int writeAt = cursor;
        int end = writeAt + 8;

        if (stretchy) {
            ensureCapacity(end);
        } else if (end > data.length) {
            throwBounds();
            return;
        }

        int half = (int) value;
        data[writeAt] = (byte) half;
        data[writeAt + 1] = (byte) (half >> 8);
        data[writeAt + 2] = (byte) (half >> 16);
        data[writeAt + 3] = (byte) (half >> 24);

        half = (int) (value >> 32);
        data[writeAt + 4] = (byte) half;
        data[writeAt + 5] = (byte) (half >> 8);
        data[writeAt + 6] = (byte) (half >> 16);
        data[writeAt + 7] = (byte) (half >> 24);

        cursor = end;
    
public voidwriteShort(int value)
{@inheritDoc}

        int writeAt = cursor;
        int end = writeAt + 2;

        if (stretchy) {
            ensureCapacity(end);
        } else if (end > data.length) {
            throwBounds();
            return;
        }

        data[writeAt] = (byte) value;
        data[writeAt + 1] = (byte) (value >> 8);
        cursor = end;
    
public intwriteSleb128(int value)
{@inheritDoc}

        if (stretchy) {
            ensureCapacity(cursor + 5); // pessimistic
        }
        int cursorBefore = cursor;
        Leb128.writeSignedLeb128(this, value);
        return (cursor - cursorBefore);
    
public intwriteUleb128(int value)
{@inheritDoc}

        if (stretchy) {
            ensureCapacity(cursor + 5); // pessimistic
        }
        int cursorBefore = cursor;
        Leb128.writeUnsignedLeb128(this, value);
        return (cursor - cursorBefore);
    
public voidwriteZeroes(int count)
{@inheritDoc}

        if (count < 0) {
            throw new IllegalArgumentException("count < 0");
        }

        int end = cursor + count;

        if (stretchy) {
            ensureCapacity(end);
        } else if (end > data.length) {
            throwBounds();
            return;
        }

        /*
         * There is no need to actually write zeroes, since the array is
         * already preinitialized with zeroes.
         */

        cursor = end;