summaryrefslogtreecommitdiff
path: root/dx/src/com/android/dx/dex/TableOfContents.java
diff options
context:
space:
mode:
Diffstat (limited to 'dx/src/com/android/dx/dex/TableOfContents.java')
-rw-r--r--dx/src/com/android/dx/dex/TableOfContents.java238
1 files changed, 238 insertions, 0 deletions
diff --git a/dx/src/com/android/dx/dex/TableOfContents.java b/dx/src/com/android/dx/dex/TableOfContents.java
new file mode 100644
index 0000000..877dddb
--- /dev/null
+++ b/dx/src/com/android/dx/dex/TableOfContents.java
@@ -0,0 +1,238 @@
+/*
+ * 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.dex;
+
+import com.android.dx.io.DexBuffer;
+import com.android.dx.util.DexException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+/**
+ * The file header and map.
+ */
+public final class TableOfContents {
+
+ /*
+ * TODO: factor out ID constants.
+ */
+
+ public final Section header = new Section(0x0000);
+ public final Section stringIds = new Section(0x0001);
+ public final Section typeIds = new Section(0x0002);
+ public final Section protoIds = new Section(0x0003);
+ public final Section fieldIds = new Section(0x0004);
+ public final Section methodIds = new Section(0x0005);
+ public final Section classDefs = new Section(0x0006);
+ public final Section mapList = new Section(0x1000);
+ public final Section typeLists = new Section(0x1001);
+ public final Section annotationSetRefLists = new Section(0x1002);
+ public final Section annotationSets = new Section(0x1003);
+ public final Section classDatas = new Section(0x2000);
+ public final Section codes = new Section(0x2001);
+ public final Section stringDatas = new Section(0x2002);
+ public final Section debugInfos = new Section(0x2003);
+ public final Section annotations = new Section(0x2004);
+ public final Section encodedArrays = new Section(0x2005);
+ public final Section annotationsDirectories = new Section(0x2006);
+ public final Section[] sections = {
+ header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList,
+ typeLists, annotationSetRefLists, annotationSets, classDatas, codes, stringDatas,
+ debugInfos, annotations, encodedArrays, annotationsDirectories
+ };
+
+ public int checksum;
+ public byte[] signature;
+ public int fileSize;
+ public int linkSize;
+ public int linkOff;
+ public int dataSize;
+ public int dataOff;
+
+ public TableOfContents() {
+ signature = new byte[20];
+ }
+
+ public void readFrom(DexBuffer buffer) throws IOException {
+ readHeader(buffer.open(0));
+ readMap(buffer.open(mapList.off));
+ computeSizesFromOffsets();
+ }
+
+ private void readHeader(DexBuffer.Section headerIn) throws UnsupportedEncodingException {
+ byte[] magic = headerIn.readByteArray(8);
+ int apiTarget = DexFormat.magicToApi(magic);
+
+ if (apiTarget < 0) {
+ throw new DexException("Unexpected magic: " + Arrays.toString(magic));
+ }
+
+ checksum = headerIn.readInt();
+ signature = headerIn.readByteArray(20);
+ fileSize = headerIn.readInt();
+ int headerSize = headerIn.readInt();
+ if (headerSize != SizeOf.HEADER_ITEM) {
+ throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize));
+ }
+ int endianTag = headerIn.readInt();
+ if (endianTag != DexFormat.ENDIAN_TAG) {
+ throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag));
+ }
+ linkSize = headerIn.readInt();
+ linkOff = headerIn.readInt();
+ mapList.off = headerIn.readInt();
+ if (mapList.off == 0) {
+ throw new DexException("Cannot merge dex files that do not contain a map");
+ }
+ stringIds.size = headerIn.readInt();
+ stringIds.off = headerIn.readInt();
+ typeIds.size = headerIn.readInt();
+ typeIds.off = headerIn.readInt();
+ protoIds.size = headerIn.readInt();
+ protoIds.off = headerIn.readInt();
+ fieldIds.size = headerIn.readInt();
+ fieldIds.off = headerIn.readInt();
+ methodIds.size = headerIn.readInt();
+ methodIds.off = headerIn.readInt();
+ classDefs.size = headerIn.readInt();
+ classDefs.off = headerIn.readInt();
+ dataSize = headerIn.readInt();
+ dataOff = headerIn.readInt();
+ }
+
+ private void readMap(DexBuffer.Section in) throws IOException {
+ int mapSize = in.readInt();
+ Section previous = null;
+ for (int i = 0; i < mapSize; i++) {
+ short type = in.readShort();
+ in.readShort(); // unused
+ Section section = getSection(type);
+ int size = in.readInt();
+ int offset = in.readInt();
+
+ if ((section.size != 0 && section.size != size)
+ || (section.off != -1 && section.off != offset)) {
+ throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type));
+ }
+
+ section.size = size;
+ section.off = offset;
+
+ if (previous != null && previous.off > section.off) {
+ throw new DexException("Map is unsorted at " + previous + ", " + section);
+ }
+
+ previous = section;
+ }
+ Arrays.sort(sections);
+ }
+
+ public void computeSizesFromOffsets() {
+ int end = dataOff + dataSize;
+ for (int i = sections.length - 1; i >= 0; i--) {
+ Section section = sections[i];
+ if (section.off == -1) {
+ continue;
+ }
+ if (section.off > end) {
+ throw new DexException("Map is unsorted at " + section);
+ }
+ section.byteCount = end - section.off;
+ end = section.off;
+ }
+ }
+
+ private Section getSection(short type) {
+ for (Section section : sections) {
+ if (section.type == type) {
+ return section;
+ }
+ }
+ throw new IllegalArgumentException("No such map item: " + type);
+ }
+
+ public void writeHeader(DexBuffer.Section out) throws IOException {
+ out.write(DexFormat.apiToMagic(DexFormat.API_CURRENT).getBytes("UTF-8"));
+ out.writeInt(checksum);
+ out.write(signature);
+ out.writeInt(fileSize);
+ out.writeInt(SizeOf.HEADER_ITEM);
+ out.writeInt(DexFormat.ENDIAN_TAG);
+ out.writeInt(linkSize);
+ out.writeInt(linkOff);
+ out.writeInt(mapList.off);
+ out.writeInt(stringIds.size);
+ out.writeInt(stringIds.off);
+ out.writeInt(typeIds.size);
+ out.writeInt(typeIds.off);
+ out.writeInt(protoIds.size);
+ out.writeInt(protoIds.off);
+ out.writeInt(fieldIds.size);
+ out.writeInt(fieldIds.off);
+ out.writeInt(methodIds.size);
+ out.writeInt(methodIds.off);
+ out.writeInt(classDefs.size);
+ out.writeInt(classDefs.off);
+ out.writeInt(dataSize);
+ out.writeInt(dataOff);
+ }
+
+ public void writeMap(DexBuffer.Section out) throws IOException {
+ int count = 0;
+ for (Section section : sections) {
+ if (section.exists()) {
+ count++;
+ }
+ }
+
+ out.writeInt(count);
+ for (Section section : sections) {
+ if (section.exists()) {
+ out.writeShort(section.type);
+ out.writeShort((short) 0);
+ out.writeInt(section.size);
+ out.writeInt(section.off);
+ }
+ }
+ }
+
+ public static class Section implements Comparable<Section> {
+ public final short type;
+ public int size = 0;
+ public int off = -1;
+ public int byteCount = 0;
+
+ public Section(int type) {
+ this.type = (short) type;
+ }
+
+ public boolean exists() {
+ return size > 0;
+ }
+
+ public int compareTo(Section section) {
+ if (off != section.off) {
+ return off < section.off ? -1 : 1;
+ }
+ return 0;
+ }
+
+ @Override public String toString() {
+ return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size);
+ }
+ }
+}