Dx: Move all com.android.dex sources from libcore.

They are no longer used there and dx is their last remaining user.

Test: dalvik/dx/tests/run-all-tests

Change-Id: Ib7d34998a7d2aa018768c63e8c3f64e2b26a9244
diff --git a/dx/junit-tests/com/android/dex/EncodedValueReaderTest.java b/dx/junit-tests/com/android/dex/EncodedValueReaderTest.java
new file mode 100644
index 0000000..a4ca376
--- /dev/null
+++ b/dx/junit-tests/com/android/dex/EncodedValueReaderTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.ByteArrayByteInput;
+import junit.framework.TestCase;
+
+public final class EncodedValueReaderTest extends TestCase {
+
+    public void testReadByte() {
+        assertEquals((byte) 0x80, readerOf(0, 0x80).readByte());
+        assertEquals((byte) 0xff, readerOf(0, 0xff).readByte());
+        assertEquals((byte) 0x00, readerOf(0, 0x00).readByte());
+        assertEquals((byte) 0x01, readerOf(0, 0x01).readByte());
+        assertEquals((byte) 0x7f, readerOf(0, 0x7f).readByte());
+    }
+
+    public void testReadShort() {
+        assertEquals((short) 0x8000, readerOf(34, 0x00, 0x80).readShort());
+        assertEquals((short)      0, readerOf( 2, 0x00).readShort());
+        assertEquals((short)   0xab, readerOf(34, 0xab, 0x00).readShort());
+        assertEquals((short) 0xabcd, readerOf(34, 0xcd, 0xab).readShort());
+        assertEquals((short) 0x7FFF, readerOf(34, 0xff, 0x7f).readShort());
+    }
+
+    public void testReadInt() {
+        assertEquals(0x80000000, readerOf(100, 0x00, 0x00, 0x00, 0x80).readInt());
+        assertEquals(      0x00, readerOf(  4, 0x00).readInt());
+        assertEquals(      0xab, readerOf( 36, 0xab, 0x00).readInt());
+        assertEquals(    0xabcd, readerOf( 68, 0xcd, 0xab, 0x00).readInt());
+        assertEquals(  0xabcdef, readerOf(100, 0xef, 0xcd, 0xab, 0x00).readInt());
+        assertEquals(0xabcdef01, readerOf(100, 0x01, 0xef, 0xcd, 0xab).readInt());
+        assertEquals(0x7fffffff, readerOf(100, 0xff, 0xff, 0xff, 127).readInt());
+    }
+
+    public void testReadLong() {
+        assertEquals(0x8000000000000000L, readerOf( -26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80).readLong());
+        assertEquals(              0x00L, readerOf(   6, 0x00).readLong());
+        assertEquals(              0xabL, readerOf(  38, 0xab, 0x00).readLong());
+        assertEquals(            0xabcdL, readerOf(  70, 0xcd, 0xab, 0x00).readLong());
+        assertEquals(          0xabcdefL, readerOf( 102, 0xef, 0xcd, 0xab, 0x00).readLong());
+        assertEquals(        0xabcdef01L, readerOf(-122, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong());
+        assertEquals(      0xabcdef0123L, readerOf( -90, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong());
+        assertEquals(    0xabcdef012345L, readerOf( -58, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong());
+        assertEquals(  0xabcdef01234567L, readerOf( -26, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong());
+        assertEquals(0xabcdef0123456789L, readerOf( -26, 0x89, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab).readLong());
+        assertEquals(0x7fffffffffffffffL, readerOf( -26, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f).readLong());
+    }
+
+    public void testReadFloat() {
+        assertEquals(Float.NEGATIVE_INFINITY, readerOf(48, -128, -1).readFloat());
+        assertEquals(Float.POSITIVE_INFINITY, readerOf(48, -128, 127).readFloat());
+        assertEquals(Float.NaN, readerOf(48, -64, 127).readFloat());
+        assertEquals(-0.0f, readerOf(16, -128).readFloat());
+        assertEquals(0.0f, readerOf(16, 0).readFloat());
+        assertEquals(0.5f, readerOf(16, 63).readFloat());
+        assertEquals(1f, readerOf(48, -128, 63).readFloat());
+        assertEquals(1.0E06f, readerOf(80, 36, 116, 73).readFloat());
+        assertEquals(1.0E12f, readerOf(112, -91, -44, 104, 83).readFloat());
+    }
+
+    public void testReadDouble() {
+        assertEquals(Double.NEGATIVE_INFINITY, readerOf(49, -16, -1).readDouble());
+        assertEquals(Double.POSITIVE_INFINITY, readerOf(49, -16, 127).readDouble());
+        assertEquals(Double.NaN, readerOf(49, -8, 127).readDouble());
+        assertEquals(-0.0, readerOf(17, -128).readDouble());
+        assertEquals(0.0, readerOf(17, 0).readDouble());
+        assertEquals(0.5, readerOf(49, -32, 63).readDouble());
+        assertEquals(1.0, readerOf(49, -16, 63).readDouble());
+        assertEquals(1.0E06, readerOf(113, -128, -124, 46, 65).readDouble());
+        assertEquals(1.0E12, readerOf(-111, -94, -108, 26, 109, 66).readDouble());
+        assertEquals(1.0E24, readerOf(-15, -76, -99, -39, 121, 67, 120, -22, 68).readDouble());
+    }
+
+    public void testReadChar() {
+        assertEquals('\u0000', readerOf( 3, 0x00).readChar());
+        assertEquals('\u00ab', readerOf( 3, 0xab).readChar());
+        assertEquals('\uabcd', readerOf(35, 0xcd, 0xab).readChar());
+        assertEquals('\uffff', readerOf(35, 0xff, 0xff).readChar());
+    }
+
+    public void testReadBoolean() {
+        assertEquals(true, readerOf(63).readBoolean());
+        assertEquals(false, readerOf(31).readBoolean());
+    }
+
+    public void testReadNull() {
+        readerOf(30).readNull();
+    }
+
+    public void testReadReference() {
+        assertEquals(      0xab, readerOf(0x17, 0xab).readString());
+        assertEquals(    0xabcd, readerOf(0x37, 0xcd, 0xab).readString());
+        assertEquals(  0xabcdef, readerOf(0x57, 0xef, 0xcd, 0xab).readString());
+        assertEquals(0xabcdef01, readerOf(0x77, 0x01, 0xef, 0xcd, 0xab).readString());
+    }
+
+    public void testReadWrongType() {
+        try {
+            readerOf(0x17, 0xab).readField();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    private EncodedValueReader readerOf(int... bytes) {
+        byte[] data = new byte[bytes.length];
+        for (int i = 0; i < bytes.length; i++) {
+            data[i] = (byte) bytes[i];
+        }
+        return new EncodedValueReader(new ByteArrayByteInput(data));
+    }
+}
diff --git a/dx/src/Android.mk b/dx/src/Android.mk
index 248f854..c295f81 100644
--- a/dx/src/Android.mk
+++ b/dx/src/Android.mk
@@ -5,9 +5,7 @@
 # This tool is prebuilt if we're doing an app-only build.
 ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
 
-dx_src_files := \
-  $(call all-subdir-java-files) \
-  $(call all-java-files-under,../../../libcore/dex/src/main/java)
+dx_src_files := $(call all-subdir-java-files)
 
 # dx java library
 # ============================================================
diff --git a/dx/src/com/android/dex/Annotation.java b/dx/src/com/android/dex/Annotation.java
new file mode 100644
index 0000000..e5ef978
--- /dev/null
+++ b/dx/src/com/android/dex/Annotation.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import static com.android.dex.EncodedValueReader.ENCODED_ANNOTATION;
+
+/**
+ * An annotation.
+ */
+public final class Annotation implements Comparable<Annotation> {
+    private final Dex dex;
+    private final byte visibility;
+    private final EncodedValue encodedAnnotation;
+
+    public Annotation(Dex dex, byte visibility, EncodedValue encodedAnnotation) {
+        this.dex = dex;
+        this.visibility = visibility;
+        this.encodedAnnotation = encodedAnnotation;
+    }
+
+    public byte getVisibility() {
+        return visibility;
+    }
+
+    public EncodedValueReader getReader() {
+        return new EncodedValueReader(encodedAnnotation, ENCODED_ANNOTATION);
+    }
+
+    public int getTypeIndex() {
+        EncodedValueReader reader = getReader();
+        reader.readAnnotation();
+        return reader.getAnnotationType();
+    }
+
+    public void writeTo(Dex.Section out) {
+        out.writeByte(visibility);
+        encodedAnnotation.writeTo(out);
+    }
+
+    @Override public int compareTo(Annotation other) {
+        return encodedAnnotation.compareTo(other.encodedAnnotation);
+    }
+
+    @Override public String toString() {
+        return dex == null
+                ? visibility + " " + getTypeIndex()
+                : visibility + " " + dex.typeNames().get(getTypeIndex());
+    }
+}
diff --git a/dx/src/com/android/dex/ClassData.java b/dx/src/com/android/dex/ClassData.java
new file mode 100644
index 0000000..840756c
--- /dev/null
+++ b/dx/src/com/android/dex/ClassData.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+public final class ClassData {
+    private final Field[] staticFields;
+    private final Field[] instanceFields;
+    private final Method[] directMethods;
+    private final Method[] virtualMethods;
+
+    public ClassData(Field[] staticFields, Field[] instanceFields,
+            Method[] directMethods, Method[] virtualMethods) {
+        this.staticFields = staticFields;
+        this.instanceFields = instanceFields;
+        this.directMethods = directMethods;
+        this.virtualMethods = virtualMethods;
+    }
+
+    public Field[] getStaticFields() {
+        return staticFields;
+    }
+
+    public Field[] getInstanceFields() {
+        return instanceFields;
+    }
+
+    public Method[] getDirectMethods() {
+        return directMethods;
+    }
+
+    public Method[] getVirtualMethods() {
+        return virtualMethods;
+    }
+
+    public Field[] allFields() {
+        Field[] result = new Field[staticFields.length + instanceFields.length];
+        System.arraycopy(staticFields, 0, result, 0, staticFields.length);
+        System.arraycopy(instanceFields, 0, result, staticFields.length, instanceFields.length);
+        return result;
+    }
+
+    public Method[] allMethods() {
+        Method[] result = new Method[directMethods.length + virtualMethods.length];
+        System.arraycopy(directMethods, 0, result, 0, directMethods.length);
+        System.arraycopy(virtualMethods, 0, result, directMethods.length, virtualMethods.length);
+        return result;
+    }
+
+    public static class Field {
+        private final int fieldIndex;
+        private final int accessFlags;
+
+        public Field(int fieldIndex, int accessFlags) {
+            this.fieldIndex = fieldIndex;
+            this.accessFlags = accessFlags;
+        }
+
+        public int getFieldIndex() {
+            return fieldIndex;
+        }
+
+        public int getAccessFlags() {
+            return accessFlags;
+        }
+    }
+
+    public static class Method {
+        private final int methodIndex;
+        private final int accessFlags;
+        private final int codeOffset;
+
+        public Method(int methodIndex, int accessFlags, int codeOffset) {
+            this.methodIndex = methodIndex;
+            this.accessFlags = accessFlags;
+            this.codeOffset = codeOffset;
+        }
+
+        public int getMethodIndex() {
+            return methodIndex;
+        }
+
+        public int getAccessFlags() {
+            return accessFlags;
+        }
+
+        public int getCodeOffset() {
+            return codeOffset;
+        }
+    }
+}
diff --git a/dx/src/com/android/dex/ClassDef.java b/dx/src/com/android/dex/ClassDef.java
new file mode 100644
index 0000000..b3225ec
--- /dev/null
+++ b/dx/src/com/android/dex/ClassDef.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+/**
+ * A type definition.
+ */
+public final class ClassDef {
+    public static final int NO_INDEX = -1;
+    private final Dex buffer;
+    private final int offset;
+    private final int typeIndex;
+    private final int accessFlags;
+    private final int supertypeIndex;
+    private final int interfacesOffset;
+    private final int sourceFileIndex;
+    private final int annotationsOffset;
+    private final int classDataOffset;
+    private final int staticValuesOffset;
+
+    public ClassDef(Dex buffer, int offset, int typeIndex, int accessFlags,
+            int supertypeIndex, int interfacesOffset, int sourceFileIndex,
+            int annotationsOffset, int classDataOffset, int staticValuesOffset) {
+        this.buffer = buffer;
+        this.offset = offset;
+        this.typeIndex = typeIndex;
+        this.accessFlags = accessFlags;
+        this.supertypeIndex = supertypeIndex;
+        this.interfacesOffset = interfacesOffset;
+        this.sourceFileIndex = sourceFileIndex;
+        this.annotationsOffset = annotationsOffset;
+        this.classDataOffset = classDataOffset;
+        this.staticValuesOffset = staticValuesOffset;
+    }
+
+    public int getOffset() {
+        return offset;
+    }
+
+    public int getTypeIndex() {
+        return typeIndex;
+    }
+
+    public int getSupertypeIndex() {
+        return supertypeIndex;
+    }
+
+    public int getInterfacesOffset() {
+        return interfacesOffset;
+    }
+
+    public short[] getInterfaces() {
+        return buffer.readTypeList(interfacesOffset).getTypes();
+    }
+
+    public int getAccessFlags() {
+        return accessFlags;
+    }
+
+    public int getSourceFileIndex() {
+        return sourceFileIndex;
+    }
+
+    public int getAnnotationsOffset() {
+        return annotationsOffset;
+    }
+
+    public int getClassDataOffset() {
+        return classDataOffset;
+    }
+
+    public int getStaticValuesOffset() {
+        return staticValuesOffset;
+    }
+
+    @Override public String toString() {
+        if (buffer == null) {
+            return typeIndex + " " + supertypeIndex;
+        }
+
+        StringBuilder result = new StringBuilder();
+        result.append(buffer.typeNames().get(typeIndex));
+        if (supertypeIndex != NO_INDEX) {
+            result.append(" extends ").append(buffer.typeNames().get(supertypeIndex));
+        }
+        return result.toString();
+    }
+}
diff --git a/dx/src/com/android/dex/Code.java b/dx/src/com/android/dex/Code.java
new file mode 100644
index 0000000..9258af7
--- /dev/null
+++ b/dx/src/com/android/dex/Code.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+public final class Code {
+    private final int registersSize;
+    private final int insSize;
+    private final int outsSize;
+    private final int debugInfoOffset;
+    private final short[] instructions;
+    private final Try[] tries;
+    private final CatchHandler[] catchHandlers;
+
+    public Code(int registersSize, int insSize, int outsSize, int debugInfoOffset,
+            short[] instructions, Try[] tries, CatchHandler[] catchHandlers) {
+        this.registersSize = registersSize;
+        this.insSize = insSize;
+        this.outsSize = outsSize;
+        this.debugInfoOffset = debugInfoOffset;
+        this.instructions = instructions;
+        this.tries = tries;
+        this.catchHandlers = catchHandlers;
+    }
+
+    public int getRegistersSize() {
+        return registersSize;
+    }
+
+    public int getInsSize() {
+        return insSize;
+    }
+
+    public int getOutsSize() {
+        return outsSize;
+    }
+
+    public int getDebugInfoOffset() {
+        return debugInfoOffset;
+    }
+
+    public short[] getInstructions() {
+        return instructions;
+    }
+
+    public Try[] getTries() {
+        return tries;
+    }
+
+    public CatchHandler[] getCatchHandlers() {
+        return catchHandlers;
+    }
+
+    public static class Try {
+        final int startAddress;
+        final int instructionCount;
+        final int catchHandlerIndex;
+
+        Try(int startAddress, int instructionCount, int catchHandlerIndex) {
+            this.startAddress = startAddress;
+            this.instructionCount = instructionCount;
+            this.catchHandlerIndex = catchHandlerIndex;
+        }
+
+        public int getStartAddress() {
+            return startAddress;
+        }
+
+        public int getInstructionCount() {
+            return instructionCount;
+        }
+
+        /**
+         * Returns this try's catch handler <strong>index</strong>. Note that
+         * this is distinct from the its catch handler <strong>offset</strong>.
+         */
+        public int getCatchHandlerIndex() {
+            return catchHandlerIndex;
+        }
+    }
+
+    public static class CatchHandler {
+        final int[] typeIndexes;
+        final int[] addresses;
+        final int catchAllAddress;
+        final int offset;
+
+        public CatchHandler(int[] typeIndexes, int[] addresses, int catchAllAddress, int offset) {
+            this.typeIndexes = typeIndexes;
+            this.addresses = addresses;
+            this.catchAllAddress = catchAllAddress;
+            this.offset = offset;
+        }
+
+        public int[] getTypeIndexes() {
+            return typeIndexes;
+        }
+
+        public int[] getAddresses() {
+            return addresses;
+        }
+
+        public int getCatchAllAddress() {
+            return catchAllAddress;
+        }
+
+        public int getOffset() {
+            return offset;
+        }
+    }
+}
diff --git a/dx/src/com/android/dex/Dex.java b/dx/src/com/android/dex/Dex.java
new file mode 100644
index 0000000..16293262
--- /dev/null
+++ b/dx/src/com/android/dex/Dex.java
@@ -0,0 +1,791 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.Code.CatchHandler;
+import com.android.dex.Code.Try;
+import com.android.dex.util.ByteInput;
+import com.android.dex.util.ByteOutput;
+import com.android.dex.util.FileUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UTFDataFormatException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.AbstractList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.RandomAccess;
+import java.util.zip.Adler32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * The bytes of a dex file in memory for reading and writing. All int offsets
+ * are unsigned.
+ */
+public final class Dex {
+    private static final int CHECKSUM_OFFSET = 8;
+    private static final int CHECKSUM_SIZE = 4;
+    private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE;
+    private static final int SIGNATURE_SIZE = 20;
+    // Provided as a convenience to avoid a memory allocation to benefit Dalvik.
+    // Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik.
+    static final short[] EMPTY_SHORT_ARRAY = new short[0];
+
+    private ByteBuffer data;
+    private final TableOfContents tableOfContents = new TableOfContents();
+    private int nextSectionStart = 0;
+    private final StringTable strings = new StringTable();
+    private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable();
+    private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable();
+    private final ProtoIdTable protoIds = new ProtoIdTable();
+    private final FieldIdTable fieldIds = new FieldIdTable();
+    private final MethodIdTable methodIds = new MethodIdTable();
+
+    /**
+     * Creates a new dex that reads from {@code data}. It is an error to modify
+     * {@code data} after using it to create a dex buffer.
+     */
+    public Dex(byte[] data) throws IOException {
+        this(ByteBuffer.wrap(data));
+    }
+
+    private Dex(ByteBuffer data) throws IOException {
+        this.data = data;
+        this.data.order(ByteOrder.LITTLE_ENDIAN);
+        this.tableOfContents.readFrom(this);
+    }
+
+    /**
+     * Creates a new empty dex of the specified size.
+     */
+    public Dex(int byteCount) throws IOException {
+        this.data = ByteBuffer.wrap(new byte[byteCount]);
+        this.data.order(ByteOrder.LITTLE_ENDIAN);
+    }
+
+    /**
+     * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}.
+     */
+    public Dex(InputStream in) throws IOException {
+        try {
+            loadFrom(in);
+        } finally {
+            in.close();
+        }
+    }
+
+    /**
+     * Creates a new dex buffer from the dex file {@code file}.
+     */
+    public Dex(File file) throws IOException {
+        if (FileUtils.hasArchiveSuffix(file.getName())) {
+            ZipFile zipFile = new ZipFile(file);
+            ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
+            if (entry != null) {
+                try (InputStream inputStream = zipFile.getInputStream(entry)) {
+                    loadFrom(inputStream);
+                }
+                zipFile.close();
+            } else {
+                throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
+            }
+        } else if (file.getName().endsWith(".dex")) {
+            try (InputStream inputStream = new FileInputStream(file)) {
+                loadFrom(inputStream);
+            }
+        } else {
+            throw new DexException("unknown output extension: " + file);
+        }
+    }
+
+    /**
+     * It is the caller's responsibility to close {@code in}.
+     */
+    private void loadFrom(InputStream in) throws IOException {
+        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+        byte[] buffer = new byte[8192];
+
+        int count;
+        while ((count = in.read(buffer)) != -1) {
+            bytesOut.write(buffer, 0, count);
+        }
+
+        this.data = ByteBuffer.wrap(bytesOut.toByteArray());
+        this.data.order(ByteOrder.LITTLE_ENDIAN);
+        this.tableOfContents.readFrom(this);
+    }
+
+    private static void checkBounds(int index, int length) {
+        if (index < 0 || index >= length) {
+            throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
+        }
+    }
+
+    public void writeTo(OutputStream out) throws IOException {
+        byte[] buffer = new byte[8192];
+        ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
+        data.clear();
+        while (data.hasRemaining()) {
+            int count = Math.min(buffer.length, data.remaining());
+            data.get(buffer, 0, count);
+            out.write(buffer, 0, count);
+        }
+    }
+
+    public void writeTo(File dexOut) throws IOException {
+        try (OutputStream out = new FileOutputStream(dexOut)) {
+            writeTo(out);
+        }
+    }
+
+    public TableOfContents getTableOfContents() {
+        return tableOfContents;
+    }
+
+    public Section open(int position) {
+        if (position < 0 || position >= data.capacity()) {
+            throw new IllegalArgumentException("position=" + position
+                    + " length=" + data.capacity());
+        }
+        ByteBuffer sectionData = data.duplicate();
+        sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
+        sectionData.position(position);
+        sectionData.limit(data.capacity());
+        return new Section("section", sectionData);
+    }
+
+    public Section appendSection(int maxByteCount, String name) {
+        if ((maxByteCount & 3) != 0) {
+            throw new IllegalStateException("Not four byte aligned!");
+        }
+        int limit = nextSectionStart + maxByteCount;
+        ByteBuffer sectionData = data.duplicate();
+        sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
+        sectionData.position(nextSectionStart);
+        sectionData.limit(limit);
+        Section result = new Section(name, sectionData);
+        nextSectionStart = limit;
+        return result;
+    }
+
+    public int getLength() {
+        return data.capacity();
+    }
+
+    public int getNextSectionStart() {
+        return nextSectionStart;
+    }
+
+    /**
+     * Returns a copy of the the bytes of this dex.
+     */
+    public byte[] getBytes() {
+        ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
+        byte[] result = new byte[data.capacity()];
+        data.position(0);
+        data.get(result);
+        return result;
+    }
+
+    public List<String> strings() {
+        return strings;
+    }
+
+    public List<Integer> typeIds() {
+        return typeIds;
+    }
+
+    public List<String> typeNames() {
+        return typeNames;
+    }
+
+    public List<ProtoId> protoIds() {
+        return protoIds;
+    }
+
+    public List<FieldId> fieldIds() {
+        return fieldIds;
+    }
+
+    public List<MethodId> methodIds() {
+        return methodIds;
+    }
+
+    public Iterable<ClassDef> classDefs() {
+        return new ClassDefIterable();
+    }
+
+    public TypeList readTypeList(int offset) {
+        if (offset == 0) {
+            return TypeList.EMPTY;
+        }
+        return open(offset).readTypeList();
+    }
+
+    public ClassData readClassData(ClassDef classDef) {
+        int offset = classDef.getClassDataOffset();
+        if (offset == 0) {
+            throw new IllegalArgumentException("offset == 0");
+        }
+        return open(offset).readClassData();
+    }
+
+    public Code readCode(ClassData.Method method) {
+        int offset = method.getCodeOffset();
+        if (offset == 0) {
+            throw new IllegalArgumentException("offset == 0");
+        }
+        return open(offset).readCode();
+    }
+
+    /**
+     * Returns the signature of all but the first 32 bytes of this dex. The
+     * first 32 bytes of dex files are not specified to be included in the
+     * signature.
+     */
+    public byte[] computeSignature() throws IOException {
+        MessageDigest digest;
+        try {
+            digest = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError();
+        }
+        byte[] buffer = new byte[8192];
+        ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
+        data.limit(data.capacity());
+        data.position(SIGNATURE_OFFSET + SIGNATURE_SIZE);
+        while (data.hasRemaining()) {
+            int count = Math.min(buffer.length, data.remaining());
+            data.get(buffer, 0, count);
+            digest.update(buffer, 0, count);
+        }
+        return digest.digest();
+    }
+
+    /**
+     * Returns the checksum of all but the first 12 bytes of {@code dex}.
+     */
+    public int computeChecksum() throws IOException {
+        Adler32 adler32 = new Adler32();
+        byte[] buffer = new byte[8192];
+        ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
+        data.limit(data.capacity());
+        data.position(CHECKSUM_OFFSET + CHECKSUM_SIZE);
+        while (data.hasRemaining()) {
+            int count = Math.min(buffer.length, data.remaining());
+            data.get(buffer, 0, count);
+            adler32.update(buffer, 0, count);
+        }
+        return (int) adler32.getValue();
+    }
+
+    /**
+     * Generates the signature and checksum of the dex file {@code out} and
+     * writes them to the file.
+     */
+    public void writeHashes() throws IOException {
+        open(SIGNATURE_OFFSET).write(computeSignature());
+        open(CHECKSUM_OFFSET).writeInt(computeChecksum());
+    }
+
+    /**
+     * Look up a descriptor index from a type index. Cheaper than:
+     * {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();}
+     */
+    public int descriptorIndexFromTypeIndex(int typeIndex) {
+       checkBounds(typeIndex, tableOfContents.typeIds.size);
+       int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex);
+       return data.getInt(position);
+    }
+
+
+    public final class Section implements ByteInput, ByteOutput {
+        private final String name;
+        private final ByteBuffer data;
+        private final int initialPosition;
+
+        private Section(String name, ByteBuffer data) {
+            this.name = name;
+            this.data = data;
+            this.initialPosition = data.position();
+        }
+
+        public int getPosition() {
+            return data.position();
+        }
+
+        public int readInt() {
+            return data.getInt();
+        }
+
+        public short readShort() {
+            return data.getShort();
+        }
+
+        public int readUnsignedShort() {
+            return readShort() & 0xffff;
+        }
+
+        public byte readByte() {
+            return data.get();
+        }
+
+        public byte[] readByteArray(int length) {
+            byte[] result = new byte[length];
+            data.get(result);
+            return result;
+        }
+
+        public short[] readShortArray(int length) {
+            if (length == 0) {
+                return EMPTY_SHORT_ARRAY;
+            }
+            short[] result = new short[length];
+            for (int i = 0; i < length; i++) {
+                result[i] = readShort();
+            }
+            return result;
+        }
+
+        public int readUleb128() {
+            return Leb128.readUnsignedLeb128(this);
+        }
+
+        public int readUleb128p1() {
+            return Leb128.readUnsignedLeb128(this) - 1;
+        }
+
+        public int readSleb128() {
+            return Leb128.readSignedLeb128(this);
+        }
+
+        public void writeUleb128p1(int i) {
+            writeUleb128(i + 1);
+        }
+
+        public TypeList readTypeList() {
+            int size = readInt();
+            short[] types = readShortArray(size);
+            alignToFourBytes();
+            return new TypeList(Dex.this, types);
+        }
+
+        public String readString() {
+            int offset = readInt();
+            int savedPosition = data.position();
+            int savedLimit = data.limit();
+            data.position(offset);
+            data.limit(data.capacity());
+            try {
+                int expectedLength = readUleb128();
+                String result = Mutf8.decode(this, new char[expectedLength]);
+                if (result.length() != expectedLength) {
+                    throw new DexException("Declared length " + expectedLength
+                            + " doesn't match decoded length of " + result.length());
+                }
+                return result;
+            } catch (UTFDataFormatException e) {
+                throw new DexException(e);
+            } finally {
+                data.position(savedPosition);
+                data.limit(savedLimit);
+            }
+        }
+
+        public FieldId readFieldId() {
+            int declaringClassIndex = readUnsignedShort();
+            int typeIndex = readUnsignedShort();
+            int nameIndex = readInt();
+            return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex);
+        }
+
+        public MethodId readMethodId() {
+            int declaringClassIndex = readUnsignedShort();
+            int protoIndex = readUnsignedShort();
+            int nameIndex = readInt();
+            return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex);
+        }
+
+        public ProtoId readProtoId() {
+            int shortyIndex = readInt();
+            int returnTypeIndex = readInt();
+            int parametersOffset = readInt();
+            return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset);
+        }
+
+        public ClassDef readClassDef() {
+            int offset = getPosition();
+            int type = readInt();
+            int accessFlags = readInt();
+            int supertype = readInt();
+            int interfacesOffset = readInt();
+            int sourceFileIndex = readInt();
+            int annotationsOffset = readInt();
+            int classDataOffset = readInt();
+            int staticValuesOffset = readInt();
+            return new ClassDef(Dex.this, offset, type, accessFlags, supertype,
+                    interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
+                    staticValuesOffset);
+        }
+
+        private Code readCode() {
+            int registersSize = readUnsignedShort();
+            int insSize = readUnsignedShort();
+            int outsSize = readUnsignedShort();
+            int triesSize = readUnsignedShort();
+            int debugInfoOffset = readInt();
+            int instructionsSize = readInt();
+            short[] instructions = readShortArray(instructionsSize);
+            Try[] tries;
+            CatchHandler[] catchHandlers;
+            if (triesSize > 0) {
+                if (instructions.length % 2 == 1) {
+                    readShort(); // padding
+                }
+
+                /*
+                 * We can't read the tries until we've read the catch handlers.
+                 * Unfortunately they're in the opposite order in the dex file
+                 * so we need to read them out-of-order.
+                 */
+                Section triesSection = open(data.position());
+                skip(triesSize * SizeOf.TRY_ITEM);
+                catchHandlers = readCatchHandlers();
+                tries = triesSection.readTries(triesSize, catchHandlers);
+            } else {
+                tries = new Try[0];
+                catchHandlers = new CatchHandler[0];
+            }
+            return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
+                            tries, catchHandlers);
+        }
+
+        private CatchHandler[] readCatchHandlers() {
+            int baseOffset = data.position();
+            int catchHandlersSize = readUleb128();
+            CatchHandler[] result = new CatchHandler[catchHandlersSize];
+            for (int i = 0; i < catchHandlersSize; i++) {
+                int offset = data.position() - baseOffset;
+                result[i] = readCatchHandler(offset);
+            }
+            return result;
+        }
+
+        private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) {
+            Try[] result = new Try[triesSize];
+            for (int i = 0; i < triesSize; i++) {
+                int startAddress = readInt();
+                int instructionCount = readUnsignedShort();
+                int handlerOffset = readUnsignedShort();
+                int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
+                result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
+            }
+            return result;
+        }
+
+        private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) {
+            for (int i = 0; i < catchHandlers.length; i++) {
+                CatchHandler catchHandler = catchHandlers[i];
+                if (catchHandler.getOffset() == offset) {
+                    return i;
+                }
+            }
+            throw new IllegalArgumentException();
+        }
+
+        private CatchHandler readCatchHandler(int offset) {
+            int size = readSleb128();
+            int handlersCount = Math.abs(size);
+            int[] typeIndexes = new int[handlersCount];
+            int[] addresses = new int[handlersCount];
+            for (int i = 0; i < handlersCount; i++) {
+                typeIndexes[i] = readUleb128();
+                addresses[i] = readUleb128();
+            }
+            int catchAllAddress = size <= 0 ? readUleb128() : -1;
+            return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
+        }
+
+        private ClassData readClassData() {
+            int staticFieldsSize = readUleb128();
+            int instanceFieldsSize = readUleb128();
+            int directMethodsSize = readUleb128();
+            int virtualMethodsSize = readUleb128();
+            ClassData.Field[] staticFields = readFields(staticFieldsSize);
+            ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
+            ClassData.Method[] directMethods = readMethods(directMethodsSize);
+            ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
+            return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
+        }
+
+        private ClassData.Field[] readFields(int count) {
+            ClassData.Field[] result = new ClassData.Field[count];
+            int fieldIndex = 0;
+            for (int i = 0; i < count; i++) {
+                fieldIndex += readUleb128(); // field index diff
+                int accessFlags = readUleb128();
+                result[i] = new ClassData.Field(fieldIndex, accessFlags);
+            }
+            return result;
+        }
+
+        private ClassData.Method[] readMethods(int count) {
+            ClassData.Method[] result = new ClassData.Method[count];
+            int methodIndex = 0;
+            for (int i = 0; i < count; i++) {
+                methodIndex += readUleb128(); // method index diff
+                int accessFlags = readUleb128();
+                int codeOff = readUleb128();
+                result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
+            }
+            return result;
+        }
+
+        /**
+         * Returns a byte array containing the bytes from {@code start} to this
+         * section's current position.
+         */
+        private byte[] getBytesFrom(int start) {
+            int end = data.position();
+            byte[] result = new byte[end - start];
+            data.position(start);
+            data.get(result);
+            return result;
+        }
+
+        public Annotation readAnnotation() {
+            byte visibility = readByte();
+            int start = data.position();
+            new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue();
+            return new Annotation(Dex.this, visibility, new EncodedValue(getBytesFrom(start)));
+        }
+
+        public EncodedValue readEncodedArray() {
+            int start = data.position();
+            new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue();
+            return new EncodedValue(getBytesFrom(start));
+        }
+
+        public void skip(int count) {
+            if (count < 0) {
+                throw new IllegalArgumentException();
+            }
+            data.position(data.position() + count);
+        }
+
+        /**
+         * Skips bytes until the position is aligned to a multiple of 4.
+         */
+        public void alignToFourBytes() {
+            data.position((data.position() + 3) & ~3);
+        }
+
+        /**
+         * Writes 0x00 until the position is aligned to a multiple of 4.
+         */
+        public void alignToFourBytesWithZeroFill() {
+            while ((data.position() & 3) != 0) {
+                data.put((byte) 0);
+            }
+        }
+
+        public void assertFourByteAligned() {
+            if ((data.position() & 3) != 0) {
+                throw new IllegalStateException("Not four byte aligned!");
+            }
+        }
+
+        public void write(byte[] bytes) {
+            this.data.put(bytes);
+        }
+
+        public void writeByte(int b) {
+            data.put((byte) b);
+        }
+
+        public void writeShort(short i) {
+            data.putShort(i);
+        }
+
+        public void writeUnsignedShort(int i) {
+            short s = (short) i;
+            if (i != (s & 0xffff)) {
+                throw new IllegalArgumentException("Expected an unsigned short: " + i);
+            }
+            writeShort(s);
+        }
+
+        public void write(short[] shorts) {
+            for (short s : shorts) {
+                writeShort(s);
+            }
+        }
+
+        public void writeInt(int i) {
+            data.putInt(i);
+        }
+
+        public void writeUleb128(int i) {
+            try {
+                Leb128.writeUnsignedLeb128(this, i);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
+            }
+        }
+
+        public void writeSleb128(int i) {
+            try {
+                Leb128.writeSignedLeb128(this, i);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
+            }
+        }
+
+        public void writeStringData(String value) {
+            try {
+                int length = value.length();
+                writeUleb128(length);
+                write(Mutf8.encode(value));
+                writeByte(0);
+            } catch (UTFDataFormatException e) {
+                throw new AssertionError();
+            }
+        }
+
+        public void writeTypeList(TypeList typeList) {
+            short[] types = typeList.getTypes();
+            writeInt(types.length);
+            for (short type : types) {
+                writeShort(type);
+            }
+            alignToFourBytesWithZeroFill();
+        }
+
+        /**
+         * Returns the number of bytes used by this section.
+         */
+        public int used() {
+            return data.position() - initialPosition;
+        }
+    }
+
+    private final class StringTable extends AbstractList<String> implements RandomAccess {
+        @Override public String get(int index) {
+            checkBounds(index, tableOfContents.stringIds.size);
+            return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
+                    .readString();
+        }
+        @Override public int size() {
+            return tableOfContents.stringIds.size;
+        }
+    }
+
+    private final class TypeIndexToDescriptorIndexTable extends AbstractList<Integer>
+            implements RandomAccess {
+        @Override public Integer get(int index) {
+            return descriptorIndexFromTypeIndex(index);
+        }
+        @Override public int size() {
+            return tableOfContents.typeIds.size;
+        }
+    }
+
+    private final class TypeIndexToDescriptorTable extends AbstractList<String>
+            implements RandomAccess {
+        @Override public String get(int index) {
+            return strings.get(descriptorIndexFromTypeIndex(index));
+        }
+        @Override public int size() {
+            return tableOfContents.typeIds.size;
+        }
+    }
+
+    private final class ProtoIdTable extends AbstractList<ProtoId> implements RandomAccess {
+        @Override public ProtoId get(int index) {
+            checkBounds(index, tableOfContents.protoIds.size);
+            return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
+                    .readProtoId();
+        }
+        @Override public int size() {
+            return tableOfContents.protoIds.size;
+        }
+    }
+
+    private final class FieldIdTable extends AbstractList<FieldId> implements RandomAccess {
+        @Override public FieldId get(int index) {
+            checkBounds(index, tableOfContents.fieldIds.size);
+            return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
+                    .readFieldId();
+        }
+        @Override public int size() {
+            return tableOfContents.fieldIds.size;
+        }
+    }
+
+    private final class MethodIdTable extends AbstractList<MethodId> implements RandomAccess {
+        @Override public MethodId get(int index) {
+            checkBounds(index, tableOfContents.methodIds.size);
+            return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
+                    .readMethodId();
+        }
+        @Override public int size() {
+            return tableOfContents.methodIds.size;
+        }
+    }
+
+    private final class ClassDefIterator implements Iterator<ClassDef> {
+        private final Dex.Section in = open(tableOfContents.classDefs.off);
+        private int count = 0;
+
+        @Override
+        public boolean hasNext() {
+            return count < tableOfContents.classDefs.size;
+        }
+        @Override
+        public ClassDef next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+            count++;
+            return in.readClassDef();
+        }
+        @Override
+            public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    private final class ClassDefIterable implements Iterable<ClassDef> {
+        public Iterator<ClassDef> iterator() {
+            return !tableOfContents.classDefs.exists()
+               ? Collections.<ClassDef>emptySet().iterator()
+               : new ClassDefIterator();
+        }
+    }
+}
diff --git a/dx/src/com/android/dex/DexException.java b/dx/src/com/android/dex/DexException.java
new file mode 100644
index 0000000..ee0af18
--- /dev/null
+++ b/dx/src/com/android/dex/DexException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.ExceptionWithContext;
+
+/**
+ * Thrown when there's a format problem reading, writing, or generally
+ * processing a dex file.
+ */
+public class DexException extends ExceptionWithContext {
+    public DexException(String message) {
+        super(message);
+    }
+
+    public DexException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/dx/src/com/android/dex/DexFormat.java b/dx/src/com/android/dex/DexFormat.java
new file mode 100644
index 0000000..97771d8
--- /dev/null
+++ b/dx/src/com/android/dex/DexFormat.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+/**
+ * Constants that show up in and are otherwise related to {@code .dex}
+ * files, and helper methods for same.
+ */
+public final class DexFormat {
+    private DexFormat() {}
+
+    /** API level to target in order to generate invoke-polymorphic */
+    public static final int API_INVOKE_POLYMORPHIC = 26;
+
+    /** API level to target in order to pass through default and static interface methods */
+    public static final int API_DEFAULT_INTERFACE_METHODS = 24;
+
+    /** API level to target in order to suppress extended opcode usage */
+    public static final int API_NO_EXTENDED_OPCODES = 13;
+
+    /**
+     * API level to target in order to produce the most modern file
+     * format
+     */
+    public static final int API_CURRENT = API_INVOKE_POLYMORPHIC;
+
+    /** dex file version number for API level 26 and earlier */
+    public static final String VERSION_FOR_API_26 = "038";
+
+    /** dex file version number for API level 24 and earlier */
+    public static final String VERSION_FOR_API_24 = "037";
+
+    /** dex file version number for API level 13 and earlier */
+    public static final String VERSION_FOR_API_13 = "035";
+
+    /**
+     * Dex file version number for dalvik.
+     * <p>
+     * Note: Dex version 36 was loadable in some versions of Dalvik but was never fully supported or
+     * completed and is not considered a valid dex file format.
+     * </p>
+     */
+    public static final String VERSION_CURRENT = VERSION_FOR_API_26;
+
+    /**
+     * file name of the primary {@code .dex} file inside an
+     * application or library {@code .jar} file
+     */
+    public static final String DEX_IN_JAR_NAME = "classes.dex";
+
+    /** common prefix for all dex file "magic numbers" */
+    public static final String MAGIC_PREFIX = "dex\n";
+
+    /** common suffix for all dex file "magic numbers" */
+    public static final String MAGIC_SUFFIX = "\0";
+
+    /**
+     * value used to indicate endianness of file contents
+     */
+    public static final int ENDIAN_TAG = 0x12345678;
+
+    /**
+     * Maximum addressable field or method index.
+     * The largest addressable member is 0xffff, in the "instruction formats" spec as field@CCCC or
+     * meth@CCCC.
+     */
+    public static final int MAX_MEMBER_IDX = 0xFFFF;
+
+    /**
+     * Maximum addressable type index.
+     * The largest addressable type is 0xffff, in the "instruction formats" spec as type@CCCC.
+     */
+    public static final int MAX_TYPE_IDX = 0xFFFF;
+
+    /**
+     * Returns the API level corresponding to the given magic number,
+     * or {@code -1} if the given array is not a well-formed dex file
+     * magic number.
+     */
+    public static int magicToApi(byte[] magic) {
+        if (magic.length != 8) {
+            return -1;
+        }
+
+        if ((magic[0] != 'd') || (magic[1] != 'e') || (magic[2] != 'x') || (magic[3] != '\n') ||
+                (magic[7] != '\0')) {
+            return -1;
+        }
+
+        String version = "" + ((char) magic[4]) + ((char) magic[5]) +((char) magic[6]);
+
+        if (version.equals(VERSION_FOR_API_13)) {
+            return API_NO_EXTENDED_OPCODES;
+        } else if (version.equals(VERSION_FOR_API_24)) {
+            return API_DEFAULT_INTERFACE_METHODS;
+        } else if (version.equals(VERSION_FOR_API_26)) {
+            return API_INVOKE_POLYMORPHIC;
+        } else if (version.equals(VERSION_CURRENT)) {
+            return API_CURRENT;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns the magic number corresponding to the given target API level.
+     */
+    public static String apiToMagic(int targetApiLevel) {
+        String version;
+
+        if (targetApiLevel >= API_CURRENT) {
+            version = VERSION_CURRENT;
+        } else if (targetApiLevel >= API_INVOKE_POLYMORPHIC) {
+            version = VERSION_FOR_API_26;
+        } else if (targetApiLevel >= API_DEFAULT_INTERFACE_METHODS) {
+            version = VERSION_FOR_API_24;
+        } else {
+            version = VERSION_FOR_API_13;
+        }
+
+        return MAGIC_PREFIX + version + MAGIC_SUFFIX;
+    }
+
+    public static boolean isSupportedDexMagic(byte[] magic) {
+        int api = magicToApi(magic);
+        return api > 0;
+    }
+}
diff --git a/dx/src/com/android/dex/DexIndexOverflowException.java b/dx/src/com/android/dex/DexIndexOverflowException.java
new file mode 100644
index 0000000..3226207
--- /dev/null
+++ b/dx/src/com/android/dex/DexIndexOverflowException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+/**
+ * Thrown when there's an index overflow writing a dex file.
+ */
+public final class DexIndexOverflowException extends DexException {
+    public DexIndexOverflowException(String message) {
+        super(message);
+    }
+
+    public DexIndexOverflowException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/dx/src/com/android/dex/EncodedValue.java b/dx/src/com/android/dex/EncodedValue.java
new file mode 100644
index 0000000..8d0c3ad
--- /dev/null
+++ b/dx/src/com/android/dex/EncodedValue.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.ByteArrayByteInput;
+import com.android.dex.util.ByteInput;
+
+/**
+ * An encoded value or array.
+ */
+public final class EncodedValue implements Comparable<EncodedValue> {
+    private final byte[] data;
+
+    public EncodedValue(byte[] data) {
+        this.data = data;
+    }
+
+    public ByteInput asByteInput() {
+        return new ByteArrayByteInput(data);
+    }
+
+    public byte[] getBytes() {
+        return data;
+    }
+
+    public void writeTo(Dex.Section out) {
+        out.write(data);
+    }
+
+    @Override public int compareTo(EncodedValue other) {
+        int size = Math.min(data.length, other.data.length);
+        for (int i = 0; i < size; i++) {
+            if (data[i] != other.data[i]) {
+                return (data[i] & 0xff) - (other.data[i] & 0xff);
+            }
+        }
+        return data.length - other.data.length;
+    }
+
+    @Override public String toString() {
+        return Integer.toHexString(data[0] & 0xff) + "...(" + data.length + ")";
+    }
+}
diff --git a/dx/src/com/android/dex/EncodedValueCodec.java b/dx/src/com/android/dex/EncodedValueCodec.java
new file mode 100644
index 0000000..7fc1724
--- /dev/null
+++ b/dx/src/com/android/dex/EncodedValueCodec.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.ByteInput;
+import com.android.dex.util.ByteOutput;
+
+/**
+ * Read and write {@code encoded_value} primitives.
+ */
+public final class EncodedValueCodec {
+    private EncodedValueCodec() {
+    }
+
+    /**
+     * Writes a signed integral to {@code out}.
+     */
+    public static void writeSignedIntegralValue(ByteOutput out, int type, long value) {
+        /*
+         * Figure out how many bits are needed to represent the value,
+         * including a sign bit: The bit count is subtracted from 65
+         * and not 64 to account for the sign bit. The xor operation
+         * has the effect of leaving non-negative values alone and
+         * unary complementing negative values (so that a leading zero
+         * count always returns a useful number for our present
+         * purpose).
+         */
+        int requiredBits = 65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
+
+        // Round up the requiredBits to a number of bytes.
+        int requiredBytes = (requiredBits + 0x07) >> 3;
+
+        /*
+         * Write the header byte, which includes the type and
+         * requiredBytes - 1.
+         */
+        out.writeByte(type | ((requiredBytes - 1) << 5));
+
+        // Write the value, per se.
+        while (requiredBytes > 0) {
+            out.writeByte((byte) value);
+            value >>= 8;
+            requiredBytes--;
+        }
+    }
+
+    /**
+     * Writes an unsigned integral to {@code out}.
+     */
+    public static void writeUnsignedIntegralValue(ByteOutput out, int type, long value) {
+        // Figure out how many bits are needed to represent the value.
+        int requiredBits = 64 - Long.numberOfLeadingZeros(value);
+        if (requiredBits == 0) {
+            requiredBits = 1;
+        }
+
+        // Round up the requiredBits to a number of bytes.
+        int requiredBytes = (requiredBits + 0x07) >> 3;
+
+        /*
+         * Write the header byte, which includes the type and
+         * requiredBytes - 1.
+         */
+        out.writeByte(type | ((requiredBytes - 1) << 5));
+
+        // Write the value, per se.
+        while (requiredBytes > 0) {
+            out.writeByte((byte) value);
+            value >>= 8;
+            requiredBytes--;
+        }
+    }
+
+    /**
+     * Writes a right-zero-extended value to {@code out}.
+     */
+    public static void writeRightZeroExtendedValue(ByteOutput out, int type, long value) {
+        // Figure out how many bits are needed to represent the value.
+        int requiredBits = 64 - Long.numberOfTrailingZeros(value);
+        if (requiredBits == 0) {
+            requiredBits = 1;
+        }
+
+        // Round up the requiredBits to a number of bytes.
+        int requiredBytes = (requiredBits + 0x07) >> 3;
+
+        // Scootch the first bits to be written down to the low-order bits.
+        value >>= 64 - (requiredBytes * 8);
+
+        /*
+         * Write the header byte, which includes the type and
+         * requiredBytes - 1.
+         */
+        out.writeByte(type | ((requiredBytes - 1) << 5));
+
+        // Write the value, per se.
+        while (requiredBytes > 0) {
+            out.writeByte((byte) value);
+            value >>= 8;
+            requiredBytes--;
+        }
+    }
+
+    /**
+     * Read a signed integer.
+     *
+     * @param zwidth byte count minus one
+     */
+    public static int readSignedInt(ByteInput in, int zwidth) {
+        int result = 0;
+        for (int i = zwidth; i >= 0; i--) {
+            result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
+        }
+        result >>= (3 - zwidth) * 8;
+        return result;
+    }
+
+    /**
+     * Read an unsigned integer.
+     *
+     * @param zwidth byte count minus one
+     * @param fillOnRight true to zero fill on the right; false on the left
+     */
+    public static int readUnsignedInt(ByteInput in, int zwidth, boolean fillOnRight) {
+        int result = 0;
+        if (!fillOnRight) {
+            for (int i = zwidth; i >= 0; i--) {
+                result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
+            }
+            result >>>= (3 - zwidth) * 8;
+        } else {
+            for (int i = zwidth; i >= 0; i--) {
+                result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Read a signed long.
+     *
+     * @param zwidth byte count minus one
+     */
+    public static long readSignedLong(ByteInput in, int zwidth) {
+        long result = 0;
+        for (int i = zwidth; i >= 0; i--) {
+            result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
+        }
+        result >>= (7 - zwidth) * 8;
+        return result;
+    }
+
+    /**
+     * Read an unsigned long.
+     *
+     * @param zwidth byte count minus one
+     * @param fillOnRight true to zero fill on the right; false on the left
+     */
+    public static long readUnsignedLong(ByteInput in, int zwidth, boolean fillOnRight) {
+        long result = 0;
+        if (!fillOnRight) {
+            for (int i = zwidth; i >= 0; i--) {
+                result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
+            }
+            result >>>= (7 - zwidth) * 8;
+        } else {
+            for (int i = zwidth; i >= 0; i--) {
+                result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
+            }
+        }
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dex/EncodedValueReader.java b/dx/src/com/android/dex/EncodedValueReader.java
new file mode 100644
index 0000000..6f60538
--- /dev/null
+++ b/dx/src/com/android/dex/EncodedValueReader.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.ByteInput;
+
+/**
+ * Pull parser for encoded values.
+ */
+public final class EncodedValueReader {
+    public static final int ENCODED_BYTE = 0x00;
+    public static final int ENCODED_SHORT = 0x02;
+    public static final int ENCODED_CHAR = 0x03;
+    public static final int ENCODED_INT = 0x04;
+    public static final int ENCODED_LONG = 0x06;
+    public static final int ENCODED_FLOAT = 0x10;
+    public static final int ENCODED_DOUBLE = 0x11;
+    public static final int ENCODED_STRING = 0x17;
+    public static final int ENCODED_TYPE = 0x18;
+    public static final int ENCODED_FIELD = 0x19;
+    public static final int ENCODED_ENUM = 0x1b;
+    public static final int ENCODED_METHOD = 0x1a;
+    public static final int ENCODED_ARRAY = 0x1c;
+    public static final int ENCODED_ANNOTATION = 0x1d;
+    public static final int ENCODED_NULL = 0x1e;
+    public static final int ENCODED_BOOLEAN = 0x1f;
+
+    /** placeholder type if the type is not yet known */
+    private static final int MUST_READ = -1;
+
+    protected final ByteInput in;
+    private int type = MUST_READ;
+    private int annotationType;
+    private int arg;
+
+    public EncodedValueReader(ByteInput in) {
+        this.in = in;
+    }
+
+    public EncodedValueReader(EncodedValue in) {
+        this(in.asByteInput());
+    }
+
+    /**
+     * Creates a new encoded value reader whose only value is the specified
+     * known type. This is useful for encoded values without a type prefix,
+     * such as class_def_item's encoded_array or annotation_item's
+     * encoded_annotation.
+     */
+    public EncodedValueReader(ByteInput in, int knownType) {
+        this.in = in;
+        this.type = knownType;
+    }
+
+    public EncodedValueReader(EncodedValue in, int knownType) {
+        this(in.asByteInput(), knownType);
+    }
+
+    /**
+     * Returns the type of the next value to read.
+     */
+    public int peek() {
+        if (type == MUST_READ) {
+            int argAndType = in.readByte() & 0xff;
+            type = argAndType & 0x1f;
+            arg = (argAndType & 0xe0) >> 5;
+        }
+        return type;
+    }
+
+    /**
+     * Begins reading the elements of an array, returning the array's size. The
+     * caller must follow up by calling a read method for each element in the
+     * array. For example, this reads a byte array: <pre>   {@code
+     *   int arraySize = readArray();
+     *   for (int i = 0, i < arraySize; i++) {
+     *     readByte();
+     *   }
+     * }</pre>
+     */
+    public int readArray() {
+        checkType(ENCODED_ARRAY);
+        type = MUST_READ;
+        return Leb128.readUnsignedLeb128(in);
+    }
+
+    /**
+     * Begins reading the fields of an annotation, returning the number of
+     * fields. The caller must follow up by making alternating calls to {@link
+     * #readAnnotationName()} and another read method. For example, this reads
+     * an annotation whose fields are all bytes: <pre>   {@code
+     *   int fieldCount = readAnnotation();
+     *   int annotationType = getAnnotationType();
+     *   for (int i = 0; i < fieldCount; i++) {
+     *       readAnnotationName();
+     *       readByte();
+     *   }
+     * }</pre>
+     */
+    public int readAnnotation() {
+        checkType(ENCODED_ANNOTATION);
+        type = MUST_READ;
+        annotationType = Leb128.readUnsignedLeb128(in);
+        return Leb128.readUnsignedLeb128(in);
+    }
+
+    /**
+     * Returns the type of the annotation just returned by {@link
+     * #readAnnotation()}. This method's value is undefined unless the most
+     * recent call was to {@link #readAnnotation()}.
+     */
+    public int getAnnotationType() {
+        return annotationType;
+    }
+
+    public int readAnnotationName() {
+        return Leb128.readUnsignedLeb128(in);
+    }
+
+    public byte readByte() {
+        checkType(ENCODED_BYTE);
+        type = MUST_READ;
+        return (byte) EncodedValueCodec.readSignedInt(in, arg);
+    }
+
+    public short readShort() {
+        checkType(ENCODED_SHORT);
+        type = MUST_READ;
+        return (short) EncodedValueCodec.readSignedInt(in, arg);
+    }
+
+    public char readChar() {
+        checkType(ENCODED_CHAR);
+        type = MUST_READ;
+        return (char) EncodedValueCodec.readUnsignedInt(in, arg, false);
+    }
+
+    public int readInt() {
+        checkType(ENCODED_INT);
+        type = MUST_READ;
+        return EncodedValueCodec.readSignedInt(in, arg);
+    }
+
+    public long readLong() {
+        checkType(ENCODED_LONG);
+        type = MUST_READ;
+        return EncodedValueCodec.readSignedLong(in, arg);
+    }
+
+    public float readFloat() {
+        checkType(ENCODED_FLOAT);
+        type = MUST_READ;
+        return Float.intBitsToFloat(EncodedValueCodec.readUnsignedInt(in, arg, true));
+    }
+
+    public double readDouble() {
+        checkType(ENCODED_DOUBLE);
+        type = MUST_READ;
+        return Double.longBitsToDouble(EncodedValueCodec.readUnsignedLong(in, arg, true));
+    }
+
+    public int readString() {
+        checkType(ENCODED_STRING);
+        type = MUST_READ;
+        return EncodedValueCodec.readUnsignedInt(in, arg, false);
+    }
+
+    public int readType() {
+        checkType(ENCODED_TYPE);
+        type = MUST_READ;
+        return EncodedValueCodec.readUnsignedInt(in, arg, false);
+    }
+
+    public int readField() {
+        checkType(ENCODED_FIELD);
+        type = MUST_READ;
+        return EncodedValueCodec.readUnsignedInt(in, arg, false);
+    }
+
+    public int readEnum() {
+        checkType(ENCODED_ENUM);
+        type = MUST_READ;
+        return EncodedValueCodec.readUnsignedInt(in, arg, false);
+    }
+
+    public int readMethod() {
+        checkType(ENCODED_METHOD);
+        type = MUST_READ;
+        return EncodedValueCodec.readUnsignedInt(in, arg, false);
+    }
+
+    public void readNull() {
+        checkType(ENCODED_NULL);
+        type = MUST_READ;
+    }
+
+    public boolean readBoolean() {
+        checkType(ENCODED_BOOLEAN);
+        type = MUST_READ;
+        return arg != 0;
+    }
+
+    /**
+     * Skips a single value, including its nested values if it is an array or
+     * annotation.
+     */
+    public void skipValue() {
+        switch (peek()) {
+        case ENCODED_BYTE:
+            readByte();
+            break;
+        case ENCODED_SHORT:
+            readShort();
+            break;
+        case ENCODED_CHAR:
+            readChar();
+            break;
+        case ENCODED_INT:
+            readInt();
+            break;
+        case ENCODED_LONG:
+            readLong();
+            break;
+        case ENCODED_FLOAT:
+            readFloat();
+            break;
+        case ENCODED_DOUBLE:
+            readDouble();
+            break;
+        case ENCODED_STRING:
+            readString();
+            break;
+        case ENCODED_TYPE:
+            readType();
+            break;
+        case ENCODED_FIELD:
+            readField();
+            break;
+        case ENCODED_ENUM:
+            readEnum();
+            break;
+        case ENCODED_METHOD:
+            readMethod();
+            break;
+        case ENCODED_ARRAY:
+            for (int i = 0, size = readArray(); i < size; i++) {
+                skipValue();
+            }
+            break;
+        case ENCODED_ANNOTATION:
+            for (int i = 0, size = readAnnotation(); i < size; i++) {
+                readAnnotationName();
+                skipValue();
+            }
+            break;
+        case ENCODED_NULL:
+            readNull();
+            break;
+        case ENCODED_BOOLEAN:
+            readBoolean();
+            break;
+        default:
+            throw new DexException("Unexpected type: " + Integer.toHexString(type));
+        }
+    }
+
+    private void checkType(int expected) {
+        if (peek() != expected) {
+            throw new IllegalStateException(
+                    String.format("Expected %x but was %x", expected, peek()));
+        }
+    }
+}
diff --git a/dx/src/com/android/dex/FieldId.java b/dx/src/com/android/dex/FieldId.java
new file mode 100644
index 0000000..2f41708
--- /dev/null
+++ b/dx/src/com/android/dex/FieldId.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.Unsigned;
+
+public final class FieldId implements Comparable<FieldId> {
+    private final Dex dex;
+    private final int declaringClassIndex;
+    private final int typeIndex;
+    private final int nameIndex;
+
+    public FieldId(Dex dex, int declaringClassIndex, int typeIndex, int nameIndex) {
+        this.dex = dex;
+        this.declaringClassIndex = declaringClassIndex;
+        this.typeIndex = typeIndex;
+        this.nameIndex = nameIndex;
+    }
+
+    public int getDeclaringClassIndex() {
+        return declaringClassIndex;
+    }
+
+    public int getTypeIndex() {
+        return typeIndex;
+    }
+
+    public int getNameIndex() {
+        return nameIndex;
+    }
+
+    public int compareTo(FieldId other) {
+        if (declaringClassIndex != other.declaringClassIndex) {
+            return Unsigned.compare(declaringClassIndex, other.declaringClassIndex);
+        }
+        if (nameIndex != other.nameIndex) {
+            return Unsigned.compare(nameIndex, other.nameIndex);
+        }
+        return Unsigned.compare(typeIndex, other.typeIndex); // should always be 0
+    }
+
+    public void writeTo(Dex.Section out) {
+        out.writeUnsignedShort(declaringClassIndex);
+        out.writeUnsignedShort(typeIndex);
+        out.writeInt(nameIndex);
+    }
+
+    @Override public String toString() {
+        if (dex == null) {
+            return declaringClassIndex + " " + typeIndex + " " + nameIndex;
+        }
+        return dex.typeNames().get(typeIndex) + "." + dex.strings().get(nameIndex);
+    }
+}
diff --git a/dx/src/com/android/dex/Leb128.java b/dx/src/com/android/dex/Leb128.java
new file mode 100644
index 0000000..e4ca500
--- /dev/null
+++ b/dx/src/com/android/dex/Leb128.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.ByteInput;
+import com.android.dex.util.ByteOutput;
+
+/**
+ * Reads and writes DWARFv3 LEB 128 signed and unsigned integers. See DWARF v3
+ * section 7.6.
+ */
+public final class Leb128 {
+    private Leb128() {
+    }
+
+    /**
+     * Gets the number of bytes in the unsigned LEB128 encoding of the
+     * given value.
+     *
+     * @param value the value in question
+     * @return its write size, in bytes
+     */
+    public static int unsignedLeb128Size(int value) {
+        // TODO: This could be much cleverer.
+
+        int remaining = value >> 7;
+        int count = 0;
+
+        while (remaining != 0) {
+            remaining >>= 7;
+            count++;
+        }
+
+        return count + 1;
+    }
+
+    /**
+     * Reads an signed integer from {@code in}.
+     */
+    public static int readSignedLeb128(ByteInput in) {
+        int result = 0;
+        int cur;
+        int count = 0;
+        int signBits = -1;
+
+        do {
+            cur = in.readByte() & 0xff;
+            result |= (cur & 0x7f) << (count * 7);
+            signBits <<= 7;
+            count++;
+        } while (((cur & 0x80) == 0x80) && count < 5);
+
+        if ((cur & 0x80) == 0x80) {
+            throw new DexException("invalid LEB128 sequence");
+        }
+
+        // Sign extend if appropriate
+        if (((signBits >> 1) & result) != 0 ) {
+            result |= signBits;
+        }
+
+        return result;
+    }
+
+    /**
+     * Reads an unsigned integer from {@code in}.
+     */
+    public static int readUnsignedLeb128(ByteInput in) {
+        int result = 0;
+        int cur;
+        int count = 0;
+
+        do {
+            cur = in.readByte() & 0xff;
+            result |= (cur & 0x7f) << (count * 7);
+            count++;
+        } while (((cur & 0x80) == 0x80) && count < 5);
+
+        if ((cur & 0x80) == 0x80) {
+            throw new DexException("invalid LEB128 sequence");
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes {@code value} as an unsigned integer to {@code out}, starting at
+     * {@code offset}. Returns the number of bytes written.
+     */
+    public static void writeUnsignedLeb128(ByteOutput out, int value) {
+        int remaining = value >>> 7;
+
+        while (remaining != 0) {
+            out.writeByte((byte) ((value & 0x7f) | 0x80));
+            value = remaining;
+            remaining >>>= 7;
+        }
+
+        out.writeByte((byte) (value & 0x7f));
+    }
+
+    /**
+     * Writes {@code value} as a signed integer to {@code out}, starting at
+     * {@code offset}. Returns the number of bytes written.
+     */
+    public static void writeSignedLeb128(ByteOutput out, int value) {
+        int remaining = value >> 7;
+        boolean hasMore = true;
+        int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+        while (hasMore) {
+            hasMore = (remaining != end)
+                    || ((remaining & 1) != ((value >> 6) & 1));
+
+            out.writeByte((byte) ((value & 0x7f) | (hasMore ? 0x80 : 0)));
+            value = remaining;
+            remaining >>= 7;
+        }
+    }
+}
diff --git a/dx/src/com/android/dex/MethodId.java b/dx/src/com/android/dex/MethodId.java
new file mode 100644
index 0000000..e518740
--- /dev/null
+++ b/dx/src/com/android/dex/MethodId.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.Unsigned;
+
+public final class MethodId implements Comparable<MethodId> {
+    private final Dex dex;
+    private final int declaringClassIndex;
+    private final int protoIndex;
+    private final int nameIndex;
+
+    public MethodId(Dex dex, int declaringClassIndex, int protoIndex, int nameIndex) {
+        this.dex = dex;
+        this.declaringClassIndex = declaringClassIndex;
+        this.protoIndex = protoIndex;
+        this.nameIndex = nameIndex;
+    }
+
+    public int getDeclaringClassIndex() {
+        return declaringClassIndex;
+    }
+
+    public int getProtoIndex() {
+        return protoIndex;
+    }
+
+    public int getNameIndex() {
+        return nameIndex;
+    }
+
+    public int compareTo(MethodId other) {
+        if (declaringClassIndex != other.declaringClassIndex) {
+            return Unsigned.compare(declaringClassIndex, other.declaringClassIndex);
+        }
+        if (nameIndex != other.nameIndex) {
+            return Unsigned.compare(nameIndex, other.nameIndex);
+        }
+        return Unsigned.compare(protoIndex, other.protoIndex);
+    }
+
+    public void writeTo(Dex.Section out) {
+        out.writeUnsignedShort(declaringClassIndex);
+        out.writeUnsignedShort(protoIndex);
+        out.writeInt(nameIndex);
+    }
+
+    @Override public String toString() {
+        if (dex == null) {
+            return declaringClassIndex + " " + protoIndex + " " + nameIndex;
+        }
+        return dex.typeNames().get(declaringClassIndex)
+                + "." + dex.strings().get(nameIndex)
+                + dex.readTypeList(dex.protoIds().get(protoIndex).getParametersOffset());
+    }
+}
diff --git a/dx/src/com/android/dex/Mutf8.java b/dx/src/com/android/dex/Mutf8.java
new file mode 100644
index 0000000..c64da33
--- /dev/null
+++ b/dx/src/com/android/dex/Mutf8.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.ByteInput;
+import java.io.UTFDataFormatException;
+
+/**
+ * Modified UTF-8 as described in the dex file format spec.
+ *
+ * <p>Derived from libcore's MUTF-8 encoder at java.nio.charset.ModifiedUtf8.
+ */
+public final class Mutf8 {
+    private Mutf8() {}
+
+    /**
+     * Decodes bytes from {@code in} into {@code out} until a delimiter 0x00 is
+     * encountered. Returns a new string containing the decoded characters.
+     */
+    public static String decode(ByteInput in, char[] out) throws UTFDataFormatException {
+        int s = 0;
+        while (true) {
+            char a = (char) (in.readByte() & 0xff);
+            if (a == 0) {
+                return new String(out, 0, s);
+            }
+            out[s] = a;
+            if (a < '\u0080') {
+                s++;
+            } else if ((a & 0xe0) == 0xc0) {
+                int b = in.readByte() & 0xff;
+                if ((b & 0xC0) != 0x80) {
+                    throw new UTFDataFormatException("bad second byte");
+                }
+                out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
+            } else if ((a & 0xf0) == 0xe0) {
+                int b = in.readByte() & 0xff;
+                int c = in.readByte() & 0xff;
+                if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
+                    throw new UTFDataFormatException("bad second or third byte");
+                }
+                out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
+            } else {
+                throw new UTFDataFormatException("bad byte");
+            }
+        }
+    }
+
+    /**
+     * Returns the number of bytes the modified UTF8 representation of 's' would take.
+     */
+    private static long countBytes(String s, boolean shortLength) throws UTFDataFormatException {
+        long result = 0;
+        final int length = s.length();
+        for (int i = 0; i < length; ++i) {
+            char ch = s.charAt(i);
+            if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
+                ++result;
+            } else if (ch <= 2047) {
+                result += 2;
+            } else {
+                result += 3;
+            }
+            if (shortLength && result > 65535) {
+                throw new UTFDataFormatException("String more than 65535 UTF bytes long");
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Encodes the modified UTF-8 bytes corresponding to {@code s} into  {@code
+     * dst}, starting at {@code offset}.
+     */
+    public static void encode(byte[] dst, int offset, String s) {
+        final int length = s.length();
+        for (int i = 0; i < length; i++) {
+            char ch = s.charAt(i);
+            if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
+                dst[offset++] = (byte) ch;
+            } else if (ch <= 2047) {
+                dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6)));
+                dst[offset++] = (byte) (0x80 | (0x3f & ch));
+            } else {
+                dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12)));
+                dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6)));
+                dst[offset++] = (byte) (0x80 | (0x3f & ch));
+            }
+        }
+    }
+
+    /**
+     * Returns an array containing the <i>modified UTF-8</i> form of {@code s}.
+     */
+    public static byte[] encode(String s) throws UTFDataFormatException {
+        int utfCount = (int) countBytes(s, true);
+        byte[] result = new byte[utfCount];
+        encode(result, 0, s);
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dex/ProtoId.java b/dx/src/com/android/dex/ProtoId.java
new file mode 100644
index 0000000..9d9f484
--- /dev/null
+++ b/dx/src/com/android/dex/ProtoId.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.Unsigned;
+
+public final class ProtoId implements Comparable<ProtoId> {
+    private final Dex dex;
+    private final int shortyIndex;
+    private final int returnTypeIndex;
+    private final int parametersOffset;
+
+    public ProtoId(Dex dex, int shortyIndex, int returnTypeIndex, int parametersOffset) {
+        this.dex = dex;
+        this.shortyIndex = shortyIndex;
+        this.returnTypeIndex = returnTypeIndex;
+        this.parametersOffset = parametersOffset;
+    }
+
+    public int compareTo(ProtoId other) {
+        if (returnTypeIndex != other.returnTypeIndex) {
+            return Unsigned.compare(returnTypeIndex, other.returnTypeIndex);
+        }
+        return Unsigned.compare(parametersOffset, other.parametersOffset);
+    }
+
+    public int getShortyIndex() {
+        return shortyIndex;
+    }
+
+    public int getReturnTypeIndex() {
+        return returnTypeIndex;
+    }
+
+    public int getParametersOffset() {
+        return parametersOffset;
+    }
+
+    public void writeTo(Dex.Section out) {
+        out.writeInt(shortyIndex);
+        out.writeInt(returnTypeIndex);
+        out.writeInt(parametersOffset);
+    }
+
+    @Override public String toString() {
+        if (dex == null) {
+            return shortyIndex + " " + returnTypeIndex + " " + parametersOffset;
+        }
+
+        return dex.strings().get(shortyIndex)
+                + ": " + dex.typeNames().get(returnTypeIndex)
+                + " " + dex.readTypeList(parametersOffset);
+    }
+}
diff --git a/dx/src/com/android/dex/SizeOf.java b/dx/src/com/android/dex/SizeOf.java
new file mode 100644
index 0000000..65fab56
--- /dev/null
+++ b/dx/src/com/android/dex/SizeOf.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+public final class SizeOf {
+    private SizeOf() {}
+
+    public static final int UBYTE = 1;
+    public static final int USHORT = 2;
+    public static final int UINT = 4;
+
+    public static final int SIGNATURE = UBYTE * 20;
+
+    /**
+     * magic ubyte[8]
+     * checksum uint
+     * signature ubyte[20]
+     * file_size uint
+     * header_size uint
+     * endian_tag uint
+     * link_size uint
+     * link_off uint
+     * map_off uint
+     * string_ids_size uint
+     * string_ids_off uint
+     * type_ids_size uint
+     * type_ids_off uint
+     * proto_ids_size uint
+     * proto_ids_off uint
+     * field_ids_size uint
+     * field_ids_off uint
+     * method_ids_size uint
+     * method_ids_off uint
+     * class_defs_size uint
+     * class_defs_off uint
+     * data_size uint
+     * data_off uint
+     */
+    public static final int HEADER_ITEM = (8 * UBYTE) + UINT + SIGNATURE + (20 * UINT); // 0x70
+
+    /**
+     * string_data_off uint
+     */
+    public static final int STRING_ID_ITEM = UINT;
+
+    /**
+     * descriptor_idx uint
+     */
+    public static final int TYPE_ID_ITEM = UINT;
+
+    /**
+     * type_idx ushort
+     */
+    public static final int TYPE_ITEM = USHORT;
+
+    /**
+     * shorty_idx uint
+     * return_type_idx uint
+     * return_type_idx uint
+     */
+    public static final int PROTO_ID_ITEM = UINT + UINT + UINT;
+
+    /**
+     * class_idx ushort
+     * type_idx/proto_idx ushort
+     * name_idx uint
+     */
+    public static final int MEMBER_ID_ITEM = USHORT + USHORT + UINT;
+
+    /**
+     * class_idx uint
+     * access_flags uint
+     * superclass_idx uint
+     * interfaces_off uint
+     * source_file_idx uint
+     * annotations_off uint
+     * class_data_off uint
+     * static_values_off uint
+     */
+    public static final int CLASS_DEF_ITEM = 8 * UINT;
+
+    /**
+     * type ushort
+     * unused ushort
+     * size uint
+     * offset uint
+     */
+    public static final int MAP_ITEM = USHORT + USHORT + UINT + UINT;
+
+    /**
+     * start_addr uint
+     * insn_count ushort
+     * handler_off ushort
+     */
+    public static final int TRY_ITEM = UINT + USHORT + USHORT;
+}
diff --git a/dx/src/com/android/dex/TableOfContents.java b/dx/src/com/android/dex/TableOfContents.java
new file mode 100644
index 0000000..b33a749
--- /dev/null
+++ b/dx/src/com/android/dex/TableOfContents.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+/**
+ * The file header and map.
+ */
+public final class TableOfContents {
+
+    /*
+     * TODO: factor out ID constants.
+     */
+
+    public final Section header = new Section(0x0000);
+    public final Section stringIds = new Section(0x0001);
+    public final Section typeIds = new Section(0x0002);
+    public final Section protoIds = new Section(0x0003);
+    public final Section fieldIds = new Section(0x0004);
+    public final Section methodIds = new Section(0x0005);
+    public final Section classDefs = new Section(0x0006);
+    public final Section callSiteIds = new Section(0x0007);
+    public final Section methodHandles = new Section(0x0008);
+    public final Section mapList = new Section(0x1000);
+    public final Section typeLists = new Section(0x1001);
+    public final Section annotationSetRefLists = new Section(0x1002);
+    public final Section annotationSets = new Section(0x1003);
+    public final Section classDatas = new Section(0x2000);
+    public final Section codes = new Section(0x2001);
+    public final Section stringDatas = new Section(0x2002);
+    public final Section debugInfos = new Section(0x2003);
+    public final Section annotations = new Section(0x2004);
+    public final Section encodedArrays = new Section(0x2005);
+    public final Section annotationsDirectories = new Section(0x2006);
+    public final Section[] sections = {
+        header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, callSiteIds,
+        methodHandles, typeLists, annotationSetRefLists, annotationSets, classDatas, codes,
+        stringDatas, debugInfos, annotations, encodedArrays, annotationsDirectories
+    };
+
+    public int apiLevel;
+    public int checksum;
+    public byte[] signature;
+    public int fileSize;
+    public int linkSize;
+    public int linkOff;
+    public int dataSize;
+    public int dataOff;
+
+    public TableOfContents() {
+        signature = new byte[20];
+    }
+
+    public void readFrom(Dex dex) throws IOException {
+        readHeader(dex.open(0));
+        readMap(dex.open(mapList.off));
+        computeSizesFromOffsets();
+    }
+
+    private void readHeader(Dex.Section headerIn) throws UnsupportedEncodingException {
+        byte[] magic = headerIn.readByteArray(8);
+
+        if (!DexFormat.isSupportedDexMagic(magic)) {
+            String msg =
+                    String.format("Unexpected magic: [0x%02x, 0x%02x, 0x%02x, 0x%02x, "
+                                  + "0x%02x, 0x%02x, 0x%02x, 0x%02x]",
+                                  magic[0], magic[1], magic[2], magic[3],
+                                  magic[4], magic[5], magic[6], magic[7]);
+            throw new DexException(msg);
+        }
+
+        apiLevel = DexFormat.magicToApi(magic);
+        checksum = headerIn.readInt();
+        signature = headerIn.readByteArray(20);
+        fileSize = headerIn.readInt();
+        int headerSize = headerIn.readInt();
+        if (headerSize != SizeOf.HEADER_ITEM) {
+            throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize));
+        }
+        int endianTag = headerIn.readInt();
+        if (endianTag != DexFormat.ENDIAN_TAG) {
+            throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag));
+        }
+        linkSize = headerIn.readInt();
+        linkOff = headerIn.readInt();
+        mapList.off = headerIn.readInt();
+        if (mapList.off == 0) {
+            throw new DexException("Cannot merge dex files that do not contain a map");
+        }
+        stringIds.size = headerIn.readInt();
+        stringIds.off = headerIn.readInt();
+        typeIds.size = headerIn.readInt();
+        typeIds.off = headerIn.readInt();
+        protoIds.size = headerIn.readInt();
+        protoIds.off = headerIn.readInt();
+        fieldIds.size = headerIn.readInt();
+        fieldIds.off = headerIn.readInt();
+        methodIds.size = headerIn.readInt();
+        methodIds.off = headerIn.readInt();
+        classDefs.size = headerIn.readInt();
+        classDefs.off = headerIn.readInt();
+        dataSize = headerIn.readInt();
+        dataOff = headerIn.readInt();
+    }
+
+    private void readMap(Dex.Section in) throws IOException {
+        int mapSize = in.readInt();
+        Section previous = null;
+        for (int i = 0; i < mapSize; i++) {
+            short type = in.readShort();
+            in.readShort(); // unused
+            Section section = getSection(type);
+            int size = in.readInt();
+            int offset = in.readInt();
+
+            if ((section.size != 0 && section.size != size)
+                    || (section.off != -1 && section.off != offset)) {
+                throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type));
+            }
+
+            section.size = size;
+            section.off = offset;
+
+            if (previous != null && previous.off > section.off) {
+                throw new DexException("Map is unsorted at " + previous + ", " + section);
+            }
+
+            previous = section;
+        }
+        Arrays.sort(sections);
+    }
+
+    public void computeSizesFromOffsets() {
+        int end = dataOff + dataSize;
+        for (int i = sections.length - 1; i >= 0; i--) {
+            Section section = sections[i];
+            if (section.off == -1) {
+                continue;
+            }
+            if (section.off > end) {
+                throw new DexException("Map is unsorted at " + section);
+            }
+            section.byteCount = end - section.off;
+            end = section.off;
+        }
+    }
+
+    private Section getSection(short type) {
+        for (Section section : sections) {
+            if (section.type == type) {
+                return section;
+            }
+        }
+        throw new IllegalArgumentException("No such map item: " + type);
+    }
+
+    public void writeHeader(Dex.Section out, int api) throws IOException {
+        out.write(DexFormat.apiToMagic(api).getBytes("UTF-8"));
+        out.writeInt(checksum);
+        out.write(signature);
+        out.writeInt(fileSize);
+        out.writeInt(SizeOf.HEADER_ITEM);
+        out.writeInt(DexFormat.ENDIAN_TAG);
+        out.writeInt(linkSize);
+        out.writeInt(linkOff);
+        out.writeInt(mapList.off);
+        out.writeInt(stringIds.size);
+        out.writeInt(stringIds.off);
+        out.writeInt(typeIds.size);
+        out.writeInt(typeIds.off);
+        out.writeInt(protoIds.size);
+        out.writeInt(protoIds.off);
+        out.writeInt(fieldIds.size);
+        out.writeInt(fieldIds.off);
+        out.writeInt(methodIds.size);
+        out.writeInt(methodIds.off);
+        out.writeInt(classDefs.size);
+        out.writeInt(classDefs.off);
+        out.writeInt(dataSize);
+        out.writeInt(dataOff);
+    }
+
+    public void writeMap(Dex.Section out) throws IOException {
+        int count = 0;
+        for (Section section : sections) {
+            if (section.exists()) {
+                count++;
+            }
+        }
+
+        out.writeInt(count);
+        for (Section section : sections) {
+            if (section.exists()) {
+                out.writeShort(section.type);
+                out.writeShort((short) 0);
+                out.writeInt(section.size);
+                out.writeInt(section.off);
+            }
+        }
+    }
+
+    public static class Section implements Comparable<Section> {
+        public final short type;
+        public int size = 0;
+        public int off = -1;
+        public int byteCount = 0;
+
+        public Section(int type) {
+            this.type = (short) type;
+        }
+
+        public boolean exists() {
+            return size > 0;
+        }
+
+        public int compareTo(Section section) {
+            if (off != section.off) {
+                return off < section.off ? -1 : 1;
+            }
+            return 0;
+        }
+
+        @Override public String toString() {
+            return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size);
+        }
+    }
+}
diff --git a/dx/src/com/android/dex/TypeList.java b/dx/src/com/android/dex/TypeList.java
new file mode 100644
index 0000000..123e82c
--- /dev/null
+++ b/dx/src/com/android/dex/TypeList.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex;
+
+import com.android.dex.util.Unsigned;
+
+public final class TypeList implements Comparable<TypeList> {
+
+    public static final TypeList EMPTY = new TypeList(null, Dex.EMPTY_SHORT_ARRAY);
+
+    private final Dex dex;
+    private final short[] types;
+
+    public TypeList(Dex dex, short[] types) {
+        this.dex = dex;
+        this.types = types;
+    }
+
+    public short[] getTypes() {
+        return types;
+    }
+
+    @Override public int compareTo(TypeList other) {
+        for (int i = 0; i < types.length && i < other.types.length; i++) {
+            if (types[i] != other.types[i]) {
+                return Unsigned.compare(types[i], other.types[i]);
+            }
+        }
+        return Unsigned.compare(types.length, other.types.length);
+    }
+
+    @Override public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append("(");
+        for (int i = 0, typesLength = types.length; i < typesLength; i++) {
+            result.append(dex != null ? dex.typeNames().get(types[i]) : types[i]);
+        }
+        result.append(")");
+        return result.toString();
+    }
+}
diff --git a/dx/src/com/android/dex/util/ByteArrayByteInput.java b/dx/src/com/android/dex/util/ByteArrayByteInput.java
new file mode 100644
index 0000000..889a936
--- /dev/null
+++ b/dx/src/com/android/dex/util/ByteArrayByteInput.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex.util;
+
+public final class ByteArrayByteInput implements ByteInput {
+
+    private final byte[] bytes;
+    private int position;
+
+    public ByteArrayByteInput(byte... bytes) {
+        this.bytes = bytes;
+    }
+
+    @Override public byte readByte() {
+        return bytes[position++];
+    }
+}
diff --git a/dx/src/com/android/dex/util/ByteInput.java b/dx/src/com/android/dex/util/ByteInput.java
new file mode 100644
index 0000000..f1a7196
--- /dev/null
+++ b/dx/src/com/android/dex/util/ByteInput.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex.util;
+
+/**
+ * A byte source.
+ */
+public interface ByteInput {
+
+    /**
+     * Returns a byte.
+     *
+     * @throws IndexOutOfBoundsException if all bytes have been read.
+     */
+    byte readByte();
+}
diff --git a/dx/src/com/android/dex/util/ByteOutput.java b/dx/src/com/android/dex/util/ByteOutput.java
new file mode 100644
index 0000000..eb77040
--- /dev/null
+++ b/dx/src/com/android/dex/util/ByteOutput.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex.util;
+
+/**
+ * A byte sink.
+ */
+public interface ByteOutput {
+
+    /**
+     * Writes a byte.
+     *
+     * @throws IndexOutOfBoundsException if all bytes have been written.
+     */
+    void writeByte(int i);
+}
diff --git a/dx/src/com/android/dex/util/ExceptionWithContext.java b/dx/src/com/android/dex/util/ExceptionWithContext.java
new file mode 100644
index 0000000..5dfd954
--- /dev/null
+++ b/dx/src/com/android/dex/util/ExceptionWithContext.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex.util;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Exception which carries around structured context.
+ */
+public class ExceptionWithContext extends RuntimeException {
+    /** {@code non-null;} human-oriented context of the exception */
+    private StringBuffer context;
+
+    /**
+     * Augments the given exception with the given context, and return the
+     * result. The result is either the given exception if it was an
+     * {@link ExceptionWithContext}, or a newly-constructed exception if it
+     * was not.
+     *
+     * @param ex {@code non-null;} the exception to augment
+     * @param str {@code non-null;} context to add
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static ExceptionWithContext withContext(Throwable ex, String str) {
+        ExceptionWithContext ewc;
+
+        if (ex instanceof ExceptionWithContext) {
+            ewc = (ExceptionWithContext) ex;
+        } else {
+            ewc = new ExceptionWithContext(ex);
+        }
+
+        ewc.addContext(str);
+        return ewc;
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param message human-oriented message
+     */
+    public ExceptionWithContext(String message) {
+        this(message, null);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cause {@code null-ok;} exception that caused this one
+     */
+    public ExceptionWithContext(Throwable cause) {
+        this(null, cause);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param message human-oriented message
+     * @param cause {@code null-ok;} exception that caused this one
+     */
+    public ExceptionWithContext(String message, Throwable cause) {
+        super((message != null) ? message :
+              (cause != null) ? cause.getMessage() : null,
+              cause);
+
+        if (cause instanceof ExceptionWithContext) {
+            String ctx = ((ExceptionWithContext) cause).context.toString();
+            context = new StringBuffer(ctx.length() + 200);
+            context.append(ctx);
+        } else {
+            context = new StringBuffer(200);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void printStackTrace(PrintStream out) {
+        super.printStackTrace(out);
+        out.println(context);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void printStackTrace(PrintWriter out) {
+        super.printStackTrace(out);
+        out.println(context);
+    }
+
+    /**
+     * Adds a line of context to this instance.
+     *
+     * @param str {@code non-null;} new context
+     */
+    public void addContext(String str) {
+        if (str == null) {
+            throw new NullPointerException("str == null");
+        }
+
+        context.append(str);
+        if (!str.endsWith("\n")) {
+            context.append('\n');
+        }
+    }
+
+    /**
+     * Gets the context.
+     *
+     * @return {@code non-null;} the context
+     */
+    public String getContext() {
+        return context.toString();
+    }
+
+    /**
+     * Prints the message and context.
+     *
+     * @param out {@code non-null;} where to print to
+     */
+    public void printContext(PrintStream out) {
+        out.println(getMessage());
+        out.print(context);
+    }
+
+    /**
+     * Prints the message and context.
+     *
+     * @param out {@code non-null;} where to print to
+     */
+    public void printContext(PrintWriter out) {
+        out.println(getMessage());
+        out.print(context);
+    }
+}
diff --git a/dx/src/com/android/dex/util/FileUtils.java b/dx/src/com/android/dex/util/FileUtils.java
new file mode 100644
index 0000000..4cea95c
--- /dev/null
+++ b/dx/src/com/android/dex/util/FileUtils.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * File I/O utilities.
+ */
+public final class FileUtils {
+    private FileUtils() {
+    }
+
+    /**
+     * Reads the named file, translating {@link IOException} to a
+     * {@link RuntimeException} of some sort.
+     *
+     * @param fileName {@code non-null;} name of the file to read
+     * @return {@code non-null;} contents of the file
+     */
+    public static byte[] readFile(String fileName) {
+        File file = new File(fileName);
+        return readFile(file);
+    }
+
+    /**
+     * Reads the given file, translating {@link IOException} to a
+     * {@link RuntimeException} of some sort.
+     *
+     * @param file {@code non-null;} the file to read
+     * @return {@code non-null;} contents of the file
+     */
+    public static byte[] readFile(File file) {
+        if (!file.exists()) {
+            throw new RuntimeException(file + ": file not found");
+        }
+
+        if (!file.isFile()) {
+            throw new RuntimeException(file + ": not a file");
+        }
+
+        if (!file.canRead()) {
+            throw new RuntimeException(file + ": file not readable");
+        }
+
+        long longLength = file.length();
+        int length = (int) longLength;
+        if (length != longLength) {
+            throw new RuntimeException(file + ": file too long");
+        }
+
+        byte[] result = new byte[length];
+
+        try {
+            FileInputStream in = new FileInputStream(file);
+            int at = 0;
+            while (length > 0) {
+                int amt = in.read(result, at, length);
+                if (amt == -1) {
+                    throw new RuntimeException(file + ": unexpected EOF");
+                }
+                at += amt;
+                length -= amt;
+            }
+            in.close();
+        } catch (IOException ex) {
+            throw new RuntimeException(file + ": trouble reading", ex);
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns true if {@code fileName} names a .zip, .jar, or .apk.
+     */
+    public static boolean hasArchiveSuffix(String fileName) {
+        return fileName.endsWith(".zip")
+                || fileName.endsWith(".jar")
+                || fileName.endsWith(".apk");
+    }
+}
diff --git a/dx/src/com/android/dex/util/Unsigned.java b/dx/src/com/android/dex/util/Unsigned.java
new file mode 100644
index 0000000..cb50d0a
--- /dev/null
+++ b/dx/src/com/android/dex/util/Unsigned.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dex.util;
+
+/**
+ * Unsigned arithmetic over Java's signed types.
+ */
+public final class Unsigned {
+    private Unsigned() {}
+
+    public static int compare(short ushortA, short ushortB) {
+        if (ushortA == ushortB) {
+            return 0;
+        }
+        int a = ushortA & 0xFFFF;
+        int b = ushortB & 0xFFFF;
+        return a < b ? -1 : 1;
+    }
+
+    public static int compare(int uintA, int uintB) {
+        if (uintA == uintB) {
+            return 0;
+        }
+        long a = uintA & 0xFFFFFFFFL;
+        long b = uintB & 0xFFFFFFFFL;
+        return a < b ? -1 : 1;
+    }
+}