Improve the interface for reading Dex files.

I'm planning on making this code reusable for grepping
dex files.

Change-Id: Iebf545ed6d6a4eb347ccc4a39fe40c02d75d69e4
diff --git a/dx/src/com/android/dx/dex/DexException.java b/dx/src/com/android/dx/dex/DexException.java
index 7e52a1a..6961209 100644
--- a/dx/src/com/android/dx/dex/DexException.java
+++ b/dx/src/com/android/dx/dex/DexException.java
@@ -16,13 +16,14 @@
 
 package com.android.dx.dex;
 
-import java.io.IOException;
-
 /**
- * Thrown when there's a format problem reading or writing a dex file.
+ * Thrown when there's a format problem reading a dex file.
  */
-public final class DexException extends IOException {
+public final class DexException extends RuntimeException {
     public DexException(String message) {
         super(message);
     }
+    public DexException(Exception cause) {
+        super(cause);
+    }
 }
diff --git a/dx/src/com/android/dx/dex/TableOfContents.java b/dx/src/com/android/dx/dex/TableOfContents.java
index 580241c..313ecad 100644
--- a/dx/src/com/android/dx/dex/TableOfContents.java
+++ b/dx/src/com/android/dx/dex/TableOfContents.java
@@ -16,9 +16,9 @@
 
 package com.android.dx.dex;
 
-import com.android.dx.util.DexReader;
-import com.android.dx.util.DexWriter;
+import com.android.dx.io.DexBuffer;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
 
 /**
@@ -66,51 +66,51 @@
         signature = new byte[20];
     }
 
-    public TableOfContents(DexReader in) throws IOException {
-        byte[] magic = in.readByteArray(8);
+    public void readFrom(DexBuffer buffer) throws IOException {
+        readHeader(buffer.open(0));
+        readMap(buffer.open(mapList.off));
+    }
+
+    private void readHeader(DexBuffer.Section headerIn) throws UnsupportedEncodingException {
+        byte[] magic = headerIn.readByteArray(8);
         if (!Arrays.equals(DexFormat.MAGIC.getBytes("UTF-8"), magic)) {
             throw new DexException("Unexpected magic: " + Arrays.toString(magic));
         }
 
-        checksum = in.readInt();
-        signature = in.readByteArray(20);
-        fileSize = in.readInt();
-        int headerSize = in.readInt();
+        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 = in.readInt();
+        int endianTag = headerIn.readInt();
         if (endianTag != DexFormat.ENDIAN_TAG) {
             throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag));
         }
-        linkSize = in.readInt();
-        linkOff = in.readInt();
-        mapList.off = in.readInt();
+        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 = in.readInt();
-        stringIds.off = in.readInt();
-        typeIds.size = in.readInt();
-        typeIds.off = in.readInt();
-        protoIds.size = in.readInt();
-        protoIds.off = in.readInt();
-        fieldIds.size = in.readInt();
-        fieldIds.off = in.readInt();
-        methodIds.size = in.readInt();
-        methodIds.off = in.readInt();
-        classDefs.size = in.readInt();
-        classDefs.off = in.readInt();
-        dataSize = in.readInt();
-        dataOff = in.readInt();
-
-        int position = in.getPosition();
-        in.seek(mapList.off);
-        readMap(in);
-        in.seek(position);
+        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(DexReader in) throws IOException {
+    private void readMap(DexBuffer.Section in) throws IOException {
         int mapSize = in.readInt();
 
         Section previous = null;
@@ -157,7 +157,7 @@
         throw new IllegalArgumentException("No such map item: " + type);
     }
 
-    public void writeHeader(DexWriter.Section out) throws IOException {
+    public void writeHeader(DexBuffer.Section out) throws IOException {
         out.write(DexFormat.MAGIC.getBytes("UTF-8"));
         out.writeInt(checksum);
         out.write(signature);
@@ -183,7 +183,7 @@
         out.writeInt(dataOff);
     }
 
-    public void writeMap(DexWriter.Section out) throws IOException {
+    public void writeMap(DexBuffer.Section out) throws IOException {
         int count = 0;
         for (Section s : sections) {
             if (s.size > 0) {
diff --git a/dx/src/com/android/dx/io/ClassDef.java b/dx/src/com/android/dx/io/ClassDef.java
new file mode 100644
index 0000000..6837a07
--- /dev/null
+++ b/dx/src/com/android/dx/io/ClassDef.java
@@ -0,0 +1,105 @@
+/*
+ * 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.dx.io;
+
+/**
+ * A type definition.
+ */
+public final class ClassDef {
+    public static final int NO_INDEX = -1;
+    private final DexBuffer buffer;
+    private final int offset;
+    private final int typeIndex;
+    private final int accessFlags;
+    private final int supertypeIndex;
+    private final int interfacesOffset;
+    private final short[] interfaces;
+    private final int sourceFileIndex;
+    private final int annotationsOffset;
+    private final int classDataOffset;
+    private final int staticValuesOffset;
+
+    public ClassDef(DexBuffer buffer, int offset, int typeIndex, int accessFlags,
+            int supertypeIndex, int interfacesOffset, short[] interfaces, 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.interfaces = interfaces;
+        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 interfaces;
+    }
+
+    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();
+        DexBuffer.Section in = buffer.open(0);
+        result.append(in.readTypeName(typeIndex));
+        if (supertypeIndex != NO_INDEX) {
+            result.append(" extends ").append(in.readTypeName(supertypeIndex));
+        }
+        return result.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/io/DexBuffer.java b/dx/src/com/android/dx/io/DexBuffer.java
new file mode 100644
index 0000000..e3309cb
--- /dev/null
+++ b/dx/src/com/android/dx/io/DexBuffer.java
@@ -0,0 +1,405 @@
+/*
+ * 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.dx.io;
+
+import com.android.dx.dex.DexException;
+import com.android.dx.dex.SizeOf;
+import com.android.dx.dex.TableOfContents;
+import com.android.dx.util.Leb128Utils;
+import com.android.dx.util.Mutf8;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInput;
+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.util.Arrays;
+
+/**
+ * The bytes of a dex file in memory for reading and writing. All int offsets
+ * are unsigned.
+ */
+public final class DexBuffer {
+    private byte[] data;
+    private final TableOfContents tableOfContents = new TableOfContents();
+    private int length;
+
+    public 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 = bytesOut.toByteArray();
+        tableOfContents.readFrom(this);
+    }
+
+    public void loadFrom(File file) throws IOException {
+        InputStream in = new FileInputStream(file);
+        loadFrom(in);
+        in.close();
+    }
+
+    public void writeTo(OutputStream out) throws IOException {
+        out.write(data);
+    }
+
+    public void writeTo(File dexOut) throws IOException {
+        OutputStream out = new FileOutputStream(dexOut);
+        writeTo(out);
+        out.close();
+    }
+
+    public TableOfContents getTableOfContents() {
+        return tableOfContents;
+    }
+
+    public Section open(int position) {
+        return new Section(position);
+    }
+
+    public Section appendSection(int maxByteCount, String name) throws IOException {
+        Section result = new Section(name, length, length + maxByteCount);
+        length = fourByteAlign(length + maxByteCount);
+        return result;
+    }
+
+    public void noMoreSections() {
+        data = new byte[length];
+    }
+
+    public int getLength() {
+        return length;
+    }
+
+    private static int fourByteAlign(int position) {
+        return (position + 3) & ~3;
+    }
+
+    public byte[] getBytes() {
+        return data;
+    }
+
+    public final class Section {
+        private final String name;
+        private int position;
+        private final int limit;
+
+        private Section(String name, int position, int limit) {
+            this.name = name;
+            this.position = position;
+            this.limit = limit;
+        }
+
+        private Section(int position) {
+            this("section", position, data.length);
+        }
+
+        private final DataInput asDataInput = new DataInputStub() {
+            public byte readByte() {
+                return Section.this.readByte();
+            }
+        };
+
+        public int getPosition() {
+            return position;
+        }
+
+        public int readInt() {
+            int result = (data[position] & 0xff)
+                    | (data[position + 1] & 0xff) << 8
+                    | (data[position + 2] & 0xff) << 16
+                    | (data[position + 3] & 0xff) << 24;
+            position += 4;
+            return result;
+        }
+
+        public short readShort() {
+            int result = (data[position] & 0xff)
+                    | (data[position + 1] & 0xff) << 8;
+            position += 2;
+            return (short) result;
+        }
+
+        public byte readByte() {
+            return (byte) (data[position++] & 0xff);
+        }
+
+        public byte[] readByteArray(int length) {
+            byte[] result = Arrays.copyOfRange(data, position, position + length);
+            position += length;
+            return result;
+        }
+
+        public short[] readShortArray(int length) {
+            short[] result = new short[length];
+            for (int i = 0; i < length; i++) {
+                result[i] = readShort();
+            }
+            return result;
+        }
+
+        public int readUleb128() {
+            try {
+                return Leb128Utils.readUnsignedLeb128(asDataInput);
+            } catch (IOException e) {
+                throw new DexException(e);
+            }
+        }
+
+        public int readSleb128() {
+            try {
+                return Leb128Utils.readSignedLeb128(asDataInput);
+            } catch (IOException e) {
+                throw new DexException(e);
+            }
+        }
+
+        public short[] readTypeList(int offset) {
+            if (offset == 0) {
+                return new short[0];
+            }
+            int savedPosition = position;
+            position = offset;
+            int size = readInt();
+            short[] parameters = new short[size];
+            for (int i = 0; i < size; i++) {
+                parameters[i] = readShort();
+            }
+            position = savedPosition;
+            return parameters;
+        }
+
+        public String readStringDataItem() {
+            try {
+                int expectedLength = readUleb128();
+                String result = Mutf8.decode(asDataInput, new char[expectedLength]);
+                if (result.length() != expectedLength) {
+                    throw new DexException("Declared length " + expectedLength
+                            + " doesn't match decoded length of " + result.length());
+                }
+                return result;
+            } catch (IOException e) {
+                throw new DexException(e);
+            }
+        }
+
+        /**
+         * Reads a string at the given index. This method does not disturb the position.
+         */
+        public String readString(int index) {
+            int savedPosition = position;
+            position = tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM);
+            position = readInt();
+            String result = readStringDataItem();
+            position = savedPosition;
+            return result;
+        }
+
+        public int readType(int index) {
+            if (index < 0 || index >= tableOfContents.typeIds.size) {
+                throw new IllegalArgumentException("type index out of range: "
+                        + index + " " + tableOfContents.typeIds.size);
+            }
+            int savedPosition = position;
+            position = tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM);
+            int result = readInt();
+            position = savedPosition;
+            return result;
+        }
+
+        public String readTypeName(int index) {
+            return readString(readType(index));
+        }
+
+        public FieldId readFieldId() {
+            short declaringClassIndex = readShort();
+            short typeIndex = readShort();
+            int nameIndex = readInt();
+            return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex);
+        }
+
+        public MethodId readMethodId() {
+            short declaringClassIndex = readShort();
+            short protoIndex = readShort();
+            int nameIndex = readInt();
+            return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex);
+        }
+
+        public ProtoId readProtoId(int index) {
+            int savedPosition = position;
+            position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index);
+            ProtoId result = readProtoId();
+            position = savedPosition;
+            return result;
+        }
+
+        public ProtoId readProtoId() {
+            int shortyIndex = readInt();
+            int returnTypeIndex = readInt();
+            int parametersOff = readInt();
+            short[] parameters = readTypeList(parametersOff);
+            return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parameters);
+        }
+
+        public ClassDef readClassDef() {
+            int offset = getPosition();
+            int type = readInt();
+            int accessFlags = readInt();
+            int supertype = readInt();
+            int interfacesOffset = readInt();
+            short[] interfaces = readTypeList(interfacesOffset);
+            int sourceFileIndex = readInt();
+            int annotationsOffset = readInt();
+            int classDataOffset = readInt();
+            int staticValuesOffset = readInt();
+            return new ClassDef(DexBuffer.this, offset, type, accessFlags, supertype,
+                    interfacesOffset, interfaces, sourceFileIndex, annotationsOffset,
+                    classDataOffset, staticValuesOffset);
+        }
+
+        private void checkPosition() {
+            if (position > limit) {
+                throw new DexException("Section limit " + limit + " exceeded by " + name);
+            }
+        }
+
+        /**
+         * Writes 0x00 until the position is aligned to a multiple of 4.
+         */
+        public void alignToFourBytes() throws IOException {
+            int unalignedCount = position;
+            position = DexBuffer.fourByteAlign(position);
+            for (int i = unalignedCount; i < position; i++) {
+                data[i] = 0;
+            }
+        }
+
+        public void assertFourByteAligned() throws IOException {
+            if ((position & 3) != 0) {
+                throw new IllegalStateException("Not four byte aligned!");
+            }
+        }
+
+        public void write(byte[] bytes) throws IOException {
+            System.arraycopy(bytes, 0, data, position, bytes.length);
+            position += bytes.length;
+            checkPosition();
+        }
+
+        public void writeByte(int b) throws IOException {
+            data[position++] = (byte) b;
+            checkPosition();
+        }
+
+        public void writeShort(short i) throws IOException {
+            data[position    ] = (byte) i;
+            data[position + 1] = (byte) (i >>> 8);
+            position += 2;
+            checkPosition();
+        }
+
+        public void write(short[] shorts) throws IOException {
+            for (short s : shorts) {
+                writeShort(s);
+            }
+        }
+
+        public void writeInt(int i) throws IOException {
+            data[position    ] = (byte) i;
+            data[position + 1] = (byte) (i >>>  8);
+            data[position + 2] = (byte) (i >>> 16);
+            data[position + 3] = (byte) (i >>> 24);
+            position += 4;
+            checkPosition();
+        }
+
+        public void writeUleb128(int i) throws IOException {
+            position += Leb128Utils.writeUnsignedLeb128(data, position, i);
+            checkPosition();
+        }
+
+        public void writeSleb128(int i) throws IOException {
+            position += Leb128Utils.writeSignedLeb128(data, position, i);
+            checkPosition();
+        }
+
+        public void writeStringDataItem(String value) throws IOException {
+            int length = value.length();
+            writeUleb128(length);
+            write(Mutf8.encode(value));
+            writeByte(0);
+        }
+
+        public Section open(int position) {
+            return new Section(position);
+        }
+    }
+
+    private static class DataInputStub implements DataInput {
+        public byte readByte() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public void readFully(byte[] buffer) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public void readFully(byte[] buffer, int offset, int count) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public int skipBytes(int i) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public boolean readBoolean() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public int readUnsignedByte() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public short readShort() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public int readUnsignedShort() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public char readChar() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public int readInt() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public long readLong() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public float readFloat() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public double readDouble() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public String readLine() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public String readUTF() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/io/DexHasher.java b/dx/src/com/android/dx/io/DexHasher.java
new file mode 100644
index 0000000..416b3e2
--- /dev/null
+++ b/dx/src/com/android/dx/io/DexHasher.java
@@ -0,0 +1,75 @@
+/*
+ * 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.dx.io;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.zip.Adler32;
+
+/**
+ * Generates and stores the checksum and signature of a dex file.
+ */
+public final class DexHasher {
+    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;
+
+    /**
+     * Returns the signature of all but the first 32 bytes of {@code dex}. The
+     * first 32 bytes of dex files are not specified to be included in the
+     * signature.
+     */
+    public byte[] computeSignature(DexBuffer dex) throws IOException {
+        MessageDigest digest;
+        try {
+            digest = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError();
+        }
+        int offset = SIGNATURE_OFFSET + SIGNATURE_SIZE;
+
+        byte[] bytes = dex.getBytes();
+        digest.update(bytes, offset, bytes.length - offset);
+        return digest.digest();
+    }
+
+    /**
+     * Returns the checksum of all but the first 12 bytes of {@code dex}.
+     */
+    public int computeChecksum(DexBuffer dex) throws IOException {
+        Adler32 adler32 = new Adler32();
+        int offset = CHECKSUM_OFFSET + CHECKSUM_SIZE;
+
+        byte[] bytes = dex.getBytes();
+        adler32.update(bytes, offset, bytes.length - offset);
+        return (int) adler32.getValue();
+    }
+
+    /**
+     * Generates the signature and checksum of the dex file {@code out} and
+     * writes them to the file.
+     */
+    public void writeHashes(DexBuffer dex) throws IOException {
+        byte[] signature = computeSignature(dex);
+        dex.open(SIGNATURE_OFFSET).write(signature);
+
+        int checksum = computeChecksum(dex);
+        dex.open(CHECKSUM_OFFSET).writeInt(checksum);
+    }
+}
diff --git a/dx/src/com/android/dx/io/DexIndexPrinter.java b/dx/src/com/android/dx/io/DexIndexPrinter.java
new file mode 100644
index 0000000..e4996bd
--- /dev/null
+++ b/dx/src/com/android/dx/io/DexIndexPrinter.java
@@ -0,0 +1,121 @@
+/*
+ * 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.dx.io;
+
+import com.android.dx.dex.TableOfContents;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Executable that prints all indices of a dex file.
+ */
+public final class DexIndexPrinter {
+    private final DexBuffer dexBuffer;
+    private final TableOfContents tableOfContents;
+
+    public DexIndexPrinter(File file) throws IOException {
+        this.dexBuffer = new DexBuffer();
+        this.dexBuffer.loadFrom(file);
+        this.tableOfContents = dexBuffer.getTableOfContents();
+    }
+
+    private void printMap() {
+        for (TableOfContents.Section section : tableOfContents.sections) {
+            if (section.off != -1) {
+                System.out.println("section " + Integer.toHexString(section.type)
+                        + " off=" + Integer.toHexString(section.off)
+                        + " size=" + Integer.toHexString(section.size)
+                        + " byteCount=" + Integer.toHexString(section.byteCount));
+            }
+        }
+    }
+
+    private void printStrings() throws IOException {
+        DexBuffer.Section in = dexBuffer.open(tableOfContents.stringIds.off);
+        for (int i = 0; i < tableOfContents.stringIds.size; i++) {
+            String s = in.readString(i);
+            System.out.println("string " + i + ": " + s);
+        }
+    }
+
+    private void printTypeIds() throws IOException {
+        DexBuffer.Section in = dexBuffer.open(tableOfContents.typeIds.off);
+        for (int i = 0; i < tableOfContents.typeIds.size; i++) {
+            int stringIndex = in.readInt();
+            System.out.println("type " + i + ": " + in.readString(stringIndex));
+        }
+    }
+
+    private void printProtoIds() throws IOException {
+        DexBuffer.Section in = dexBuffer.open(tableOfContents.protoIds.off);
+        for (int i = 0; i < tableOfContents.protoIds.size; i++) {
+            System.out.println("proto " + i + ": " + in.readProtoId());
+        }
+    }
+
+    private void printFieldIds() throws IOException {
+        DexBuffer.Section in = dexBuffer.open(tableOfContents.fieldIds.off);
+        for (int i = 0; i < tableOfContents.fieldIds.size; i++) {
+            System.out.println("field " + i + ": " + in.readFieldId());
+        }
+    }
+
+    private void printMethodIds() throws IOException {
+        DexBuffer.Section in = dexBuffer.open(tableOfContents.methodIds.off);
+        for (int i = 0; i < tableOfContents.methodIds.size; i++) {
+            System.out.println("method " + i + ": " + in.readMethodId());
+        }
+    }
+
+    private void printTypeLists() throws IOException {
+        if (tableOfContents.typeLists.off == -1) {
+            System.out.println("No type lists");
+            return;
+        }
+        DexBuffer.Section in = dexBuffer.open(tableOfContents.typeLists.off);
+        for (int i = 0; i < tableOfContents.typeLists.size; i++) {
+            int size = in.readInt();
+            System.out.print("Type list i=" + i + ", size=" + size + ", elements=");
+            for (int t = 0; t < size; t++) {
+                System.out.print(" " + in.readTypeName((int) in.readShort()));
+            }
+            if (size % 2 == 1) {
+                in.readShort(); // retain alignment
+            }
+            System.out.println();
+        }
+    }
+
+    private void printClassDefs() {
+        DexBuffer.Section in = dexBuffer.open(tableOfContents.classDefs.off);
+        for (int i = 0; i < tableOfContents.classDefs.size; i++) {
+            System.out.println("class def " + i + ": " + in.readClassDef());
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+        DexIndexPrinter indexPrinter = new DexIndexPrinter(new File(args[0]));
+        indexPrinter.printMap();
+        indexPrinter.printStrings();
+        indexPrinter.printTypeIds();
+        indexPrinter.printProtoIds();
+        indexPrinter.printFieldIds();
+        indexPrinter.printMethodIds();
+        indexPrinter.printTypeLists();
+        indexPrinter.printClassDefs();
+    }
+}
diff --git a/dx/src/com/android/dx/io/FieldId.java b/dx/src/com/android/dx/io/FieldId.java
new file mode 100644
index 0000000..c0eeac3
--- /dev/null
+++ b/dx/src/com/android/dx/io/FieldId.java
@@ -0,0 +1,72 @@
+/*
+ * 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.dx.io;
+
+import com.android.dx.util.Unsigned;
+import java.io.IOException;
+
+public final class FieldId implements Comparable<FieldId> {
+    private final DexBuffer buffer;
+    private final short declaringClassIndex;
+    private final short typeIndex;
+    private final int nameIndex;
+
+    public FieldId(DexBuffer buffer, short declaringClassIndex, short typeIndex, int nameIndex) {
+        this.buffer = buffer;
+        this.declaringClassIndex = declaringClassIndex;
+        this.typeIndex = typeIndex;
+        this.nameIndex = nameIndex;
+    }
+
+    public short getDeclaringClassIndex() {
+        return declaringClassIndex;
+    }
+
+    public short 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(DexBuffer.Section out) throws IOException {
+        out.writeShort(declaringClassIndex);
+        out.writeShort(typeIndex);
+        out.writeInt(nameIndex);
+    }
+
+    @Override public String toString() {
+        if (buffer == null) {
+            return declaringClassIndex + " " + typeIndex + " " + nameIndex;
+        }
+        DexBuffer.Section in = buffer.open(0);
+        return in.readType(declaringClassIndex)
+                + " { " + in.readTypeName(typeIndex)
+                + " " + in.readString(nameIndex) + " }";
+    }
+}
diff --git a/dx/src/com/android/dx/io/MethodId.java b/dx/src/com/android/dx/io/MethodId.java
new file mode 100644
index 0000000..9498b68
--- /dev/null
+++ b/dx/src/com/android/dx/io/MethodId.java
@@ -0,0 +1,72 @@
+/*
+ * 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.dx.io;
+
+import com.android.dx.util.Unsigned;
+import java.io.IOException;
+
+public final class MethodId implements Comparable<MethodId> {
+    private final DexBuffer buffer;
+    private final short declaringClassIndex;
+    private final short protoIndex;
+    private final int nameIndex;
+
+    public MethodId(DexBuffer buffer, short declaringClassIndex, short protoIndex, int nameIndex) {
+        this.buffer = buffer;
+        this.declaringClassIndex = declaringClassIndex;
+        this.protoIndex = protoIndex;
+        this.nameIndex = nameIndex;
+    }
+
+    public short getDeclaringClassIndex() {
+        return declaringClassIndex;
+    }
+
+    public short 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(DexBuffer.Section out) throws IOException {
+        out.writeShort(declaringClassIndex);
+        out.writeShort(protoIndex);
+        out.writeInt(nameIndex);
+    }
+
+    @Override public String toString() {
+        if (buffer == null) {
+            return declaringClassIndex + " " + protoIndex + " " + nameIndex;
+        }
+        DexBuffer.Section in = buffer.open(0);
+        return in.readTypeName(declaringClassIndex)
+                + " " + in.readProtoId(protoIndex)
+                + " " + in.readString(nameIndex);
+    }
+}
diff --git a/dx/src/com/android/dx/io/ProtoId.java b/dx/src/com/android/dx/io/ProtoId.java
new file mode 100644
index 0000000..dcab90f
--- /dev/null
+++ b/dx/src/com/android/dx/io/ProtoId.java
@@ -0,0 +1,88 @@
+/*
+ * 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.dx.io;
+
+import com.android.dx.util.Unsigned;
+import java.io.IOException;
+import java.util.Arrays;
+
+public final class ProtoId implements Comparable<ProtoId> {
+    private final DexBuffer buffer;
+    private final int shortyIndex;
+    private final int returnTypeIndex;
+    private final short[] parameters;
+
+    public ProtoId(DexBuffer buffer, int shortyIndex, int returnTypeIndex, short[] parameters) {
+        this.buffer = buffer;
+        this.shortyIndex = shortyIndex;
+        this.returnTypeIndex = returnTypeIndex;
+        this.parameters = parameters;
+    }
+
+    public int compareTo(ProtoId other) {
+        if (returnTypeIndex != other.returnTypeIndex) {
+            return Unsigned.compare(returnTypeIndex, other.returnTypeIndex);
+        }
+        for (int i = 0; i < parameters.length && i < other.parameters.length; i++) {
+            if (parameters[i] != other.parameters[i]) {
+                return Unsigned.compare(parameters[i], other.parameters[i]);
+            }
+        }
+        return Unsigned.compare(parameters.length, other.parameters.length);
+    }
+
+    public int getShortyIndex() {
+        return shortyIndex;
+    }
+
+    public int getReturnTypeIndex() {
+        return returnTypeIndex;
+    }
+
+    public short[] getParameters() {
+        return parameters;
+    }
+
+    public void writeTo(DexBuffer.Section out, int typeListOffset) throws IOException {
+        out.writeInt(shortyIndex);
+        out.writeInt(returnTypeIndex);
+        out.writeInt(typeListOffset);
+    }
+
+    @Override public String toString() {
+        if (buffer == null) {
+            return shortyIndex + " " + returnTypeIndex + " " + Arrays.toString(parameters);
+        }
+
+        DexBuffer.Section in = buffer.open(0);
+        StringBuilder result = new StringBuilder()
+                .append(in.readString(shortyIndex))
+                .append(": ")
+                .append(in.readTypeName(returnTypeIndex))
+                .append(" (");
+        int j = 0;
+        for (short parameter : parameters) {
+            if (j > 0) {
+                result.append(", ");
+            }
+            result.append(in.readTypeName(parameter));
+            j++;
+        }
+        result.append(")");
+        return result.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/merge/DexHasher.java b/dx/src/com/android/dx/merge/DexHasher.java
deleted file mode 100644
index 43b0df4..0000000
--- a/dx/src/com/android/dx/merge/DexHasher.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.dx.merge;
-
-import com.android.dx.dex.SizeOf;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.zip.Adler32;
-
-/**
- * Generates and stores the checksum and signature of a dex file.
- */
-public final class DexHasher {
-
-    /**
-     * Returns the signature of all but the first 32 bytes of {@code dex}. The
-     * first 32 bytes of dex files are not specified to be included in the
-     * signature.
-     */
-    public byte[] computeSignature(File dex) throws IOException {
-        MessageDigest digest;
-        try {
-            digest = MessageDigest.getInstance("SHA-1");
-        } catch (NoSuchAlgorithmException e) {
-            throw new AssertionError();
-        }
-
-        FileInputStream in = new FileInputStream(dex);
-        skipFully(in, 32);
-
-        byte[] buffer = new byte[8192];
-        int count;
-        while ((count = in.read(buffer)) != -1) {
-            digest.update(buffer, 0, count);
-        }
-
-        return digest.digest();
-    }
-
-    /**
-     * Returns the checksum of all but the first 12 bytes of {@code dex}.
-     *
-     * @param signature either a 20-byte signature of {@code dex}, or null. If
-     *     non-null, the checksum will be computed using {@code signature}
-     *     rather than the corresponding bytes in the file.
-     */
-    public int computeChecksum(File dex, byte[] signature) throws IOException {
-        Adler32 adler32 = new Adler32();
-
-        FileInputStream in = new FileInputStream(dex);
-        skipFully(in, 12);
-
-        if (signature != null) {
-            adler32.update(signature);
-            skipFully(in, SizeOf.SIGNATURE);
-        }
-
-        byte[] buffer = new byte[8192];
-        int count;
-        while ((count = in.read(buffer)) != -1) {
-            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(File out) throws IOException {
-        byte[] signature = computeSignature(out);
-        int checksum = computeChecksum(out, signature);
-
-        RandomAccessFile file = new RandomAccessFile(out, "rw");
-        file.seek(8);
-        file.writeInt(Integer.reverseBytes(checksum));
-        file.write(signature);
-        file.close();
-    }
-
-    private void skipFully(InputStream in, int count) throws IOException {
-        int total = 0;
-        while (total < count) {
-            total += in.skip(count);
-        }
-    }
-}
diff --git a/dx/src/com/android/dx/merge/DexMerger.java b/dx/src/com/android/dx/merge/DexMerger.java
index 54adff2..7e8321a 100644
--- a/dx/src/com/android/dx/merge/DexMerger.java
+++ b/dx/src/com/android/dx/merge/DexMerger.java
@@ -18,8 +18,12 @@
 
 import com.android.dx.dex.SizeOf;
 import com.android.dx.dex.TableOfContents;
-import com.android.dx.util.DexReader;
-import com.android.dx.util.DexWriter;
+import com.android.dx.io.ClassDef;
+import com.android.dx.io.DexBuffer;
+import com.android.dx.io.DexHasher;
+import com.android.dx.io.FieldId;
+import com.android.dx.io.MethodId;
+import com.android.dx.io.ProtoId;
 import com.android.dx.util.Uint;
 import java.io.File;
 import java.io.IOException;
@@ -30,30 +34,27 @@
  * Combine two dex files into one.
  */
 public final class DexMerger {
-    public static final int NO_INDEX = -1;
     private static final Logger logger = Logger.getLogger(DexMerger.class.getName());
 
     private final File dexOut;
-    private final DexWriter dexWriter;
-    private final DexWriter.Section headerWriter;
-    private final DexWriter.Section idsDefsWriter;
-    private final DexWriter.Section mapListWriter;
-    private final DexWriter.Section typeListWriter;
-    private final DexWriter.Section annotationSetRefListWriter;
-    private final DexWriter.Section annotationSetItemWriter;
-    private final DexWriter.Section classDataItemWriter;
-    private final DexWriter.Section codeItemWriter;
-    private final DexWriter.Section stringDataItemWriter;
-    private final DexWriter.Section debugInfoItemWriter;
-    private final DexWriter.Section annotationItemWriter;
-    private final DexWriter.Section encodedArrayItemWriter;
-    private final DexWriter.Section annotationsDirectoryItemWriter;
-    private final TableOfContents contentsOut = new TableOfContents();
-    private final DexReader dexA;
-    private final DexReader dexB;
-    private final TableOfContents contentsA;
-    private final TableOfContents contentsB;
+    private final DexBuffer dexWriter = new DexBuffer();
+    private final DexBuffer.Section headerWriter;
+    private final DexBuffer.Section idsDefsWriter;
+    private final DexBuffer.Section mapListWriter;
+    private final DexBuffer.Section typeListWriter;
+    private final DexBuffer.Section annotationSetRefListWriter;
+    private final DexBuffer.Section annotationSetItemWriter;
+    private final DexBuffer.Section classDataItemWriter;
+    private final DexBuffer.Section codeItemWriter;
+    private final DexBuffer.Section stringDataItemWriter;
+    private final DexBuffer.Section debugInfoItemWriter;
+    private final DexBuffer.Section annotationItemWriter;
+    private final DexBuffer.Section encodedArrayItemWriter;
+    private final DexBuffer.Section annotationsDirectoryItemWriter;
+    private final TableOfContents contentsOut;
 
+    private final DexBuffer dexA = new DexBuffer();
+    private final DexBuffer dexB = new DexBuffer();
     private final IndexMap aIndexMap;
     private final IndexMap bIndexMap;
 
@@ -64,34 +65,34 @@
 
         this.dexOut = dexOut;
 
-        dexWriter = new DexWriter(dexOut);
-        dexA = new DexReader(a);
-        dexB = new DexReader(b);
-        contentsA = dexA.getTableOfContents();
-        contentsB = dexB.getTableOfContents();
+        dexA.loadFrom(a);
+        dexB.loadFrom(b);
 
-        aIndexMap = new IndexMap(contentsA);
-        bIndexMap = new IndexMap(contentsB);
+        TableOfContents aContents = dexA.getTableOfContents();
+        TableOfContents bContents = dexB.getTableOfContents();
 
-        // header
-        headerWriter = dexWriter.newSection(SizeOf.HEADER_ITEM, "header");
+        aIndexMap = new IndexMap(dexWriter, aContents);
+        bIndexMap = new IndexMap(dexWriter, bContents);
+
+        headerWriter = dexWriter.appendSection(SizeOf.HEADER_ITEM, "header");
 
         // All IDs and definitions sections
         int idsDefsMaxSize
-                = (contentsA.stringIds.size + contentsB.stringIds.size) * SizeOf.STRING_ID_ITEM
-                + (contentsA.typeIds.size + contentsB.typeIds.size) * SizeOf.TYPE_ID_ITEM
-                + (contentsA.protoIds.size + contentsB.protoIds.size) * SizeOf.PROTO_ID_ITEM
-                + (contentsA.fieldIds.size + contentsB.fieldIds.size) * SizeOf.MEMBER_ID_ITEM
-                + (contentsA.methodIds.size + contentsB.methodIds.size) * SizeOf.MEMBER_ID_ITEM
-                + (contentsA.classDefs.size + contentsB.classDefs.size) * SizeOf.CLASS_DEF_ITEM;
-        idsDefsWriter = dexWriter.newSection(idsDefsMaxSize, "ids defs");
+                = (aContents.stringIds.size + bContents.stringIds.size) * SizeOf.STRING_ID_ITEM
+                + (aContents.typeIds.size + bContents.typeIds.size) * SizeOf.TYPE_ID_ITEM
+                + (aContents.protoIds.size + bContents.protoIds.size) * SizeOf.PROTO_ID_ITEM
+                + (aContents.fieldIds.size + bContents.fieldIds.size) * SizeOf.MEMBER_ID_ITEM
+                + (aContents.methodIds.size + bContents.methodIds.size) * SizeOf.MEMBER_ID_ITEM
+                + (aContents.classDefs.size + bContents.classDefs.size) * SizeOf.CLASS_DEF_ITEM;
+        idsDefsWriter = dexWriter.appendSection(idsDefsMaxSize, "ids defs");
 
         // data section
+        contentsOut = dexWriter.getTableOfContents();
         contentsOut.dataOff = dexWriter.getLength();
 
         contentsOut.mapList.off = dexWriter.getLength();
         contentsOut.mapList.size = 1;
-        mapListWriter = dexWriter.newSection(SizeOf.UINT
+        mapListWriter = dexWriter.appendSection(SizeOf.UINT
                 + (contentsOut.sections.length * SizeOf.MAP_ITEM), "map list");
 
         /*
@@ -110,58 +111,59 @@
 
         contentsOut.typeLists.off = dexWriter.getLength();
         contentsOut.typeLists.size = 0;
-        int maxTypeListBytes = contentsA.typeLists.byteCount + contentsB.typeLists.byteCount;
-        typeListWriter = dexWriter.newSection(maxTypeListBytes * 5, "type list");
+        int maxTypeListBytes = aContents.typeLists.byteCount + bContents.typeLists.byteCount;
+        typeListWriter = dexWriter.appendSection(maxTypeListBytes * 5, "type list");
 
         contentsOut.annotationSetRefLists.off = dexWriter.getLength();
         contentsOut.annotationSetRefLists.size = 0;
-        annotationSetRefListWriter = dexWriter.newSection(SizeOf.UINT, "annotation set ref list");
+        annotationSetRefListWriter = dexWriter.appendSection(SizeOf.UINT, "annotation set ref list");
 
         contentsOut.annotationSets.off = dexWriter.getLength();
         contentsOut.annotationSets.size = 0;
-        annotationSetItemWriter = dexWriter.newSection(SizeOf.UINT, "annotation set item");
+        annotationSetItemWriter = dexWriter.appendSection(SizeOf.UINT, "annotation set item");
 
         contentsOut.classDatas.off = dexWriter.getLength();
         contentsOut.classDatas.size = 0;
-        int maxClassDataItemBytes = contentsA.classDatas.byteCount + contentsB.classDatas.byteCount;
-        classDataItemWriter = dexWriter.newSection(maxClassDataItemBytes * 2, "class data");
+        int maxClassDataItemBytes = aContents.classDatas.byteCount + bContents.classDatas.byteCount;
+        classDataItemWriter = dexWriter.appendSection(maxClassDataItemBytes * 2, "class data");
 
         contentsOut.codes.off = dexWriter.getLength();
         contentsOut.codes.size = 0;
-        int maxCodeItemBytes = contentsA.codes.byteCount + contentsB.codes.byteCount;
-        codeItemWriter = dexWriter.newSection(maxCodeItemBytes, "code item");
+        int maxCodeItemBytes = aContents.codes.byteCount + bContents.codes.byteCount;
+        codeItemWriter = dexWriter.appendSection(maxCodeItemBytes, "code item");
 
         contentsOut.stringDatas.off = dexWriter.getLength();
         contentsOut.stringDatas.size = 0;
-        int maxStringDataItemBytes = contentsA.stringDatas.byteCount
-                + contentsB.stringDatas.byteCount;
-        stringDataItemWriter = dexWriter.newSection(maxStringDataItemBytes * 2, "string data");
+        int maxStringDataItemBytes = aContents.stringDatas.byteCount
+                + bContents.stringDatas.byteCount;
+        stringDataItemWriter = dexWriter.appendSection(maxStringDataItemBytes * 2, "string data");
 
         contentsOut.debugInfos.off = dexWriter.getLength();
         contentsOut.debugInfos.size = 0;
-        int maxDebugInfoItemBytes = contentsA.debugInfos.byteCount + contentsB.debugInfos.byteCount;
-        debugInfoItemWriter = dexWriter.newSection(maxDebugInfoItemBytes, "debug info");
+        int maxDebugInfoItemBytes = aContents.debugInfos.byteCount + bContents.debugInfos.byteCount;
+        debugInfoItemWriter = dexWriter.appendSection(maxDebugInfoItemBytes, "debug info");
 
         contentsOut.annotations.off = dexWriter.getLength();
         contentsOut.annotations.size = 0;
-        int maxAnnotationItemBytes = contentsA.annotations.byteCount
-                + contentsB.annotations.byteCount;
-        annotationItemWriter = dexWriter.newSection(maxAnnotationItemBytes, "annotation");
+        int maxAnnotationItemBytes = aContents.annotations.byteCount
+                + bContents.annotations.byteCount;
+        annotationItemWriter = dexWriter.appendSection(maxAnnotationItemBytes, "annotation");
 
         contentsOut.encodedArrays.off = dexWriter.getLength();
         contentsOut.encodedArrays.size = 0;
-        int maxEncodedArrayItemBytes = contentsA.encodedArrays.byteCount
-                + contentsB.encodedArrays.byteCount;
-        encodedArrayItemWriter = dexWriter.newSection(
+        int maxEncodedArrayItemBytes = aContents.encodedArrays.byteCount
+                + bContents.encodedArrays.byteCount;
+        encodedArrayItemWriter = dexWriter.appendSection(
                 maxEncodedArrayItemBytes * 2, "encoded array");
 
         contentsOut.annotationsDirectories.off = dexWriter.getLength();
         contentsOut.annotationsDirectories.size = 0;
-        int maxAnnotationsDirectoryItemBytes = contentsA.annotationsDirectories.byteCount
-                + contentsB.annotationsDirectories.byteCount;
-        annotationsDirectoryItemWriter = dexWriter.newSection(
+        int maxAnnotationsDirectoryItemBytes = aContents.annotationsDirectories.byteCount
+                + bContents.annotationsDirectories.byteCount;
+        annotationsDirectoryItemWriter = dexWriter.appendSection(
                 maxAnnotationsDirectoryItemBytes, "annotations");
 
+        dexWriter.noMoreSections();
         contentsOut.dataSize = dexWriter.getLength() - contentsOut.dataOff;
     }
 
@@ -183,8 +185,8 @@
         contentsOut.writeMap(mapListWriter);
 
         // close (and flush) the result, then reopen to generate and write the hashes
-        dexWriter.close();
-        new DexHasher().writeHashes(dexOut);
+        new DexHasher().writeHashes(dexWriter);
+        dexWriter.writeTo(dexOut);
 
         long elapsed = System.nanoTime() - start;
         logger.info(String.format("Merged. Result length=%.1fKiB. Took %.1fs",
@@ -197,24 +199,24 @@
      */
     abstract class IdMerger<T extends Comparable<T>> {
         public final void merge() throws IOException {
-            dexA.seek(getSection(contentsA).off);
-            dexB.seek(getSection(contentsB).off);
-            getSection(contentsOut).off = idsDefsWriter.getCursor();
+            TableOfContents.Section aSection = getSection(dexA.getTableOfContents());
+            TableOfContents.Section bSection = getSection(dexB.getTableOfContents());
+            DexBuffer.Section aIn = dexA.open(aSection.off);
+            DexBuffer.Section bIn = dexB.open(bSection.off);
+            getSection(contentsOut).off = idsDefsWriter.getPosition();
 
             int aIndex = 0;
             int bIndex = 0;
             int outCount = 0;
-            int aCount = getSection(contentsA).size;
-            int bCount = getSection(contentsB).size;
             T a = null;
             T b = null;
 
             while (true) {
-                if (a == null && aIndex < aCount) {
-                    a = read(dexA, aIndexMap, aIndex);
+                if (a == null && aIndex < aSection.size) {
+                    a = read(aIn, aIndexMap, aIndex);
                 }
-                if (b == null && bIndex < bCount) {
-                    b = read(dexB, bIndexMap, bIndex);
+                if (b == null && bIndex < bSection.size) {
+                    b = read(bIn, bIndexMap, bIndex);
                 }
 
                 // Write the smaller of a and b. If they're equal, write only once
@@ -251,7 +253,7 @@
         }
 
         abstract TableOfContents.Section getSection(TableOfContents tableOfContents);
-        abstract T read(DexReader in, IndexMap indexMap, int index) throws IOException;
+        abstract T read(DexBuffer.Section in, IndexMap indexMap, int index) throws IOException;
         abstract void updateIndex(IndexMap indexMap, int oldIndex, int newIndex);
         abstract void write(T value) throws IOException;
     }
@@ -262,7 +264,8 @@
                 return tableOfContents.stringIds;
             }
 
-            @Override String read(DexReader in, IndexMap indexMap, int index) throws IOException {
+            @Override String read(DexBuffer.Section in, IndexMap indexMap, int index)
+                    throws IOException {
                 return in.readString(index);
             }
 
@@ -272,7 +275,7 @@
 
             @Override void write(String value) throws IOException {
                 contentsOut.stringDatas.size++;
-                idsDefsWriter.writeInt(stringDataItemWriter.getCursor());
+                idsDefsWriter.writeInt(stringDataItemWriter.getPosition());
                 stringDataItemWriter.writeStringDataItem(value);
             }
         }.merge();
@@ -284,12 +287,13 @@
                 return tableOfContents.typeIds;
             }
 
-            @Override Uint read(DexReader in, IndexMap indexMap, int index) throws IOException {
-                return new Uint(indexMap.stringIds[in.readInt()]);
+            @Override Uint read(DexBuffer.Section in, IndexMap indexMap, int index)
+                    throws IOException {
+                return new Uint(indexMap.adjustString(in.readInt()));
             }
 
             @Override void updateIndex(IndexMap indexMap, int oldIndex, int newIndex) {
-                indexMap.typeIds[oldIndex] = newIndex;
+                indexMap.typeIds[oldIndex] = (short) newIndex;
             }
 
             @Override void write(Uint value) throws IOException {
@@ -304,14 +308,13 @@
                 return tableOfContents.protoIds;
             }
 
-            @Override ProtoId read(DexReader in, IndexMap indexMap, int index) throws IOException {
-                ProtoId result = new ProtoId(in);
-                result.adjust(indexMap);
-                return result;
+            @Override ProtoId read(DexBuffer.Section in, IndexMap indexMap, int index)
+                    throws IOException {
+                return indexMap.adjust(in.readProtoId());
             }
 
             @Override void updateIndex(IndexMap indexMap, int oldIndex, int newIndex) {
-                indexMap.protoIds[oldIndex] = newIndex;
+                indexMap.protoIds[oldIndex] = (short) newIndex;
             }
 
             @Override void write(ProtoId value) throws IOException {
@@ -327,14 +330,13 @@
                 return tableOfContents.fieldIds;
             }
 
-            @Override FieldId read(DexReader in, IndexMap indexMap, int index) throws IOException {
-                FieldId result = new FieldId(in);
-                result.adjust(indexMap);
-                return result;
+            @Override
+            FieldId read(DexBuffer.Section in, IndexMap indexMap, int index) throws IOException {
+                return indexMap.adjust(in.readFieldId());
             }
 
             @Override void updateIndex(IndexMap indexMap, int oldIndex, int newIndex) {
-                indexMap.fieldIds[oldIndex] = newIndex;
+                indexMap.fieldIds[oldIndex] = (short) newIndex;
             }
 
             @Override void write(FieldId value) throws IOException {
@@ -349,14 +351,13 @@
                 return tableOfContents.methodIds;
             }
 
-            @Override MethodId read(DexReader in, IndexMap indexMap, int index) throws IOException {
-                MethodId result = new MethodId(in);
-                result.adjust(indexMap);
-                return result;
+            @Override MethodId read(DexBuffer.Section in, IndexMap indexMap, int index)
+                    throws IOException {
+                return indexMap.adjust(in.readMethodId());
             }
 
             @Override void updateIndex(IndexMap indexMap, int oldIndex, int newIndex) {
-                indexMap.methodIds[oldIndex] = newIndex;
+                indexMap.methodIds[oldIndex] = (short) newIndex;
             }
 
             @Override void write(MethodId methodId) throws IOException {
@@ -367,13 +368,13 @@
 
     private void mergeClassDefs() throws IOException {
         SortableType[] types = getSortedTypes();
-        contentsOut.classDefs.off = idsDefsWriter.getCursor();
+        contentsOut.classDefs.off = idsDefsWriter.getPosition();
         contentsOut.classDefs.size = types.length;
 
         for (SortableType type : types) {
-            DexReader in = type.prepareReader();
+            DexBuffer in = type.getBuffer();
             IndexMap indexMap = (in == dexA) ? aIndexMap : bIndexMap;
-            transformClassDef(in, indexMap);
+            transformClassDef(in, type.getClassDef(), indexMap);
         }
     }
 
@@ -417,15 +418,15 @@
      * Reads just enough data on each class so that we can sort it and then find
      * it later.
      */
-    private void readSortableTypes(SortableType[] sortableTypes, DexReader reader,
+    private void readSortableTypes(SortableType[] sortableTypes, DexBuffer buffer,
             IndexMap indexMap) throws IOException {
-        TableOfContents tableOfContents = reader.getTableOfContents();
-        reader.seek(tableOfContents.classDefs.off);
+        TableOfContents tableOfContents = buffer.getTableOfContents();
+        DexBuffer.Section classDefsIn = buffer.open(tableOfContents.classDefs.off);
 
         for (int i = 0; i < tableOfContents.classDefs.size; i++) {
-            SortableType sortableType = new SortableType(reader);
-            sortableType.adjust(indexMap);
-            int t = sortableType.getType();
+            SortableType sortableType = indexMap.adjust(
+                    new SortableType(buffer, classDefsIn.readClassDef()));
+            int t = sortableType.getTypeIndex();
             if (sortableTypes[t] == null) {
                 sortableTypes[t] = sortableType;
             }
@@ -436,60 +437,47 @@
      * Reads a class_def_item beginning at {@code in} and writes the index and
      * data.
      */
-    private void transformClassDef(DexReader in, IndexMap indexMap) throws IOException {
+    private void transformClassDef(DexBuffer in, ClassDef classDef, IndexMap indexMap)
+            throws IOException {
         idsDefsWriter.assertFourByteAligned();
-        idsDefsWriter.writeInt(indexMap.typeIds[in.readInt()]); // class idx
-        idsDefsWriter.writeInt(in.readInt()); // access flags
-        int inSuperclassIndex = in.readInt(); // superclass idx
-        int outSuperclassIndex = inSuperclassIndex != NO_INDEX
-                ? indexMap.typeIds[inSuperclassIndex]
-                : NO_INDEX;
-        idsDefsWriter.writeInt(outSuperclassIndex);
+        idsDefsWriter.writeInt(classDef.getTypeIndex());
+        idsDefsWriter.writeInt(classDef.getAccessFlags());
+        idsDefsWriter.writeInt(classDef.getSupertypeIndex());
 
-        int interfaceOff = in.readInt(); // interface off
-        short[] interfaces = in.readTypeList(interfaceOff);
-        indexMap.adjustTypeList(interfaces);
+        short[] interfaces = classDef.getInterfaces();
         int typeListPosition = writeTypeList(interfaces);
         idsDefsWriter.writeInt(typeListPosition);
 
-        int sourceFileIndex = in.readInt(); // source file idx
-        if (sourceFileIndex != NO_INDEX) {
-            sourceFileIndex = indexMap.stringIds[sourceFileIndex];
-        }
+        int sourceFileIndex = indexMap.adjustString(
+                classDef.getSourceFileIndex()); // source file idx
         idsDefsWriter.writeInt(sourceFileIndex);
 
-        int annotationsOff = in.readInt(); // annotations off
+        int annotationsOff = classDef.getAnnotationsOffset();
         if (annotationsOff == 0) {
             idsDefsWriter.writeInt(0);
         } else {
-            int position = in.getPosition();
-            in.seek(annotationsOff);
+            DexBuffer.Section annotationsIn = in.open(annotationsOff);
             annotationsDirectoryItemWriter.alignToFourBytes();
-            idsDefsWriter.writeInt(annotationsDirectoryItemWriter.getCursor());
-            transformAnnotations(in, indexMap);
-            in.seek(position);
+            idsDefsWriter.writeInt(annotationsDirectoryItemWriter.getPosition());
+            transformAnnotations(annotationsIn, indexMap);
         }
 
-        int classDataOff = in.readInt(); // class data off
+        int classDataOff = classDef.getClassDataOffset();
         if (classDataOff == 0) {
             idsDefsWriter.writeInt(0);
         } else {
-            int position = in.getPosition();
-            in.seek(classDataOff);
-            idsDefsWriter.writeInt(classDataItemWriter.getCursor());
-            transformClassData(in, indexMap);
-            in.seek(position);
+            DexBuffer.Section classDataIn = in.open(classDataOff);
+            idsDefsWriter.writeInt(classDataItemWriter.getPosition());
+            transformClassData(classDataIn, indexMap);
         }
 
-        int staticValuesOff = in.readInt(); // static values off
+        int staticValuesOff = classDef.getStaticValuesOffset();
         if (staticValuesOff == 0) {
             idsDefsWriter.writeInt(0);
         } else {
-            int position = in.getPosition();
-            in.seek(staticValuesOff);
-            idsDefsWriter.writeInt(encodedArrayItemWriter.getCursor());
-            transformStaticValues(in, indexMap);
-            in.seek(position);
+            DexBuffer.Section staticValuesIn = in.open(staticValuesOff);
+            idsDefsWriter.writeInt(encodedArrayItemWriter.getPosition());
+            transformStaticValues(staticValuesIn, indexMap);
         }
     }
 
@@ -499,13 +487,13 @@
         }
         contentsOut.typeLists.size++;
         typeListWriter.alignToFourBytes();
-        int cursor = typeListWriter.getCursor();
+        int cursor = typeListWriter.getPosition();
         typeListWriter.writeInt(interfaces.length);
         typeListWriter.write(interfaces);
         return cursor;
     }
 
-    private void transformAnnotations(DexReader in, IndexMap indexMap) throws IOException {
+    private void transformAnnotations(DexBuffer.Section in, IndexMap indexMap) throws IOException {
         contentsOut.annotationsDirectories.size++;
 
         // TODO: retain annotations
@@ -521,7 +509,7 @@
         annotationsDirectoryItemWriter.writeInt(0);
     }
 
-    private void transformClassData(DexReader in, IndexMap indexMap) throws IOException {
+    private void transformClassData(DexBuffer.Section in, IndexMap indexMap) throws IOException {
         contentsOut.classDatas.size++;
 
         int staticFieldsSize = in.readUleb128();
@@ -543,13 +531,13 @@
         transformEncodedMethods(in, indexMap, virtualMethodsSize);
     }
 
-    private void transformEncodedFields(DexReader in, IndexMap indexMap, int count)
+    private void transformEncodedFields(DexBuffer.Section in, IndexMap indexMap, int count)
             throws IOException {
         int inFieldIndex = 0;
         int lastOutFieldIndex = 0;
         for (int i = 0; i < count; i++) {
             inFieldIndex += in.readUleb128(); // field idx diff
-            int outFieldIndex = indexMap.fieldIds[inFieldIndex];
+            int outFieldIndex = indexMap.adjustField(inFieldIndex);
             classDataItemWriter.writeUleb128(outFieldIndex - lastOutFieldIndex);
             lastOutFieldIndex = outFieldIndex;
 
@@ -560,13 +548,13 @@
     /**
      * Transforms a list of encoded methods.
      */
-    private void transformEncodedMethods(DexReader in, IndexMap indexMap, int count)
+    private void transformEncodedMethods(DexBuffer.Section in, IndexMap indexMap, int count)
             throws IOException {
         int inMethodIndex = 0;
         int lastOutMethodIndex = 0;
         for (int i = 0; i < count; i++) {
             inMethodIndex += in.readUleb128(); // method idx diff
-            int outMethodIndex = indexMap.methodIds[inMethodIndex];
+            int outMethodIndex = indexMap.adjustMethod(inMethodIndex);
             classDataItemWriter.writeUleb128(outMethodIndex - lastOutMethodIndex);
             lastOutMethodIndex = outMethodIndex;
 
@@ -576,18 +564,14 @@
             if (codeOff == 0) {
                 classDataItemWriter.writeUleb128(0);
             } else {
-                int inPosition = in.getPosition();
                 codeItemWriter.alignToFourBytes();
-                classDataItemWriter.writeUleb128(codeItemWriter.getCursor());
-
-                in.seek(codeOff);
-                transformCodeItem(in, indexMap);
-                in.seek(inPosition);
+                classDataItemWriter.writeUleb128(codeItemWriter.getPosition());
+                transformCodeItem(in.open(codeOff), indexMap);
             }
         }
     }
 
-    private void transformCodeItem(DexReader in, IndexMap indexMap) throws IOException {
+    private void transformCodeItem(DexBuffer.Section in, IndexMap indexMap) throws IOException {
         contentsOut.codes.size++;
         codeItemWriter.assertFourByteAligned();
 
@@ -628,13 +612,13 @@
         }
     }
 
-    private void transformTryItem(DexReader in, IndexMap indexMap) throws IOException {
+    private void transformTryItem(DexBuffer.Section in, IndexMap indexMap) throws IOException {
         codeItemWriter.writeInt(in.readInt()); // start addr
         codeItemWriter.writeShort(in.readShort()); // insn count
         codeItemWriter.writeShort(in.readShort()); // handler off
     }
 
-    private void transformEncodedCatchHandlerList(DexReader in, IndexMap indexMap)
+    private void transformEncodedCatchHandlerList(DexBuffer.Section in, IndexMap indexMap)
             throws IOException {
         int size = in.readUleb128(); // size
         codeItemWriter.writeUleb128(size);
@@ -644,13 +628,14 @@
         }
     }
 
-    private void transformEncodedCatchHandler(DexReader in, IndexMap indexMap) throws IOException {
+    private void transformEncodedCatchHandler(DexBuffer.Section in, IndexMap indexMap)
+            throws IOException {
         int size = in.readSleb128(); // size
         codeItemWriter.writeSleb128(size);
 
         int handlersCount = Math.abs(size);
         for (int i = 0; i < handlersCount; i++) {
-            codeItemWriter.writeUleb128(indexMap.typeIds[in.readUleb128()]); // type idx
+            codeItemWriter.writeUleb128(indexMap.adjustType(in.readUleb128())); // type idx
             codeItemWriter.writeUleb128(in.readUleb128()); // addr
         }
 
@@ -659,7 +644,7 @@
         }
     }
 
-    private void transformStaticValues(DexReader in, IndexMap indexMap) throws IOException {
+    private void transformStaticValues(DexBuffer.Section in, IndexMap indexMap) throws IOException {
         contentsOut.encodedArrays.size++;
         new EncodedValueTransformer(indexMap, in, encodedArrayItemWriter).transformArray();
     }
diff --git a/dx/src/com/android/dx/merge/EncodedValueTransformer.java b/dx/src/com/android/dx/merge/EncodedValueTransformer.java
index aed59e8..68a9cd6 100644
--- a/dx/src/com/android/dx/merge/EncodedValueTransformer.java
+++ b/dx/src/com/android/dx/merge/EncodedValueTransformer.java
@@ -16,18 +16,16 @@
 
 package com.android.dx.merge;
 
-import com.android.dx.util.DexReader;
-import com.android.dx.util.DexWriter;
+import com.android.dx.io.DexBuffer;
 import com.android.dx.util.Unsigned;
 import java.io.IOException;
 
-public final class EncodedValueTransformer {
-
+final class EncodedValueTransformer {
     private final IndexMap indexMap;
-    private final DexReader in;
-    private final DexWriter.Section out;
+    private final DexBuffer.Section in;
+    private final DexBuffer.Section out;
 
-    public EncodedValueTransformer(IndexMap indexMap, DexReader in, DexWriter.Section out) {
+    public EncodedValueTransformer(IndexMap indexMap, DexBuffer.Section in, DexBuffer.Section out) {
         this.indexMap = indexMap;
         this.in = in;
         this.out = out;
@@ -42,13 +40,13 @@
     }
 
     public void transformAnnotation() throws IOException {
-        out.writeUleb128(indexMap.typeIds[in.readUleb128()]); // type idx
+        out.writeUleb128(indexMap.adjustType(in.readUleb128())); // type idx
 
         int size = in.readUleb128(); // size
         out.writeUleb128(size);
 
         for (int i = 0; i < size; i++) {
-            out.writeUleb128(indexMap.stringIds[in.readUleb128()]); // name idx
+            out.writeUleb128(indexMap.adjustString(in.readUleb128())); // name idx
             transformValue();
         }
     }
@@ -73,23 +71,23 @@
 
         case 0x17: // string
             int indexIn = readIndex(in, size);
-            int indexOut = indexMap.stringIds[indexIn];
+            int indexOut = indexMap.adjustString(indexIn);
             writeTypeAndSizeAndIndex(type, indexOut, out);
             break;
         case 0x18: // type
             indexIn = readIndex(in, size);
-            indexOut = indexMap.typeIds[indexIn];
+            indexOut = indexMap.adjustType(indexIn);
             writeTypeAndSizeAndIndex(type, indexOut, out);
             break;
         case 0x19: // field
         case 0x1b: // enum
             indexIn = readIndex(in, size);
-            indexOut = indexMap.fieldIds[indexIn];
+            indexOut = indexMap.adjustField(indexIn);
             writeTypeAndSizeAndIndex(type, indexOut, out);
             break;
         case 0x1a: // method
             indexIn = readIndex(in, size);
-            indexOut = indexMap.methodIds[indexIn];
+            indexOut = indexMap.adjustMethod(indexIn);
             writeTypeAndSizeAndIndex(type, indexOut, out);
             break;
 
@@ -110,7 +108,7 @@
         }
     }
 
-    private int readIndex(DexReader in, int byteCount) throws IOException {
+    private int readIndex(DexBuffer.Section in, int byteCount) throws IOException {
         int result = 0;
         int shift = 0;
         for (int i = 0; i < byteCount; i++) {
@@ -120,7 +118,7 @@
         return result;
     }
 
-    private void writeTypeAndSizeAndIndex(int type, int index, DexWriter.Section out)
+    private void writeTypeAndSizeAndIndex(int type, int index, DexBuffer.Section out)
             throws IOException {
         int byteCount;
         if (Unsigned.compare(index, 0xff) <= 0) {
@@ -141,7 +139,8 @@
         }
     }
 
-    private void copyBytes(DexReader in, DexWriter.Section out, int size) throws IOException {
+    private void copyBytes(DexBuffer.Section in, DexBuffer.Section out, int size)
+            throws IOException {
         for (int i = 0; i < size; i++) {
             out.writeByte(in.readByte());
         }
diff --git a/dx/src/com/android/dx/merge/FieldId.java b/dx/src/com/android/dx/merge/FieldId.java
deleted file mode 100644
index aae94db..0000000
--- a/dx/src/com/android/dx/merge/FieldId.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.dx.merge;
-
-import com.android.dx.util.DexReader;
-import com.android.dx.util.DexWriter;
-import com.android.dx.util.Unsigned;
-import java.io.IOException;
-
-public final class FieldId implements Comparable<FieldId> {
-    private short classIndex;
-    private short typeIndex;
-    private int nameIndex;
-
-    public FieldId(DexReader in) throws IOException {
-        classIndex = in.readShort();
-        typeIndex = in.readShort();
-        nameIndex = in.readInt();
-    }
-
-    /**
-     * Maps from one set of indices to another.
-     */
-    public void adjust(IndexMap indexMap) {
-        classIndex = (short) indexMap.typeIds[classIndex];
-        typeIndex = (short) indexMap.typeIds[typeIndex];
-        nameIndex = (short) indexMap.stringIds[nameIndex];
-    }
-
-    public int compareTo(FieldId other) {
-        if (classIndex != other.classIndex) {
-            return Unsigned.compare(classIndex, other.classIndex);
-        }
-        if (nameIndex != other.nameIndex) {
-            return Unsigned.compare(nameIndex, other.nameIndex);
-        }
-        return Unsigned.compare(typeIndex, other.typeIndex); // should always be 0
-    }
-
-    public void writeTo(DexWriter.Section out) throws IOException {
-        out.writeShort(classIndex);
-        out.writeShort(typeIndex);
-        out.writeInt(nameIndex);
-    }
-}
diff --git a/dx/src/com/android/dx/merge/IndexMap.java b/dx/src/com/android/dx/merge/IndexMap.java
index 1449d94..f12adc7 100644
--- a/dx/src/com/android/dx/merge/IndexMap.java
+++ b/dx/src/com/android/dx/merge/IndexMap.java
@@ -17,6 +17,11 @@
 package com.android.dx.merge;
 
 import com.android.dx.dex.TableOfContents;
+import com.android.dx.io.ClassDef;
+import com.android.dx.io.DexBuffer;
+import com.android.dx.io.FieldId;
+import com.android.dx.io.MethodId;
+import com.android.dx.io.ProtoId;
 
 /**
  * Maps the index offsets from one dex file to those in another. For example, if
@@ -24,23 +29,81 @@
  * {@code strings[5]}.
  */
 public final class IndexMap {
+    private final DexBuffer target;
     public final int[] stringIds;
-    public final int[] typeIds;
-    public final int[] protoIds;
-    public final int[] fieldIds;
-    public final int[] methodIds;
+    public final short[] typeIds;
+    public final short[] protoIds;
+    public final short[] fieldIds;
+    public final short[] methodIds;
 
-    public IndexMap(TableOfContents tableOfContents) {
-        stringIds = new int[tableOfContents.stringIds.size];
-        typeIds = new int[tableOfContents.typeIds.size];
-        protoIds = new int[tableOfContents.protoIds.size];
-        fieldIds = new int[tableOfContents.fieldIds.size];
-        methodIds = new int[tableOfContents.methodIds.size];
+    public IndexMap(DexBuffer target, TableOfContents tableOfContents) {
+        this.target = target;
+        this.stringIds = new int[tableOfContents.stringIds.size];
+        this.typeIds = new short[tableOfContents.typeIds.size];
+        this.protoIds = new short[tableOfContents.protoIds.size];
+        this.fieldIds = new short[tableOfContents.fieldIds.size];
+        this.methodIds = new short[tableOfContents.methodIds.size];
     }
 
-    public void adjustTypeList(short[] typeList) {
+    public int adjustString(int stringIndex) {
+        return stringIndex == ClassDef.NO_INDEX ? ClassDef.NO_INDEX : stringIds[stringIndex];
+    }
+
+    public short adjustType(int typeIndex) {
+        return (typeIndex == ClassDef.NO_INDEX) ? ClassDef.NO_INDEX : typeIds[typeIndex];
+    }
+
+    public short[] adjustTypeList(short[] typeList) {
+        short[] result = new short[typeList.length];
         for (int i = 0; i < typeList.length; i++) {
-            typeList[i] = (short) typeIds[typeList[i]];
+            result[i] = adjustType(typeList[i]);
         }
+        return result;
+    }
+
+    public short adjustProto(int protoIndex) {
+        return protoIds[protoIndex];
+    }
+
+    public short adjustField(int fieldIndex) {
+        return fieldIds[fieldIndex];
+    }
+
+    public short adjustMethod(int methodIndex) {
+        return methodIds[methodIndex];
+    }
+
+    public MethodId adjust(MethodId methodId) {
+        return new MethodId(target,
+                adjustType(methodId.getDeclaringClassIndex()),
+                adjustProto(methodId.getProtoIndex()),
+                adjustString(methodId.getNameIndex()));
+    }
+
+    public FieldId adjust(FieldId fieldId) {
+        return new FieldId(target,
+                adjustType(fieldId.getDeclaringClassIndex()),
+                adjustType(fieldId.getTypeIndex()),
+                adjustString(fieldId.getNameIndex()));
+
+    }
+
+    public ProtoId adjust(ProtoId protoId) {
+        return new ProtoId(target,
+                adjustString(protoId.getShortyIndex()),
+                adjustType(protoId.getReturnTypeIndex()),
+                adjustTypeList(protoId.getParameters()));
+    }
+
+    public ClassDef adjust(ClassDef classDef) {
+        return new ClassDef(target, classDef.getOffset(), adjustType(classDef.getTypeIndex()),
+                classDef.getAccessFlags(), adjustType(classDef.getSupertypeIndex()),
+                classDef.getInterfacesOffset(), adjustTypeList(classDef.getInterfaces()),
+                classDef.getSourceFileIndex(), classDef.getAnnotationsOffset(),
+                classDef.getClassDataOffset(), classDef.getStaticValuesOffset());
+    }
+
+    public SortableType adjust(SortableType sortableType) {
+        return new SortableType(sortableType.getBuffer(), adjust(sortableType.getClassDef()));
     }
 }
diff --git a/dx/src/com/android/dx/merge/InstructionTransformer.java b/dx/src/com/android/dx/merge/InstructionTransformer.java
index c8b84dd..1e0aa49 100644
--- a/dx/src/com/android/dx/merge/InstructionTransformer.java
+++ b/dx/src/com/android/dx/merge/InstructionTransformer.java
@@ -22,7 +22,7 @@
 /**
  * Adjusts a block of instructions to a new index.
  */
-public final class InstructionTransformer {
+final class InstructionTransformer {
 
     private static final Instruction[] INSTRUCTIONS = new Instruction[] {
             // 0x00...0x0f
@@ -342,7 +342,7 @@
         @Override public void transform(short[] instructions, int i, IndexMap indexMap,
                 BitSet skippedInstructions) throws DexException {
             int stringIndex = instructions[i + 1] & 0xFFFF;
-            int mappedIndex = indexMap.stringIds[stringIndex];
+            int mappedIndex = indexMap.adjustString(stringIndex);
             if (mappedIndex > 0xFFFF) {
                 throw new DexException("Cannot convert string to jumbo string!");
             }
@@ -369,7 +369,7 @@
         @Override public void transform(short[] instructions, int i, IndexMap indexMap,
                 BitSet skippedInstructions) throws DexException {
             short field = instructions[i + 1];
-            instructions[i + 1] = (short) indexMap.fieldIds[field];
+            instructions[i + 1] = (short) indexMap.adjustField(field);
         }
     }
 
@@ -380,7 +380,7 @@
         @Override public void transform(short[] instructions, int i, IndexMap indexMap,
                 BitSet skippedInstructions) throws DexException {
             short type = instructions[i + 1];
-            instructions[i + 1] = (short) indexMap.typeIds[type];
+            instructions[i + 1] = (short) indexMap.adjustType(type);
         }
     }
 
@@ -391,7 +391,7 @@
         @Override public void transform(short[] instructions, int i, IndexMap indexMap,
                 BitSet skippedInstructions) throws DexException {
             short method = instructions[i + 1];
-            instructions[i + 1] = (short) indexMap.methodIds[method];
+            instructions[i + 1] = (short) indexMap.adjustMethod(method);
         }
     }
 
diff --git a/dx/src/com/android/dx/merge/MethodId.java b/dx/src/com/android/dx/merge/MethodId.java
deleted file mode 100644
index 0b28204..0000000
--- a/dx/src/com/android/dx/merge/MethodId.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.dx.merge;
-
-import com.android.dx.util.DexReader;
-import com.android.dx.util.DexWriter;
-import com.android.dx.util.Unsigned;
-import java.io.IOException;
-
-public final class MethodId implements Comparable<MethodId> {
-    private short classIndex;
-    private short protoIndex;
-    private int nameIndex;
-
-    public MethodId(DexReader in) throws IOException {
-        classIndex = in.readShort();
-        protoIndex = in.readShort();
-        nameIndex = in.readInt();
-    }
-
-    /**
-     * Maps from one set of indices to another.
-     */
-    public void adjust(IndexMap indexMap) {
-        classIndex = (short) indexMap.typeIds[classIndex];
-        protoIndex = (short) indexMap.protoIds[protoIndex];
-        nameIndex = (short) indexMap.stringIds[nameIndex];
-    }
-
-    public int compareTo(MethodId other) {
-        if (classIndex != other.classIndex) {
-            return Unsigned.compare(classIndex, other.classIndex);
-        }
-        if (nameIndex != other.nameIndex) {
-            return Unsigned.compare(nameIndex, other.nameIndex);
-        }
-        return Unsigned.compare(protoIndex, other.protoIndex);
-    }
-
-    public void writeTo(DexWriter.Section out) throws IOException {
-        out.writeShort(classIndex);
-        out.writeShort(protoIndex);
-        out.writeInt(nameIndex);
-    }
-}
diff --git a/dx/src/com/android/dx/merge/ProtoId.java b/dx/src/com/android/dx/merge/ProtoId.java
deleted file mode 100644
index f18a292..0000000
--- a/dx/src/com/android/dx/merge/ProtoId.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.dx.merge;
-
-import com.android.dx.util.DexReader;
-import com.android.dx.util.DexWriter;
-import com.android.dx.util.Unsigned;
-import java.io.IOException;
-
-public final class ProtoId implements Comparable<ProtoId> {
-    private int shorty;
-    private int returnType;
-    private short[] parameters;
-
-    public ProtoId(DexReader in) throws IOException {
-        shorty = in.readInt();
-        returnType = in.readInt();
-        int parametersOff = in.readInt();
-        parameters = in.readTypeList(parametersOff);
-    }
-
-    /**
-     * Maps from one set of indices to another.
-     */
-    public void adjust(IndexMap indexMap) {
-        shorty = indexMap.stringIds[shorty];
-        returnType = indexMap.typeIds[returnType];
-        for (int i = 0; i < parameters.length; i++) {
-            parameters[i] = (short) indexMap.typeIds[parameters[i]];
-        }
-    }
-
-    public int compareTo(ProtoId other) {
-        if (returnType != other.returnType) {
-            return Unsigned.compare(returnType, other.returnType);
-        }
-        for (int i = 0; i < parameters.length && i < other.parameters.length; i++) {
-            if (parameters[i] != other.parameters[i]) {
-                return Unsigned.compare(parameters[i], other.parameters[i]);
-            }
-        }
-        return Unsigned.compare(parameters.length, other.parameters.length);
-    }
-
-    public short[] getParameters() {
-        return parameters;
-    }
-
-    public void writeTo(DexWriter.Section out, int typeListOffset) throws IOException {
-        out.writeInt(shorty);
-        out.writeInt(returnType);
-        out.writeInt(typeListOffset);
-    }
-}
diff --git a/dx/src/com/android/dx/merge/SortableType.java b/dx/src/com/android/dx/merge/SortableType.java
index 0d8399e..838ea28 100644
--- a/dx/src/com/android/dx/merge/SortableType.java
+++ b/dx/src/com/android/dx/merge/SortableType.java
@@ -16,15 +16,15 @@
 
 package com.android.dx.merge;
 
-import com.android.dx.util.DexReader;
-import java.io.IOException;
+import com.android.dx.io.ClassDef;
+import com.android.dx.io.DexBuffer;
 import java.util.Comparator;
 
 /**
  * Name and structure of a type. Used to order types such that each type is
  * preceded by its supertype and implemented interfaces.
  */
-public final class SortableType {
+final class SortableType {
     public static final Comparator<SortableType> NULLS_LAST_ORDER = new Comparator<SortableType>() {
         public int compare(SortableType a, SortableType b) {
             if (a == b) {
@@ -39,46 +39,29 @@
             if (a.depth != b.depth) {
                 return a.depth - b.depth;
             }
-            return a.type - b.type;
+            return a.getTypeIndex() - b.getTypeIndex();
         }
     };
 
-    private final DexReader reader;
-    private final int offset;
-    private int type;
-    private int supertype;
-    private final short[] interfaces;
+    private final DexBuffer buffer;
+    private ClassDef classDef;
     private int depth = -1;
 
-    public SortableType(DexReader reader) throws IOException {
-        this.reader = reader;
-        this.offset = reader.getPosition();
-        this.type = reader.readInt(); // class idx
-        reader.readInt(); // access flags
-        this.supertype = reader.readInt(); // superclass idx
-        int interfacesOff = reader.readInt(); // interface off
-        this.interfaces = reader.readTypeList(interfacesOff);
-        reader.readInt(); // source file index
-        reader.readInt(); // annotations off
-        reader.readInt(); // class data off
-        reader.readInt(); // static values off
+    public SortableType(DexBuffer buffer, ClassDef classDef) {
+        this.buffer = buffer;
+        this.classDef = classDef;
     }
 
-    public void adjust(IndexMap indexMap) {
-        type = indexMap.typeIds[type];
-        if (supertype != DexMerger.NO_INDEX) {
-            supertype = indexMap.typeIds[supertype];
-        }
-        indexMap.adjustTypeList(interfaces);
+    public DexBuffer getBuffer() {
+        return buffer;
     }
 
-    public DexReader prepareReader() throws IOException {
-        reader.seek(offset);
-        return reader;
+    public ClassDef getClassDef() {
+        return classDef;
     }
 
-    public int getType() {
-        return type;
+    public int getTypeIndex() {
+        return classDef.getTypeIndex();
     }
 
     /**
@@ -88,10 +71,10 @@
      */
     public boolean tryAssignDepth(SortableType[] types) {
         int max;
-        if (supertype == DexMerger.NO_INDEX) {
+        if (classDef.getSupertypeIndex() == ClassDef.NO_INDEX) {
             max = 0; // this is Object.class or an interface
         } else {
-            SortableType sortableSupertype = types[supertype];
+            SortableType sortableSupertype = types[classDef.getSupertypeIndex()];
             if (sortableSupertype == null) {
                 max = 1; // unknown, so assume it's a root.
             } else if (sortableSupertype.depth == -1) {
@@ -101,7 +84,7 @@
             }
         }
 
-        for (short interfaceIndex : interfaces) {
+        for (short interfaceIndex : classDef.getInterfaces()) {
             SortableType implemented = types[interfaceIndex];
             if (implemented == null) {
                 max = Math.max(max, 1); // unknown, so assume it's a root.
diff --git a/dx/src/com/android/dx/print/DexIndexPrinter.java b/dx/src/com/android/dx/print/DexIndexPrinter.java
deleted file mode 100644
index 331f514..0000000
--- a/dx/src/com/android/dx/print/DexIndexPrinter.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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.dx.print;
-
-import com.android.dx.dex.SizeOf;
-import com.android.dx.util.DexReader;
-import com.android.dx.dex.TableOfContents;
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Executable that prints all indices of a dex file.
- */
-public final class DexIndexPrinter implements Closeable {
-    private final DexReader dexReader;
-    private final TableOfContents tableOfContents;
-
-    public DexIndexPrinter(File file) throws IOException {
-        this.dexReader = new DexReader(file);
-        this.tableOfContents = dexReader.getTableOfContents();
-    }
-
-    private void printMap() {
-        for (TableOfContents.Section section : tableOfContents.sections) {
-            if (section.off != -1) {
-                System.out.println("section " + Integer.toHexString(section.type)
-                        + " off=" + Integer.toHexString(section.off)
-                        + " size=" + Integer.toHexString(section.size)
-                        + " byteCount=" + Integer.toHexString(section.byteCount));
-            }
-        }
-    }
-
-    private void printStrings() throws IOException {
-        for (int i = 0; i < tableOfContents.stringIds.size; i++) {
-            String s = dexReader.readString(i);
-            System.out.println("string " + i + ": " + s);
-        }
-    }
-
-    private void printTypeIds() throws IOException {
-        dexReader.seek(tableOfContents.typeIds.off);
-        for (int i = 0; i < tableOfContents.typeIds.size; i++) {
-            int stringIndex = dexReader.readInt();
-            System.out.println("type " + i + ": " + dexReader.readString(stringIndex));
-        }
-    }
-
-    private String readProto(int protoIndex) throws IOException {
-        int position = dexReader.getPosition();
-        dexReader.seek(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex));
-        int shortyIdx = dexReader.readInt(); // string ID
-        int returnTypeIdx = dexReader.readInt(); //type ID
-        int parametersOff = dexReader.readInt(); // type list, or 0 for no parameters
-        String shorty = dexReader.readString(shortyIdx);
-        String returnType = readType(returnTypeIdx);
-        StringBuilder result = new StringBuilder()
-                .append(shorty)
-                .append(": ")
-                .append(returnType)
-                .append(" (");
-
-        if (parametersOff != 0) {
-            dexReader.seek(parametersOff);
-            int typeListSize = dexReader.readInt();
-            for (int j = 0; j < typeListSize; j++) {
-                if (j > 0) {
-                    result.append(", ");
-                }
-                dexReader.seek(parametersOff + 4 + (j * SizeOf.TYPE_ITEM));
-                int typeIndex = dexReader.readShort();
-                result.append(readType(typeIndex));
-            }
-        }
-        result.append(")");
-        dexReader.seek(position);
-        return result.toString();
-    }
-
-    private void printProtoIds() throws IOException {
-        for (int i = 0; i < tableOfContents.protoIds.size; i++) {
-            String proto = readProto(i);
-            System.out.println("proto " + i + ": " + proto);
-        }
-        dexReader.seek(tableOfContents.protoIds.off
-                + (SizeOf.PROTO_ID_ITEM * tableOfContents.protoIds.size));
-    }
-
-    private void printFieldIds() throws IOException {
-        for (int i = 0; i < tableOfContents.fieldIds.size; i++) {
-            int declaringClass = dexReader.readShort();
-            int type = dexReader.readShort();
-            int nameIndex = dexReader.readInt();
-            System.out.println("field " + i + ": " + readType(declaringClass)
-                    + " { " + readType(type) + " " + dexReader.readString(nameIndex) + " }");
-        }
-    }
-
-    private void printMethodIds() throws IOException {
-        for (int i = 0; i < tableOfContents.methodIds.size; i++) {
-            int declaringClass = dexReader.readShort();
-            int protoIndex = dexReader.readShort();
-            int name = dexReader.readInt();
-            String proto = readProto(protoIndex);
-            System.out.println("method " + i + ": " + readType(declaringClass)
-                    + " " + proto + " " + dexReader.readString(name));
-        }
-    }
-
-    private void printTypeLists() throws IOException {
-        if (tableOfContents.typeLists.off == -1) {
-            System.out.println("No type lists");
-            return;
-        }
-        int position = dexReader.getPosition();
-        dexReader.seek(tableOfContents.typeLists.off);
-        for (int i = 0; i < tableOfContents.typeLists.size; i++) {
-            int size = dexReader.readInt();
-            System.out.print("Type list i=" + i + ", size=" + size + ", elements=");
-            for (int t = 0; t < size; t++) {
-                System.out.print(" " + readType(dexReader.readShort()));
-            }
-            if (size % 2 == 1) {
-                dexReader.readShort(); // retain alignment
-            }
-            System.out.println();
-        }
-        dexReader.seek(position);
-    }
-
-    private String readType(int index) throws IOException {
-        if (index < 0 || index >= tableOfContents.typeIds.size) {
-            throw new IllegalArgumentException("type index out of range: "
-                    + index + " " + tableOfContents.typeIds.size);
-        }
-        int position = dexReader.getPosition();
-        dexReader.seek(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM));
-        int stringIndex = dexReader.readInt();
-        String result = dexReader.readString(stringIndex);
-        dexReader.seek(position);
-        return result;
-    }
-
-    public void close() throws IOException {
-        dexReader.close();
-    }
-
-    public static void main(String[] args) throws IOException {
-        DexIndexPrinter indexPrinter = new DexIndexPrinter(new File(args[0]));
-        indexPrinter.printMap();
-        indexPrinter.printStrings();
-        indexPrinter.printTypeIds();
-        indexPrinter.printProtoIds();
-        indexPrinter.printFieldIds();
-        indexPrinter.printMethodIds();
-        indexPrinter.printTypeLists();
-    }
-}
diff --git a/dx/src/com/android/dx/util/DexReader.java b/dx/src/com/android/dx/util/DexReader.java
deleted file mode 100644
index ed8a68a..0000000
--- a/dx/src/com/android/dx/util/DexReader.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * 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.dx.util;
-
-import com.android.dx.dex.DexException;
-import com.android.dx.dex.SizeOf;
-import com.android.dx.dex.TableOfContents;
-import java.io.Closeable;
-import java.io.DataInput;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.Arrays;
-
-/**
- * All int offsets are unsigned.
- */
-public final class DexReader implements Closeable {
-    private final String name;
-    private final byte[] fileContents;
-    private final TableOfContents tableOfContents;
-    private int position = 0;
-
-    private final DataInput asDataInput = new DataInputStub() {
-        public byte readByte() throws IOException {
-            return DexReader.this.readByte();
-        }
-    };
-
-    /**
-     * Creates a new DexReader that reads ints and shorts in little-endian byte
-     * order.
-     */
-    public DexReader(File file) throws IOException {
-        name = file.getPath();
-
-        FileInputStream in = new FileInputStream(file);
-        int length = (int) file.length();
-        fileContents = new byte[length];
-
-        int count = 0;
-        while (count < length) {
-            int bytesRead = in.read(fileContents, count, length - count);
-            if (bytesRead == -1) {
-                throw new IOException("Expected " + length + " bytes but was " + count);
-            }
-            count += bytesRead;
-        }
-        in.close();
-
-        tableOfContents = new TableOfContents(this);
-    }
-
-    public TableOfContents getTableOfContents() {
-        return tableOfContents;
-    }
-
-    public int getPosition() throws IOException {
-        return position;
-    }
-
-    public void seek(int offset) throws IOException {
-        position = offset;
-    }
-
-    public void close() throws IOException {}
-
-    public int readInt() throws IOException {
-        int result = (fileContents[position] & 0xff)
-                | (fileContents[position + 1] & 0xff) << 8
-                | (fileContents[position + 2] & 0xff) << 16
-                | (fileContents[position + 3] & 0xff) << 24;
-        position += 4;
-        return result;
-    }
-
-    public short readShort() throws IOException {
-        int result = (fileContents[position] & 0xff)
-                | (fileContents[position + 1] & 0xff) << 8;
-        position += 2;
-        return (short) result;
-    }
-
-    public byte readByte() throws IOException {
-        return (byte) (fileContents[position++] & 0xff);
-    }
-
-    public byte[] readByteArray(int length) throws IOException {
-        byte[] result = Arrays.copyOfRange(fileContents, position, position + length);
-        position += length;
-        return result;
-    }
-
-    public short[] readShortArray(int length) throws IOException {
-        short[] result = new short[length];
-        for (int i = 0; i < length; i++) {
-            result[i] = readShort();
-        }
-        return result;
-    }
-
-    public int readUleb128() throws IOException {
-        return Leb128Utils.readUnsignedLeb128(asDataInput);
-    }
-
-    public int readSleb128() throws IOException {
-        return Leb128Utils.readSignedLeb128(asDataInput);
-    }
-
-    public short[] readTypeList(int offset) throws IOException {
-        if (offset == 0) {
-            return new short[0];
-        }
-        int savedPosition = position;
-        position = offset;
-        int size = readInt();
-        short[] parameters = new short[size];
-        for (int i = 0; i < size; i++) {
-            parameters[i] = readShort();
-        }
-        position = savedPosition;
-        return parameters;
-    }
-
-    public String readStringDataItem() throws IOException {
-        int expectedLength = readUleb128();
-        String result = Mutf8.decode(asDataInput, new char[expectedLength]);
-        if (result.length() != expectedLength) {
-            throw new DexException("Declared length " + expectedLength + " doesn't match decoded "
-                    + "length of " + result.length());
-        }
-        return result;
-    }
-
-    /**
-     * Reads a string at the given index. This method does not disturb the seek position.
-     */
-    public String readString(int index) throws IOException {
-        int savedPosition = position;
-        seek(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM));
-        int stringDataOff = readInt();
-        seek(stringDataOff);
-        String result = readStringDataItem();
-        position = savedPosition;
-        return result;
-    }
-
-    @Override public String toString() {
-        return name;
-    }
-
-    private static class DataInputStub implements DataInput {
-        public byte readByte() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public void readFully(byte[] buffer) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public void readFully(byte[] buffer, int offset, int count) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public int skipBytes(int i) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public boolean readBoolean() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public int readUnsignedByte() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public short readShort() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public int readUnsignedShort() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public char readChar() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public int readInt() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public long readLong() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public float readFloat() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public double readDouble() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public String readLine() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-        public String readUTF() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-    }
-}
diff --git a/dx/src/com/android/dx/util/DexWriter.java b/dx/src/com/android/dx/util/DexWriter.java
deleted file mode 100644
index 2651d4d..0000000
--- a/dx/src/com/android/dx/util/DexWriter.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * 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.dx.util;
-
-import com.android.dx.dex.DexException;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Writes a dex file in sections.
- */
-public final class DexWriter implements Closeable {
-    private final List<Section> sections = new ArrayList<Section>();
-    private final RandomAccessFile randomAccessFile;
-    private int length;
-
-    /**
-     * Creates a new DexWriter that writes ints and shorts in little-endian byte
-     * order.
-     */
-    public DexWriter(File file) throws FileNotFoundException {
-        this.randomAccessFile = new RandomAccessFile(file, "rw");
-    }
-
-    public Section newSection(int maxByteCount, String name) throws IOException {
-        Section result = new Section(name, length, maxByteCount);
-        sections.add(result);
-        length = fourByteAlign(length + maxByteCount);
-        return result;
-    }
-
-    public int getLength() {
-        return length;
-    }
-
-    public void close() throws IOException {
-        for (Section s : sections) {
-            s.flush();
-        }
-        randomAccessFile.setLength(length);
-        randomAccessFile.close();
-    }
-
-    public static int fourByteAlign(int position) {
-        return (position + 3) & ~3;
-    }
-
-    public final class Section {
-        private final String name;
-        private final int offset;
-        private final int maxByteCount;
-        private final byte[] buffer;
-        private int bufferedByteCount;
-        private int byteCount;
-
-        private Section(String name, int offset, int maxByteCount) {
-            this.name = name;
-            this.offset = offset;
-            this.maxByteCount = maxByteCount;
-            this.buffer = new byte[Math.min(8192, maxByteCount)];
-        }
-
-        public int getCursor() throws IOException {
-            return offset + byteCount + bufferedByteCount;
-        }
-
-        public void flush() throws IOException {
-            if (bufferedByteCount == 0) {
-                return;
-            }
-            if (byteCount + bufferedByteCount > maxByteCount) {
-                throw new DexException("Expected size " + maxByteCount
-                        + " exceeded by " + name + ": " + byteCount + bufferedByteCount);
-            }
-            randomAccessFile.seek(offset + byteCount);
-            randomAccessFile.write(buffer, 0, bufferedByteCount);
-            byteCount += bufferedByteCount;
-            bufferedByteCount = 0;
-        }
-
-        private void ensureCapacity(int byteCount) throws IOException {
-            if (bufferedByteCount + byteCount > buffer.length) {
-                flush();
-            }
-        }
-
-        /**
-         * Writes 0x00 until the position is aligned to a multiple of 4.
-         */
-        public void alignToFourBytes() throws IOException {
-            int unalignedCount = bufferedByteCount;
-            bufferedByteCount = DexWriter.fourByteAlign(byteCount + bufferedByteCount) - byteCount;
-            for (int i = unalignedCount; i < bufferedByteCount; i++) {
-                buffer[i] = 0;
-            }
-        }
-
-        public void assertFourByteAligned() throws IOException {
-            if ((getCursor() & 3) != 0) {
-                throw new IllegalStateException("Not four byte aligned!");
-            }
-        }
-
-        public void write(byte[] bytes) throws IOException {
-            int offset = 0;
-            while (offset < bytes.length) {
-                ensureCapacity(1);
-                int toCopy = Math.min(bytes.length - offset, buffer.length - bufferedByteCount);
-                System.arraycopy(bytes, offset, buffer, bufferedByteCount, toCopy);
-                bufferedByteCount += toCopy;
-                offset += toCopy;
-            }
-        }
-
-        public void writeByte(int b) throws IOException {
-            ensureCapacity(1);
-            buffer[bufferedByteCount] = (byte) b;
-            bufferedByteCount++;
-        }
-
-        public void writeShort(short i) throws IOException {
-            ensureCapacity(2);
-            buffer[bufferedByteCount    ] = (byte) i;
-            buffer[bufferedByteCount + 1] = (byte) (i >>> 8);
-            bufferedByteCount += 2;
-        }
-
-        public void write(short[] shorts) throws IOException {
-            for (short s : shorts) {
-                writeShort(s);
-            }
-        }
-
-        public void writeInt(int i) throws IOException {
-            ensureCapacity(4);
-            buffer[bufferedByteCount    ] = (byte) i;
-            buffer[bufferedByteCount + 1] = (byte) (i >>>  8);
-            buffer[bufferedByteCount + 2] = (byte) (i >>> 16);
-            buffer[bufferedByteCount + 3] = (byte) (i >>> 24);
-            bufferedByteCount += 4;
-        }
-
-        public void writeUleb128(int i) throws IOException {
-            ensureCapacity(5);
-            bufferedByteCount += Leb128Utils.writeUnsignedLeb128(buffer, bufferedByteCount, i);
-        }
-
-        public void writeSleb128(int i) throws IOException {
-            ensureCapacity(5);
-            bufferedByteCount += Leb128Utils.writeSignedLeb128(buffer, bufferedByteCount, i);
-        }
-
-        public void writeStringDataItem(String value) throws IOException {
-            int length = value.length();
-            writeUleb128(length);
-            write(Mutf8.encode(value));
-            writeByte(0);
-        }
-    }
-}