Dex.java revision c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8b
12bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson/*
22bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * Copyright (C) 2011 The Android Open Source Project
32bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson *
42bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
52bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * you may not use this file except in compliance with the License.
62bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * You may obtain a copy of the License at
72bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson *
82bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
92bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson *
102bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * Unless required by applicable law or agreed to in writing, software
112bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
122bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * See the License for the specific language governing permissions and
142bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * limitations under the License.
152bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson */
162bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
172bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonpackage com.android.dex;
182bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
192bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport com.android.dex.util.ByteInput;
202bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport com.android.dex.util.ByteOutput;
212bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport com.android.dex.util.FileUtils;
222bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.io.ByteArrayOutputStream;
232bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.io.File;
242bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.io.FileInputStream;
252bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.io.FileOutputStream;
262bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.io.IOException;
272bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.io.InputStream;
282bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.io.OutputStream;
292bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.io.UTFDataFormatException;
30c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstromimport java.nio.ByteBuffer;
31c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstromimport java.nio.ByteOrder;
322bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.util.AbstractList;
332bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.util.Arrays;
342bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.util.Collections;
352bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.util.Iterator;
362bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.util.List;
372bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.util.NoSuchElementException;
382bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.util.zip.ZipEntry;
392bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonimport java.util.zip.ZipFile;
402bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
412bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson/**
422bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * The bytes of a dex file in memory for reading and writing. All int offsets
432bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson * are unsigned.
442bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson */
452bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilsonpublic final class Dex {
462bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private byte[] data;
472bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private final TableOfContents tableOfContents = new TableOfContents();
482bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private int length = 0;
492bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
502bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private final List<String> strings = new AbstractList<String>() {
512bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public String get(int index) {
522bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            checkBounds(index, tableOfContents.stringIds.size);
532bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
542bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    .readString();
552bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
562bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public int size() {
572bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return tableOfContents.stringIds.size;
582bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
592bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    };
602bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
612bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private final List<Integer> typeIds = new AbstractList<Integer>() {
622bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public Integer get(int index) {
632bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            checkBounds(index, tableOfContents.typeIds.size);
642bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();
652bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
662bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public int size() {
672bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return tableOfContents.typeIds.size;
682bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
692bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    };
702bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
712bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private final List<String> typeNames = new AbstractList<String>() {
722bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public String get(int index) {
732bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            checkBounds(index, tableOfContents.typeIds.size);
742bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return strings.get(typeIds.get(index));
752bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
762bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public int size() {
772bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return tableOfContents.typeIds.size;
782bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
792bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    };
802bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
812bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private final List<ProtoId> protoIds = new AbstractList<ProtoId>() {
822bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public ProtoId get(int index) {
832bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            checkBounds(index, tableOfContents.protoIds.size);
842bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
852bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    .readProtoId();
862bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
872bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public int size() {
882bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return tableOfContents.protoIds.size;
892bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
902bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    };
912bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
922bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private final List<FieldId> fieldIds = new AbstractList<FieldId>() {
932bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public FieldId get(int index) {
942bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            checkBounds(index, tableOfContents.fieldIds.size);
952bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
962bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    .readFieldId();
972bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
982bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public int size() {
992bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return tableOfContents.fieldIds.size;
1002bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
1012bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    };
1022bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
1032bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private final List<MethodId> methodIds = new AbstractList<MethodId>() {
1042bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public MethodId get(int index) {
1052bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            checkBounds(index, tableOfContents.methodIds.size);
1062bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
1072bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    .readMethodId();
1082bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
1092bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        @Override public int size() {
1102bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return tableOfContents.methodIds.size;
1112bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
1122bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    };
1132bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
1142bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    /**
1152bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson     * Creates a new dex buffer defining no classes.
1162bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson     */
1172bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public Dex() {
1182bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        this.data = new byte[0];
1192bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
1202bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
1212bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    /**
1222bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson     * Creates a new dex buffer that reads from {@code data}. It is an error to
1232bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson     * modify {@code data} after using it to create a dex buffer.
1242bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson     */
1252bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public Dex(byte[] data) throws IOException {
1262bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        this.data = data;
1272bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        this.length = data.length;
1282bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        this.tableOfContents.readFrom(this);
1292bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
1302bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
1312bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    /**
1322bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson     * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}.
1332bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson     */
1342bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public Dex(InputStream in) throws IOException {
1352bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        loadFrom(in);
1362bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
1372bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
1382bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    /**
1392bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson     * Creates a new dex buffer from the dex file {@code file}.
1402bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson     */
1412bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public Dex(File file) throws IOException {
1422bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        if (FileUtils.hasArchiveSuffix(file.getName())) {
1432bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ZipFile zipFile = new ZipFile(file);
1442bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
1452bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            if (entry != null) {
1462bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                loadFrom(zipFile.getInputStream(entry));
1472bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                zipFile.close();
1482bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            } else {
1492bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
1502bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
1512bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        } else if (file.getName().endsWith(".dex")) {
1522bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            loadFrom(new FileInputStream(file));
1532bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        } else {
1542bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            throw new DexException("unknown output extension: " + file);
1552bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
1562bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
1572bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
158c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom    /**
159c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom     * Creates a new dex from the contents of {@code bytes}. This API supports
160c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom     * both {@code .dex} and {@code .odex} input.
161c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom     */
162c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom    public static Dex create(ByteBuffer bytes) throws IOException {
163c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom        // if it's an .odex file, set position and limit to the .dex section
164c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom        if (bytes.get(0) == 'd'
165c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom                && bytes.get(1) == 'e'
166c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom                && bytes.get(2) == 'y'
167c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom                && bytes.get(3) == '\n') {
168c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom            bytes.position(8);
169c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom            bytes.order(ByteOrder.nativeOrder());
170c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom            int offset = bytes.getInt();
171c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom            int length = bytes.getInt();
172c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom            bytes.position(offset);
173c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom            bytes.limit(offset + length);
174c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom        }
175c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom
176c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom        // TODO: don't copy the bytes; use the ByteBuffer directly
177c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom        byte[] data = new byte[bytes.remaining()];
178c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom        bytes.get(data);
179c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom        return new Dex(data);
180c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom    }
181c69e85bfb7ddd6c4b0023ca29eeb0416087f9a8bBrian Carlstrom
1822bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private void loadFrom(InputStream in) throws IOException {
1832bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
1842bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        byte[] buffer = new byte[8192];
1852bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
1862bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        int count;
1872bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        while ((count = in.read(buffer)) != -1) {
1882bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            bytesOut.write(buffer, 0, count);
1892bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
1902bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        in.close();
1912bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
1922bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        this.data = bytesOut.toByteArray();
1932bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        this.length = data.length;
1942bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        this.tableOfContents.readFrom(this);
1952bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
1962bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
1972bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private static void checkBounds(int index, int length) {
1982bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        if (index < 0 || index >= length) {
1992bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
2002bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
2012bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2022bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2032bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public void writeTo(OutputStream out) throws IOException {
2042bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        out.write(data);
2052bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2062bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2072bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public void writeTo(File dexOut) throws IOException {
2082bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        OutputStream out = new FileOutputStream(dexOut);
2092bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        writeTo(out);
2102bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        out.close();
2112bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2122bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2132bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public TableOfContents getTableOfContents() {
2142bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return tableOfContents;
2152bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2162bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2172bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public Section open(int position) {
2182bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        if (position < 0 || position > length) {
2192bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            throw new IllegalArgumentException("position=" + position + " length=" + length);
2202bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
2212bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return new Section(position);
2222bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2232bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2242bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public Section appendSection(int maxByteCount, String name) {
2252bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        int limit = fourByteAlign(length + maxByteCount);
2262bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        Section result = new Section(name, length, limit);
2272bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        length = limit;
2282bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return result;
2292bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2302bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2312bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public void noMoreSections() {
2322bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        data = new byte[length];
2332bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2342bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2352bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public int getLength() {
2362bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return length;
2372bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2382bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2392bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    private static int fourByteAlign(int position) {
2402bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return (position + 3) & ~3;
2412bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2422bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2432bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public byte[] getBytes() {
2442bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return data;
2452bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2462bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2472bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public List<String> strings() {
2482bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return strings;
2492bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2502bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2512bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public List<Integer> typeIds() {
2522bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return typeIds;
2532bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2542bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2552bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public List<String> typeNames() {
2562bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return typeNames;
2572bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2582bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2592bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public List<ProtoId> protoIds() {
2602bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return protoIds;
2612bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2622bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2632bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public List<FieldId> fieldIds() {
2642bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return fieldIds;
2652bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2662bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2672bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public List<MethodId> methodIds() {
2682bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return methodIds;
2692bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2702bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2712bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public Iterable<ClassDef> classDefs() {
2722bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return new Iterable<ClassDef>() {
2732bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            public Iterator<ClassDef> iterator() {
2742bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                if (!tableOfContents.classDefs.exists()) {
2752bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    return Collections.<ClassDef>emptySet().iterator();
2762bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                }
2772bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                return new Iterator<ClassDef>() {
2782bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    private Dex.Section in = open(tableOfContents.classDefs.off);
2792bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    private int count = 0;
2802bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2812bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    public boolean hasNext() {
2822bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                        return count < tableOfContents.classDefs.size;
2832bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    }
2842bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    public ClassDef next() {
2852bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                        if (!hasNext()) {
2862bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                            throw new NoSuchElementException();
2872bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                        }
2882bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                        count++;
2892bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                        return in.readClassDef();
2902bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    }
2912bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    public void remove() {
2922bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                        throw new UnsupportedOperationException();
2932bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    }
2942bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                };
2952bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
2962bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        };
2972bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
2982bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
2992bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public TypeList readTypeList(int offset) {
3002bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        if (offset == 0) {
3012bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return TypeList.EMPTY;
3022bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3032bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return open(offset).readTypeList();
3042bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
3052bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3062bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public ClassData readClassData(ClassDef classDef) {
3072bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        int offset = classDef.getClassDataOffset();
3082bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        if (offset == 0) {
3092bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            throw new IllegalArgumentException("offset == 0");
3102bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3112bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return open(offset).readClassData();
3122bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
3132bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3142bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public Code readCode(ClassData.Method method) {
3152bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        int offset = method.getCodeOffset();
3162bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        if (offset == 0) {
3172bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            throw new IllegalArgumentException("offset == 0");
3182bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3192bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        return open(offset).readCode();
3202bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
3212bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3222bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    public final class Section implements ByteInput, ByteOutput {
3232bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private final String name;
3242bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private int position;
3252bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private final int limit;
3262bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3272bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private Section(String name, int position, int limit) {
3282bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            this.name = name;
3292bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            this.position = position;
3302bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            this.limit = limit;
3312bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3322bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3332bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private Section(int position) {
3342bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            this("section", position, data.length);
3352bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3362bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3372bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public int getPosition() {
3382bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return position;
3392bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3402bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3412bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public int readInt() {
3422bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int result = (data[position] & 0xff)
3432bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    | (data[position + 1] & 0xff) << 8
3442bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    | (data[position + 2] & 0xff) << 16
3452bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    | (data[position + 3] & 0xff) << 24;
3462bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            position += 4;
3472bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return result;
3482bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3492bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3502bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public short readShort() {
3512bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int result = (data[position] & 0xff)
3522bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    | (data[position + 1] & 0xff) << 8;
3532bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            position += 2;
3542bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return (short) result;
3552bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3562bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3572bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public int readUnsignedShort() {
3582bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return readShort() & 0xffff;
3592bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3602bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3612bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public byte readByte() {
3622bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return (byte) (data[position++] & 0xff);
3632bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3642bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3652bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public byte[] readByteArray(int length) {
3662bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            byte[] result = Arrays.copyOfRange(data, position, position + length);
3672bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            position += length;
3682bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return result;
3692bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3702bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3712bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public short[] readShortArray(int length) {
3722bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            short[] result = new short[length];
3732bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            for (int i = 0; i < length; i++) {
3742bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                result[i] = readShort();
3752bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
3762bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return result;
3772bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3782bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3792bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public int readUleb128() {
3802bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return Leb128.readUnsignedLeb128(this);
3812bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3822bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3832bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public int readSleb128() {
3842bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return Leb128.readSignedLeb128(this);
3852bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3862bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3872bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public TypeList readTypeList() {
3882bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int size = readInt();
3892bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            short[] types = new short[size];
3902bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            for (int i = 0; i < size; i++) {
3912bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                types[i] = readShort();
3922bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
3932bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            alignToFourBytes();
3942bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return new TypeList(Dex.this, types);
3952bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
3962bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
3972bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public String readString() {
3982bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int offset = readInt();
3992bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int savedPosition = position;
4002bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            position = offset;
4012bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            try {
4022bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                int expectedLength = readUleb128();
4032bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                String result = Mutf8.decode(this, new char[expectedLength]);
4042bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                if (result.length() != expectedLength) {
4052bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    throw new DexException("Declared length " + expectedLength
4062bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                            + " doesn't match decoded length of " + result.length());
4072bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                }
4082bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                return result;
4092bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            } catch (UTFDataFormatException e) {
4102bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                throw new DexException(e);
4112bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            } finally {
4122bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                position = savedPosition;
4132bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
4142bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
4152bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
4162bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public FieldId readFieldId() {
4172bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int declaringClassIndex = readUnsignedShort();
4182bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int typeIndex = readUnsignedShort();
4192bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int nameIndex = readInt();
4202bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex);
4212bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
4222bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
4232bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public MethodId readMethodId() {
4242bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int declaringClassIndex = readUnsignedShort();
4252bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int protoIndex = readUnsignedShort();
4262bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int nameIndex = readInt();
4272bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex);
4282bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
4292bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
4302bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public ProtoId readProtoId() {
4312bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int shortyIndex = readInt();
4322bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int returnTypeIndex = readInt();
4332bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int parametersOffset = readInt();
4342bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset);
4352bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
4362bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
4372bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public ClassDef readClassDef() {
4382bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int offset = getPosition();
4392bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int type = readInt();
4402bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int accessFlags = readInt();
4412bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int supertype = readInt();
4422bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int interfacesOffset = readInt();
4432bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int sourceFileIndex = readInt();
4442bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int annotationsOffset = readInt();
4452bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int classDataOffset = readInt();
4462bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int staticValuesOffset = readInt();
4472bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return new ClassDef(Dex.this, offset, type, accessFlags, supertype,
4482bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
4492bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    staticValuesOffset);
4502bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
4512bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
4522bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private Code readCode() {
4532bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int registersSize = readUnsignedShort();
4542bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int insSize = readUnsignedShort();
4552bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int outsSize = readUnsignedShort();
4562bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int triesSize = readUnsignedShort();
4572bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int debugInfoOffset = readInt();
4582bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int instructionsSize = readInt();
4592bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            short[] instructions = readShortArray(instructionsSize);
4602bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            Code.Try[] tries = new Code.Try[triesSize];
4612bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            Code.CatchHandler[] catchHandlers = new Code.CatchHandler[0];
4622bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            if (triesSize > 0) {
4632bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                if (instructions.length % 2 == 1) {
4642bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    readShort(); // padding
4652bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                }
4662bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
4672bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                for (int i = 0; i < triesSize; i++) {
4682bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    int startAddress = readInt();
4692bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    int instructionCount = readUnsignedShort();
4702bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    int handlerOffset = readUnsignedShort();
4712bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    tries[i] = new Code.Try(startAddress, instructionCount, handlerOffset);
4722bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                }
4732bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
4742bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                int catchHandlersSize = readUleb128();
4752bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                catchHandlers = new Code.CatchHandler[catchHandlersSize];
4762bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                for (int i = 0; i < catchHandlersSize; i++) {
4772bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    catchHandlers[i] = readCatchHandler();
4782bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                }
4792bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
4802bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
4812bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    tries, catchHandlers);
4822bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
4832bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
4842bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private Code.CatchHandler readCatchHandler() {
4852bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int size = readSleb128();
4862bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int handlersCount = Math.abs(size);
4872bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int[] typeIndexes = new int[handlersCount];
4882bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int[] addresses = new int[handlersCount];
4892bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            for (int i = 0; i < handlersCount; i++) {
4902bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                typeIndexes[i] = readUleb128();
4912bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                addresses[i] = readUleb128();
4922bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
4932bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int catchAllAddress = size <= 0 ? readUleb128() : -1;
4942bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return new Code.CatchHandler(typeIndexes, addresses, catchAllAddress);
4952bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
4962bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
4972bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private ClassData readClassData() {
4982bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int staticFieldsSize = readUleb128();
4992bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int instanceFieldsSize = readUleb128();
5002bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int directMethodsSize = readUleb128();
5012bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int virtualMethodsSize = readUleb128();
5022bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ClassData.Field[] staticFields = readFields(staticFieldsSize);
5032bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
5042bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ClassData.Method[] directMethods = readMethods(directMethodsSize);
5052bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
5062bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
5072bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5082bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5092bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private ClassData.Field[] readFields(int count) {
5102bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ClassData.Field[] result = new ClassData.Field[count];
5112bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int fieldIndex = 0;
5122bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            for (int i = 0; i < count; i++) {
5132bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                fieldIndex += readUleb128(); // field index diff
5142bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                int accessFlags = readUleb128();
5152bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                result[i] = new ClassData.Field(fieldIndex, accessFlags);
5162bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
5172bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return result;
5182bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5192bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5202bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private ClassData.Method[] readMethods(int count) {
5212bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ClassData.Method[] result = new ClassData.Method[count];
5222bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int methodIndex = 0;
5232bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            for (int i = 0; i < count; i++) {
5242bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                methodIndex += readUleb128(); // method index diff
5252bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                int accessFlags = readUleb128();
5262bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                int codeOff = readUleb128();
5272bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
5282bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
5292bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return result;
5302bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5312bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5322bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public Annotation readAnnotation() {
5332bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            byte visibility = readByte();
5342bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int start = position;
5352bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue();
5362bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int end = position;
5372bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return new Annotation(Dex.this, visibility,
5382bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                    new EncodedValue(Arrays.copyOfRange(data, start, end)));
5392bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5402bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5412bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public EncodedValue readEncodedArray() {
5422bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int start = position;
5432bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue();
5442bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int end = position;
5452bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return new EncodedValue(Arrays.copyOfRange(data, start, end));
5462bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5472bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5482bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        private void ensureCapacity(int size) {
5492bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            if (position + size > limit) {
5502bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                throw new DexException("Section limit " + limit + " exceeded by " + name);
5512bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
5522bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5532bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5542bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        /**
5552bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson         * Writes 0x00 until the position is aligned to a multiple of 4.
5562bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson         */
5572bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void alignToFourBytes() {
5582bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            int unalignedCount = position;
5592bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            position = Dex.fourByteAlign(position);
5602bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            for (int i = unalignedCount; i < position; i++) {
5612bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                data[i] = 0;
5622bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
5632bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5642bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5652bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void assertFourByteAligned() {
5662bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            if ((position & 3) != 0) {
5672bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                throw new IllegalStateException("Not four byte aligned!");
5682bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
5692bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5702bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5712bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void write(byte[] bytes) {
5722bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ensureCapacity(bytes.length);
5732bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            System.arraycopy(bytes, 0, data, position, bytes.length);
5742bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            position += bytes.length;
5752bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5762bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5772bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void writeByte(int b) {
5782bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ensureCapacity(1);
5792bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            data[position++] = (byte) b;
5802bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5812bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5822bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void writeShort(short i) {
5832bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ensureCapacity(2);
5842bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            data[position    ] = (byte) i;
5852bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            data[position + 1] = (byte) (i >>> 8);
5862bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            position += 2;
5872bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5882bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5892bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void writeUnsignedShort(int i) {
5902bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            short s = (short) i;
5912bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            if (i != (s & 0xffff)) {
5922bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                throw new IllegalArgumentException("Expected an unsigned short: " + i);
5932bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
5942bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            writeShort(s);
5952bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
5962bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
5972bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void write(short[] shorts) {
5982bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            for (short s : shorts) {
5992bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                writeShort(s);
6002bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
6012bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
6022bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
6032bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void writeInt(int i) {
6042bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            ensureCapacity(4);
6052bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            data[position    ] = (byte) i;
6062bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            data[position + 1] = (byte) (i >>>  8);
6072bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            data[position + 2] = (byte) (i >>> 16);
6082bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            data[position + 3] = (byte) (i >>> 24);
6092bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            position += 4;
6102bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
6112bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
6122bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void writeUleb128(int i) {
6132bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            try {
6142bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                Leb128.writeUnsignedLeb128(this, i);
6152bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                ensureCapacity(0);
6162bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            } catch (ArrayIndexOutOfBoundsException e) {
6172bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                throw new DexException("Section limit " + limit + " exceeded by " + name);
6182bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
6192bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
6202bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
6212bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void writeSleb128(int i) {
6222bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            try {
6232bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                Leb128.writeSignedLeb128(this, i);
6242bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                ensureCapacity(0);
6252bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            } catch (ArrayIndexOutOfBoundsException e) {
6262bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                throw new DexException("Section limit " + limit + " exceeded by " + name);
6272bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
6282bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
6292bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
6302bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void writeStringData(String value) {
6312bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            try {
6322bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                int length = value.length();
6332bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                writeUleb128(length);
6342bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                write(Mutf8.encode(value));
6352bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                writeByte(0);
6362bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            } catch (UTFDataFormatException e) {
6372bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                throw new AssertionError();
6382bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
6392bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
6402bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
6412bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public void writeTypeList(TypeList typeList) {
6422bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            short[] types = typeList.getTypes();
6432bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            writeInt(types.length);
6442bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            for (short type : types) {
6452bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson                writeShort(type);
6462bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            }
6472bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            alignToFourBytes();
6482bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
6492bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson
6502bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        /**
6512bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson         * Returns the number of bytes remaining in this section.
6522bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson         */
6532bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        public int remaining() {
6542bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson            return limit - position;
6552bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson        }
6562bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson    }
6572bea5ee615b0f4add658d5660bd81c5145a0d05eJesse Wilson}
658