auto import from //depot/cupcake/@135843
diff --git a/libdex/DexFile.c b/libdex/DexFile.c
new file mode 100644
index 0000000..2639d7b
--- /dev/null
+++ b/libdex/DexFile.c
@@ -0,0 +1,1183 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Access the contents of a .dex file.
+ */
+
+#include "DexFile.h"
+#include "DexProto.h"
+#include "Leb128.h"
+#include "sha1.h"
+#include "ZipArchive.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/*
+ * Verifying checksums is good, but it slows things down and causes us to
+ * touch every page.  In the "optimized" world, it doesn't work at all,
+ * because we rewrite the contents.
+ */
+static const bool kVerifyChecksum = false;
+static const bool kVerifySignature = false;
+
+
+/* Compare two '\0'-terminated modified UTF-8 strings, using Unicode
+ * code point values for comparison. This treats different encodings
+ * for the same code point as equivalent, except that only a real '\0'
+ * byte is considered the string terminator. The return value is as
+ * for strcmp(). */
+int dexUtf8Cmp(const char* s1, const char* s2) {
+    for (;;) {
+        if (*s1 == '\0') {
+            if (*s2 == '\0') {
+                return 0;
+            }
+            return -1;
+        } else if (*s2 == '\0') {
+            return 1;
+        }
+
+        int utf1 = dexGetUtf16FromUtf8(&s1);
+        int utf2 = dexGetUtf16FromUtf8(&s2);
+        int diff = utf1 - utf2;
+
+        if (diff != 0) {
+            return diff;
+        }
+    }
+}
+
+/* for dexIsValidMemberNameUtf8(), a bit vector indicating valid low ascii */
+u4 DEX_MEMBER_VALID_LOW_ASCII[4] = {
+    0x00000000, // 00..1f low control characters; nothing valid
+    0x03ff2010, // 20..3f digits and symbols; valid: '0'..'9', '$', '-'
+    0x87fffffe, // 40..5f uppercase etc.; valid: 'A'..'Z', '_'
+    0x07fffffe  // 60..7f lowercase etc.; valid: 'a'..'z'
+};
+
+/* Helper for dexIsValidMemberNameUtf8(); do not call directly. */
+bool dexIsValidMemberNameUtf8_0(const char** pUtf8Ptr) {
+    /*
+     * It's a multibyte encoded character. Decode it and analyze. We
+     * accept anything that isn't (a) an improperly encoded low value,
+     * (b) an improper surrogate pair, (c) an encoded '\0', (d) a high
+     * control character, or (e) a high space, layout, or special
+     * character (U+00a0, U+2000..U+200f, U+2028..U+202f,
+     * U+fff0..U+ffff).
+     */
+    
+    u2 utf16 = dexGetUtf16FromUtf8(pUtf8Ptr);
+
+    // Perform follow-up tests based on the high 8 bits.
+    switch (utf16 >> 8) {
+        case 0x00: {
+            // It's only valid if it's above the ISO-8859-1 high space (0xa0).
+            return (utf16 > 0x00a0);
+        }
+        case 0xd8:
+        case 0xd9:
+        case 0xda:
+        case 0xdb: {
+            /*
+             * It's a leading surrogate. Check to see that a trailing
+             * surrogate follows.
+             */
+            utf16 = dexGetUtf16FromUtf8(pUtf8Ptr);
+            return (utf16 >= 0xdc00) && (utf16 <= 0xdfff);
+        }
+        case 0xdc:
+        case 0xdd:
+        case 0xde:
+        case 0xdf: {
+            // It's a trailing surrogate, which is not valid at this point.
+            return false;
+        }
+        case 0x20:
+        case 0xff: {
+            // It's in the range that has spaces, controls, and specials.
+            switch (utf16 & 0xfff8) {
+                case 0x2000:
+                case 0x2008:
+                case 0x2028:
+                case 0xfff0:
+                case 0xfff8: {
+                    return false;
+                }
+            }
+            break;
+        }
+    }
+
+    return true;
+}
+
+/* Return whether the given string is a valid field or method name. */
+bool dexIsValidMemberName(const char* s) {
+    bool angleName = false;
+    
+    switch (*s) {
+        case '\0': {
+            // The empty string is not a valid name.
+            return false;
+        }
+        case '<': {
+            /*
+             * '<' is allowed only at the start of a name, and if present,
+             * means that the name must end with '>'.
+             */
+            angleName = true;
+            s++;
+            break;
+        }
+    }
+
+    for (;;) {
+        switch (*s) {
+            case '\0': {
+                return !angleName;
+            }
+            case '>': {
+                return angleName && s[1] == '\0';
+            }
+        }
+        if (!dexIsValidMemberNameUtf8(&s)) {
+            return false;
+        }
+    }
+}
+
+/* Return whether the given string is a valid type descriptor. */
+bool dexIsValidTypeDescriptor(const char* s) {
+    int arrayCount = 0;
+
+    while (*s == '[') {
+        arrayCount++;
+        s++;
+    }
+
+    if (arrayCount > 255) {
+        // Arrays may have no more than 255 dimensions.
+        return false;
+    }
+    
+    switch (*(s++)) {
+        case 'B': 
+        case 'C':
+        case 'D':
+        case 'F':
+        case 'I':
+        case 'J':
+        case 'S':
+        case 'Z': {
+            // These are all single-character descriptors for primitive types.
+            return (*s == '\0');
+        }
+        case 'V': {
+            // You can't have an array of void.
+            return (arrayCount == 0) && (*s == '\0');
+        }
+        case 'L': {
+            // Break out and continue below.
+            break;
+        }
+        default: {
+            // Oddball descriptor character.
+            return false;
+        }
+    }
+
+    // We just consumed the 'L' that introduces a class name.
+
+    bool slashOrFirst = true; // first character or just encountered a slash
+    for (;;) {
+        u1 c = (u1) *s;
+        switch (c) {
+            case '\0': {
+                // Premature end.
+                return false;
+            }
+            case ';': {
+                /*
+                 * Make sure that this is the end of the string and that
+                 * it doesn't end with an empty component (including the
+                 * degenerate case of "L;").
+                 */
+                return (s[1] == '\0') && !slashOrFirst;
+            }
+            case '/': {
+                if (slashOrFirst) {
+                    // Slash at start or two slashes in a row.
+                    return false;
+                }
+                slashOrFirst = true;
+                s++;
+                break;
+            }
+            default: {
+                if (!dexIsValidMemberNameUtf8(&s)) {
+                    return false;
+                }
+                slashOrFirst = false;
+                break;
+            }
+        }
+    }
+}
+
+/* Return whether the given string is a valid reference descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class or array and not a primitive type. */
+bool dexIsReferenceDescriptor(const char* s) {
+    if (!dexIsValidTypeDescriptor(s)) {
+        return false;
+    }
+
+    return (s[0] == 'L') || (s[0] == '[');
+}
+
+/* Return whether the given string is a valid class descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class and not an array or primitive type. */
+bool dexIsClassDescriptor(const char* s) {
+    if (!dexIsValidTypeDescriptor(s)) {
+        return false;
+    }
+
+    return s[0] == 'L';
+}
+
+/* Return whether the given string is a valid field type descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for anything but "void". */
+bool dexIsFieldDescriptor(const char* s) {
+    if (!dexIsValidTypeDescriptor(s)) {
+        return false;
+    }
+
+    return s[0] != 'V';
+}
+
+/* Return the UTF-8 encoded string with the specified string_id index,
+ * also filling in the UTF-16 size (number of 16-bit code points).*/
+const char* dexStringAndSizeById(const DexFile* pDexFile, u4 idx,
+        u4* utf16Size) {
+    const DexStringId* pStringId = dexGetStringId(pDexFile, idx);
+    const u1* ptr = pDexFile->baseAddr + pStringId->stringDataOff;
+
+    *utf16Size = readUnsignedLeb128(&ptr);
+    return (const char*) ptr;
+}
+
+/*
+ * Format an SHA-1 digest for printing.  tmpBuf must be able to hold at
+ * least kSHA1DigestOutputLen bytes.
+ */
+const char* dvmSHA1DigestToStr(const unsigned char digest[], char* tmpBuf);
+
+/*
+ * Compute a SHA-1 digest on a range of bytes.
+ */
+static void dexComputeSHA1Digest(const unsigned char* data, size_t length,
+    unsigned char digest[])
+{
+    SHA1_CTX context;
+    SHA1Init(&context);
+    SHA1Update(&context, data, length);
+    SHA1Final(digest, &context);
+}
+
+/*
+ * Format the SHA-1 digest into the buffer, which must be able to hold at
+ * least kSHA1DigestOutputLen bytes.  Returns a pointer to the buffer,
+ */
+static const char* dexSHA1DigestToStr(const unsigned char digest[],char* tmpBuf)
+{
+    static const char hexDigit[] = "0123456789abcdef";
+    char* cp;
+    int i;
+
+    cp = tmpBuf;
+    for (i = 0; i < kSHA1DigestLen; i++) {
+        *cp++ = hexDigit[digest[i] >> 4];
+        *cp++ = hexDigit[digest[i] & 0x0f];
+    }
+    *cp++ = '\0';
+
+    assert(cp == tmpBuf + kSHA1DigestOutputLen);
+
+    return tmpBuf;
+}
+
+/*
+ * Compute a hash code on a UTF-8 string, for use with internal hash tables.
+ *
+ * This may or may not be compatible with UTF-8 hash functions used inside
+ * the Dalvik VM.
+ *
+ * The basic "multiply by 31 and add" approach does better on class names
+ * than most other things tried (e.g. adler32).
+ */
+static u4 classDescriptorHash(const char* str)
+{
+    u4 hash = 1;
+
+    while (*str != '\0')
+        hash = hash * 31 + *str++;
+
+    return hash;
+}
+
+/*
+ * Add an entry to the class lookup table.  We hash the string and probe
+ * until we find an open slot.
+ */
+static void classLookupAdd(DexFile* pDexFile, DexClassLookup* pLookup,
+    int stringOff, int classDefOff, int* pNumProbes)
+{
+    const char* classDescriptor =
+        (const char*) (pDexFile->baseAddr + stringOff);
+    const DexClassDef* pClassDef =
+        (const DexClassDef*) (pDexFile->baseAddr + classDefOff);
+    u4 hash = classDescriptorHash(classDescriptor);
+    int mask = pLookup->numEntries-1;
+    int idx = hash & mask;
+
+    /*
+     * Find the first empty slot.  We oversized the table, so this is
+     * guaranteed to finish.
+     */
+    int probes = 0;
+    while (pLookup->table[idx].classDescriptorOffset != 0) {
+        idx = (idx + 1) & mask;
+        probes++;
+    }
+    //if (probes > 1)
+    //    LOGW("classLookupAdd: probes=%d\n", probes);
+
+    pLookup->table[idx].classDescriptorHash = hash;
+    pLookup->table[idx].classDescriptorOffset = stringOff;
+    pLookup->table[idx].classDefOffset = classDefOff;
+    *pNumProbes = probes;
+}
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+u4 dexRoundUpPower2(u4 val)
+{
+    val--;
+    val |= val >> 1;
+    val |= val >> 2;
+    val |= val >> 4;
+    val |= val >> 8;
+    val |= val >> 16;
+    val++;
+
+    return val;
+}
+
+/*
+ * Create the class lookup hash table.
+ *
+ * Returns newly-allocated storage.
+ */
+DexClassLookup* dexCreateClassLookup(DexFile* pDexFile)
+{
+    DexClassLookup* pLookup;
+    int allocSize;
+    int i, numEntries;
+    int numProbes, totalProbes, maxProbes;
+
+    numProbes = totalProbes = maxProbes = 0;
+
+    assert(pDexFile != NULL);
+
+    /*
+     * Using a factor of 3 results in far less probing than a factor of 2,
+     * but almost doubles the flash storage requirements for the bootstrap
+     * DEX files.  The overall impact on class loading performance seems
+     * to be minor.  We could probably get some performance improvement by
+     * using a secondary hash.
+     */
+    numEntries = dexRoundUpPower2(pDexFile->pHeader->classDefsSize * 2);
+    allocSize = offsetof(DexClassLookup, table)
+                    + numEntries * sizeof(pLookup->table[0]);
+
+    pLookup = (DexClassLookup*) calloc(1, allocSize);
+    if (pLookup == NULL)
+        return NULL;
+    pLookup->size = allocSize;
+    pLookup->numEntries = numEntries;
+
+    for (i = 0; i < (int)pDexFile->pHeader->classDefsSize; i++) {
+        const DexClassDef* pClassDef;
+        const char* pString;
+
+        pClassDef = dexGetClassDef(pDexFile, i);
+        pString = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+        classLookupAdd(pDexFile, pLookup, 
+            (u1*)pString - pDexFile->baseAddr,
+            (u1*)pClassDef - pDexFile->baseAddr, &numProbes);
+
+        if (numProbes > maxProbes)
+            maxProbes = numProbes;
+        totalProbes += numProbes;
+    }
+
+    LOGV("Class lookup: classes=%d slots=%d (%d%% occ) alloc=%d"
+         " total=%d max=%d\n",
+        pDexFile->pHeader->classDefsSize, numEntries,
+        (100 * pDexFile->pHeader->classDefsSize) / numEntries,
+        allocSize, totalProbes, maxProbes);
+
+    return pLookup;
+}
+
+
+/*
+ * Set up the basic raw data pointers of a DexFile. This function isn't
+ * meant for general use.
+ */
+void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {
+    DexHeader *pHeader = (DexHeader*) data;
+
+    pDexFile->baseAddr = data;
+    pDexFile->pHeader = pHeader;
+    pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);
+    pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
+    pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);
+    pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff);
+    pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
+    pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);
+    pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
+}
+
+
+/*
+ * Parse out an index map entry, advancing "*pData" and reducing "*pSize".
+ */
+static bool parseIndexMapEntry(const u1** pData, u4* pSize, bool expanding,
+    u4* pFullCount, u4* pReducedCount, const u2** pMap)
+{
+    const u4* wordPtr = (const u4*) *pData;
+    u4 size = *pSize;
+    u4 mapCount;
+
+    if (expanding) {
+        if (size < 4)
+            return false;
+        mapCount = *pReducedCount = *wordPtr++;
+        *pFullCount = (u4) -1;
+        size -= sizeof(u4);
+    } else {
+        if (size < 8)
+            return false;
+        mapCount = *pFullCount = *wordPtr++;
+        *pReducedCount = *wordPtr++;
+        size -= sizeof(u4) * 2;
+    }
+
+    u4 mapSize = mapCount * sizeof(u2);
+
+    if (size < mapSize)
+        return false;
+    *pMap = (const u2*) wordPtr;
+    size -= mapSize;
+
+    /* advance the pointer */
+    const u1* ptr = (const u1*) wordPtr;
+    ptr += (mapSize + 3) & ~0x3;
+
+    /* update pass-by-reference values */
+    *pData = (const u1*) ptr;
+    *pSize = size;
+
+    return true;
+}
+
+/*
+ * Set up some pointers into the mapped data.
+ *
+ * See analysis/ReduceConstants.c for the data layout description.
+ */
+static bool parseIndexMap(DexFile* pDexFile, const u1* data, u4 size,
+    bool expanding)
+{
+    if (!parseIndexMapEntry(&data, &size, expanding,
+            &pDexFile->indexMap.classFullCount,
+            &pDexFile->indexMap.classReducedCount,
+            &pDexFile->indexMap.classMap))
+    {
+        return false;
+    }
+
+    if (!parseIndexMapEntry(&data, &size, expanding,
+            &pDexFile->indexMap.methodFullCount,
+            &pDexFile->indexMap.methodReducedCount,
+            &pDexFile->indexMap.methodMap))
+    {
+        return false;
+    }
+
+    if (!parseIndexMapEntry(&data, &size, expanding,
+            &pDexFile->indexMap.fieldFullCount,
+            &pDexFile->indexMap.fieldReducedCount,
+            &pDexFile->indexMap.fieldMap))
+    {
+        return false;
+    }
+
+    if (!parseIndexMapEntry(&data, &size, expanding,
+            &pDexFile->indexMap.stringFullCount,
+            &pDexFile->indexMap.stringReducedCount,
+            &pDexFile->indexMap.stringMap))
+    {
+        return false;
+    }
+
+    if (expanding) {
+        /*
+         * The map includes the "reduced" counts; pull the original counts
+         * out of the DexFile so that code has a consistent source.
+         */
+        assert(pDexFile->indexMap.classFullCount == (u4) -1);
+        assert(pDexFile->indexMap.methodFullCount == (u4) -1);
+        assert(pDexFile->indexMap.fieldFullCount == (u4) -1);
+        assert(pDexFile->indexMap.stringFullCount == (u4) -1);
+
+#if 0   // TODO: not available yet -- do later or just skip this
+        pDexFile->indexMap.classFullCount =
+            pDexFile->pHeader->typeIdsSize;
+        pDexFile->indexMap.methodFullCount =
+            pDexFile->pHeader->methodIdsSize;
+        pDexFile->indexMap.fieldFullCount =
+            pDexFile->pHeader->fieldIdsSize;
+        pDexFile->indexMap.stringFullCount =
+            pDexFile->pHeader->stringIdsSize;
+#endif
+    }
+
+    LOGI("Class : %u %u %u\n",
+        pDexFile->indexMap.classFullCount,
+        pDexFile->indexMap.classReducedCount,
+        pDexFile->indexMap.classMap[0]);
+    LOGI("Method: %u %u %u\n",
+        pDexFile->indexMap.methodFullCount,
+        pDexFile->indexMap.methodReducedCount,
+        pDexFile->indexMap.methodMap[0]);
+    LOGI("Field : %u %u %u\n",
+        pDexFile->indexMap.fieldFullCount,
+        pDexFile->indexMap.fieldReducedCount,
+        pDexFile->indexMap.fieldMap[0]);
+    LOGI("String: %u %u %u\n",
+        pDexFile->indexMap.stringFullCount,
+        pDexFile->indexMap.stringReducedCount,
+        pDexFile->indexMap.stringMap[0]);
+
+    return true;
+}
+
+/*
+ * Parse some auxillary data tables.
+ *
+ * v1.0 wrote a zero in the first 32 bits, followed by the DexClassLookup
+ * table.  Subsequent versions switched to the "chunk" format.
+ */
+static bool parseAuxData(const u1* data, DexFile* pDexFile)
+{
+    const u4* pAux = (const u4*) (data + pDexFile->pOptHeader->auxOffset);
+    u4 indexMapType = 0;
+
+    /* v1.0 format? */
+    if (*pAux == 0) {
+        LOGV("+++ found OLD dex format\n");
+        pDexFile->pClassLookup = (const DexClassLookup*) (pAux+1);
+        return true;
+    }
+    LOGV("+++ found NEW dex format\n");
+
+    /* process chunks until we see the end marker */
+    while (*pAux != kDexChunkEnd) {
+        u4 size = *(pAux+1);
+        u1* data = (u1*) (pAux + 2);
+
+        switch (*pAux) {
+        case kDexChunkClassLookup:
+            pDexFile->pClassLookup = (const DexClassLookup*) data;
+            break;
+        case kDexChunkReducingIndexMap:
+            LOGI("+++ found reducing index map, size=%u\n", size);
+            if (!parseIndexMap(pDexFile, data, size, false)) {
+                LOGE("Failed parsing reducing index map\n");
+                return false;
+            }
+            indexMapType = *pAux;
+            break;
+        case kDexChunkExpandingIndexMap:
+            LOGI("+++ found expanding index map, size=%u\n", size);
+            if (!parseIndexMap(pDexFile, data, size, true)) {
+                LOGE("Failed parsing expanding index map\n");
+                return false;
+            }
+            indexMapType = *pAux;
+            break;
+        default:
+            LOGI("Unknown chunk 0x%08x (%c%c%c%c), size=%d in aux data area\n",
+                *pAux,
+                (char) ((*pAux) >> 24), (char) ((*pAux) >> 16),
+                (char) ((*pAux) >> 8),  (char)  (*pAux),
+                size);
+            break;
+        }
+
+        /*
+         * Advance pointer, padding to 64-bit boundary.  The extra "+8" is
+         * for the type/size header.
+         */
+        size = (size + 8 + 7) & ~7;
+        pAux += size / sizeof(u4);
+    }
+
+#if 0   // TODO: propagate expected map type from the VM through the API
+    /*
+     * If we're configured to expect an index map, and we don't find one,
+     * reject this DEX so we'll regenerate it.  Also, if we found an
+     * "expanding" map but we're not configured to use it, we have to fail
+     * because the constants aren't usable without translation.
+     */
+    if (indexMapType != expectedIndexMapType) {
+        LOGW("Incompatible index map configuration: found 0x%04x, need %d\n",
+            indexMapType, DVM_REDUCE_CONSTANTS);
+        return false;
+    }
+#endif
+
+    return true;
+}
+
+/*
+ * Parse an optimized or unoptimized .dex file sitting in memory.  This is
+ * called after the byte-ordering and structure alignment has been fixed up.
+ *
+ * On success, return a newly-allocated DexFile.
+ */
+DexFile* dexFileParse(const u1* data, size_t length, int flags)
+{
+    DexFile* pDexFile = NULL;
+    const DexHeader* pHeader;
+    const u1* magic;
+    int result = -1;
+
+    if (length < sizeof(DexHeader)) {
+        LOGE("too short to be a valid .dex\n");
+        goto bail;      /* bad file format */
+    }
+
+    pDexFile = (DexFile*) malloc(sizeof(DexFile));
+    if (pDexFile == NULL)
+        goto bail;      /* alloc failure */
+    memset(pDexFile, 0, sizeof(DexFile));
+
+    /*
+     * Peel off the optimized header.
+     */
+    if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
+        magic = data;
+        if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
+            LOGE("bad opt version (0x%02x %02x %02x %02x)\n",
+                 magic[4], magic[5], magic[6], magic[7]);
+            goto bail;
+        }
+
+        pDexFile->pOptHeader = (const DexOptHeader*) data;
+        LOGV("Good opt header, DEX offset is %d, flags=0x%02x\n",
+            pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);
+
+        /* locate some auxillary data tables */
+        if (!parseAuxData(data, pDexFile))
+            goto bail;
+
+        /* ignore the opt header and appended data from here on out */
+        data += pDexFile->pOptHeader->dexOffset;
+        length -= pDexFile->pOptHeader->dexOffset;
+        if (pDexFile->pOptHeader->dexLength > length) {
+            LOGE("File truncated? stored len=%d, rem len=%d\n",
+                pDexFile->pOptHeader->dexLength, (int) length);
+            goto bail;
+        }
+        length = pDexFile->pOptHeader->dexLength;
+    }
+
+    dexFileSetupBasicPointers(pDexFile, data);
+    pHeader = pDexFile->pHeader;
+
+    magic = pHeader->magic;
+    if (memcmp(magic, DEX_MAGIC, 4) != 0) {
+        /* not expected */
+        LOGE("bad magic number (0x%02x %02x %02x %02x)\n",
+             magic[0], magic[1], magic[2], magic[3]);
+        goto bail;
+    }
+    if (memcmp(magic+4, DEX_MAGIC_VERS, 4) != 0) {
+        LOGE("bad dex version (0x%02x %02x %02x %02x)\n",
+             magic[4], magic[5], magic[6], magic[7]);
+        goto bail;
+    }
+
+    /*
+     * Verify the checksum.  This is reasonably quick, but does require
+     * touching every byte in the DEX file.  The checksum changes after
+     * byte-swapping and DEX optimization.
+     */
+    if (flags & kDexParseVerifyChecksum) {
+        u4 adler = dexComputeChecksum(pHeader);
+        if (adler != pHeader->checksum) {
+            LOGE("ERROR: bad checksum (%08x vs %08x)\n",
+                adler, pHeader->checksum);
+            if (!(flags & kDexParseContinueOnError))
+                goto bail;
+        } else {
+            LOGV("+++ adler32 checksum (%08x) verified\n", adler);
+        }
+    }
+
+    /*
+     * Verify the SHA-1 digest.  (Normally we don't want to do this --
+     * the digest is used to uniquely identify a DEX file, and can't be
+     * computed post-optimization.)
+     *
+     * The digest will be invalid after byte swapping and DEX optimization.
+     */
+    if (kVerifySignature) {
+        unsigned char sha1Digest[kSHA1DigestLen];
+        const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +
+                            kSHA1DigestLen;
+
+        dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);
+        if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {
+            char tmpBuf1[kSHA1DigestOutputLen];
+            char tmpBuf2[kSHA1DigestOutputLen];
+            LOGE("ERROR: bad SHA1 digest (%s vs %s)\n",
+                dexSHA1DigestToStr(sha1Digest, tmpBuf1),
+                dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
+            if (!(flags & kDexParseContinueOnError))
+                goto bail;
+        } else {
+            LOGV("+++ sha1 digest verified\n");
+        }
+    }
+
+    if (pHeader->fileSize != length) {
+        LOGE("ERROR: stored file size (%d) != expected (%d)\n",
+            (int) pHeader->fileSize, (int) length);
+        if (!(flags & kDexParseContinueOnError))
+            goto bail;
+    }
+
+    if (pHeader->classDefsSize == 0) {
+        LOGE("ERROR: DEX file has no classes in it, failing\n");
+        goto bail;
+    }
+
+    /*
+     * Success!
+     */
+    result = 0;
+
+bail:
+    if (result != 0 && pDexFile != NULL) {
+        dexFileFree(pDexFile);
+        pDexFile = NULL;
+    }
+    return pDexFile;
+}
+
+/*
+ * Free up the DexFile and any associated data structures.
+ *
+ * Note we may be called with a partially-initialized DexFile.
+ */
+void dexFileFree(DexFile* pDexFile)
+{
+    if (pDexFile == NULL)
+        return;
+
+    free(pDexFile);
+}
+
+/*
+ * Look up a class definition entry by descriptor.
+ *
+ * "descriptor" should look like "Landroid/debug/Stuff;".
+ */
+const DexClassDef* dexFindClass(const DexFile* pDexFile,
+    const char* descriptor)
+{
+    const DexClassLookup* pLookup = pDexFile->pClassLookup;
+    u4 hash;
+    int idx, mask;
+
+    hash = classDescriptorHash(descriptor);
+    mask = pLookup->numEntries - 1;
+    idx = hash & mask;
+
+    /*
+     * Search until we find a matching entry or an empty slot.
+     */
+    while (true) {
+        int offset;
+
+        offset = pLookup->table[idx].classDescriptorOffset;
+        if (offset == 0)
+            return NULL;
+
+        if (pLookup->table[idx].classDescriptorHash == hash) {
+            const char* str;
+        
+            str = (const char*) (pDexFile->baseAddr + offset);
+            if (strcmp(str, descriptor) == 0) {
+                return (const DexClassDef*)
+                    (pDexFile->baseAddr + pLookup->table[idx].classDefOffset);
+            }
+        }
+
+        idx = (idx + 1) & mask;
+    }
+}
+
+
+/*
+ * Compute the DEX file checksum for a memory-mapped DEX file.
+ */
+u4 dexComputeChecksum(const DexHeader* pHeader)
+{
+    const u1* start = (const u1*) pHeader;
+
+    uLong adler = adler32(0L, Z_NULL, 0);
+    const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+
+    return (u4) adler32(adler, start + nonSum, pHeader->fileSize - nonSum);
+}
+
+
+/*
+ * ===========================================================================
+ *      Debug info
+ * ===========================================================================
+ */
+
+/*
+ * Decode the arguments in a method signature, which looks something
+ * like "(ID[Ljava/lang/String;)V".
+ *
+ * Returns the type signature letter for the next argument, or ')' if
+ * there are no more args.  Advances "pSig" to point to the character
+ * after the one returned.
+ */
+static char decodeSignature(const char** pSig)
+{
+    const char* sig = *pSig;
+
+    if (*sig == '(')
+        sig++;
+
+    if (*sig == 'L') {
+        /* object ref */
+        while (*++sig != ';')
+            ;
+        *pSig = sig+1;
+        return 'L';
+    }
+    if (*sig == '[') {
+        /* array; advance past array type */
+        while (*++sig == '[')
+            ;
+        if (*sig == 'L') {
+            while (*++sig != ';')
+                ;
+        }
+        *pSig = sig+1;
+        return '[';
+    }
+    if (*sig == '\0')
+        return *sig;        /* don't advance further */
+
+    *pSig = sig+1;
+    return *sig;
+}
+
+/*
+ * returns the length of a type string, given the start of the
+ * type string. Used for the case where the debug info format
+ * references types that are inside a method type signature.
+ */
+static int typeLength (const char *type) {
+    // Assumes any leading '(' has already been gobbled
+    const char *end = type;
+    decodeSignature(&end);
+    return end - type;
+}
+
+/*
+ * Reads a string index as encoded for the debug info format,
+ * returning a string pointer or NULL as appropriate.
+ */
+static const char* readStringIdx(const DexFile* pDexFile, 
+        const u1** pStream) {
+    u4 stringIdx = readUnsignedLeb128(pStream);
+
+    // Remember, encoded string indicies have 1 added to them.
+    if (stringIdx == 0) {
+        return NULL;
+    } else {
+        return dexStringById(pDexFile, stringIdx - 1);
+    }
+}
+
+/*
+ * Reads a type index as encoded for the debug info format, returning
+ * a string pointer for its descriptor or NULL as appropriate.
+ */
+static const char* readTypeIdx(const DexFile* pDexFile, 
+        const u1** pStream) {
+    u4 typeIdx = readUnsignedLeb128(pStream);
+
+    // Remember, encoded type indicies have 1 added to them.
+    if (typeIdx == 0) {
+        return NULL;
+    } else {
+        return dexStringByTypeIdx(pDexFile, typeIdx - 1);
+    }
+}
+
+/* access_flag value indicating that a method is static */
+#define ACC_STATIC              0x0008
+
+typedef struct LocalInfo {
+    const char *name;
+    const char *descriptor;
+    const char *signature;
+    u2 startAddress;
+    bool live;
+} LocalInfo;
+
+static void emitLocalCbIfLive (void *cnxt, int reg, u4 endAddress, 
+        LocalInfo *localInReg, DexDebugNewLocalCb localCb)
+{
+    if (localCb != NULL && localInReg[reg].live) {
+        localCb(cnxt, reg, localInReg[reg].startAddress, endAddress,
+                localInReg[reg].name, 
+                localInReg[reg].descriptor, 
+                localInReg[reg].signature == NULL 
+                ? "" : localInReg[reg].signature );
+    }
+}
+
+// TODO optimize localCb == NULL case
+void dexDecodeDebugInfo(
+            const DexFile* pDexFile,
+            const DexCode* pCode,
+            const char* classDescriptor,
+            u4 protoIdx,
+            u4 accessFlags,
+            DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+            void* cnxt)
+{
+    const u1 *stream = dexGetDebugInfoStream(pDexFile, pCode);
+    u4 line;
+    u4 parametersSize;
+    u4 address = 0;
+    LocalInfo localInReg[pCode->registersSize];
+    u4 insnsSize = pCode->insnsSize;
+    DexProto proto = { pDexFile, protoIdx };
+
+    memset(localInReg, 0, sizeof(LocalInfo) * pCode->registersSize);
+
+    if (stream == NULL) {
+        goto end;
+    }
+
+    line = readUnsignedLeb128(&stream);
+    parametersSize = readUnsignedLeb128(&stream);
+
+    u2 argReg = pCode->registersSize - pCode->insSize;
+
+    if ((accessFlags & ACC_STATIC) == 0) {
+        /*
+         * The code is an instance method, which means that there is
+         * an initial this parameter. Also, the proto list should
+         * contain exactly one fewer argument word than the insSize
+         * indicates.
+         */
+        assert(pCode->insSize == (dexProtoComputeArgsSize(&proto) + 1));
+        localInReg[argReg].name = "this";
+        localInReg[argReg].descriptor = classDescriptor;
+        localInReg[argReg].startAddress = 0;
+        localInReg[argReg].live = true;
+        argReg++;
+    } else {
+        assert(pCode->insSize == dexProtoComputeArgsSize(&proto));
+    }
+    
+    DexParameterIterator iterator;
+    dexParameterIteratorInit(&iterator, &proto);
+
+    while (parametersSize-- != 0) {
+        const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+        const char *name;
+        int reg;
+        
+        if ((argReg >= pCode->registersSize) || (descriptor == NULL)) {
+            goto invalid_stream;
+        }
+
+        name = readStringIdx(pDexFile, &stream);
+        reg = argReg;
+
+        switch (descriptor[0]) {
+            case 'D':
+            case 'J':
+                argReg += 2;
+                break;
+            default:
+                argReg += 1;
+                break;
+        }
+
+        if (name != NULL) {
+            localInReg[reg].name = name;
+            localInReg[reg].descriptor = descriptor;
+            localInReg[reg].signature = NULL;
+            localInReg[reg].startAddress = address;
+            localInReg[reg].live = true;
+        }
+    }
+
+    for (;;)  {
+        u1 opcode = *stream++;
+        u2 reg;
+
+        switch (opcode) {
+            case DBG_END_SEQUENCE:
+                goto end;
+
+            case DBG_ADVANCE_PC:
+                address += readUnsignedLeb128(&stream);
+                break;
+                
+            case DBG_ADVANCE_LINE:
+                line += readSignedLeb128(&stream);
+                break;
+
+            case DBG_START_LOCAL:
+            case DBG_START_LOCAL_EXTENDED:
+                reg = readUnsignedLeb128(&stream);
+                if (reg > pCode->registersSize) goto invalid_stream;
+
+                // Emit what was previously there, if anything
+                emitLocalCbIfLive (cnxt, reg, address, 
+                    localInReg, localCb);
+
+                localInReg[reg].name = readStringIdx(pDexFile, &stream);
+                localInReg[reg].descriptor = readTypeIdx(pDexFile, &stream);
+                if (opcode == DBG_START_LOCAL_EXTENDED) {
+                    localInReg[reg].signature 
+                        = readStringIdx(pDexFile, &stream);
+                } else {
+                    localInReg[reg].signature = NULL;
+                }
+                localInReg[reg].startAddress = address;
+                localInReg[reg].live = true;
+                break;
+
+            case DBG_END_LOCAL:
+                reg = readUnsignedLeb128(&stream);
+                if (reg > pCode->registersSize) goto invalid_stream;
+
+                emitLocalCbIfLive (cnxt, reg, address, localInReg, localCb);
+                localInReg[reg].live = false;
+                break;
+
+            case DBG_RESTART_LOCAL:
+                reg = readUnsignedLeb128(&stream);
+                if (reg > pCode->registersSize) goto invalid_stream;
+
+                if (localInReg[reg].name == NULL 
+                        || localInReg[reg].descriptor == NULL) {
+                    goto invalid_stream;
+                }
+
+                /*
+                 * If the register is live, the "restart" is superfluous,
+                 * and we don't want to mess with the existing start address.
+                 */
+                if (!localInReg[reg].live) {
+                    localInReg[reg].startAddress = address;
+                    localInReg[reg].live = true;
+                }
+                break;
+
+            case DBG_SET_PROLOGUE_END:
+            case DBG_SET_EPILOGUE_BEGIN:
+            case DBG_SET_FILE:
+                break;
+
+            default: {
+                int adjopcode = opcode - DBG_FIRST_SPECIAL;
+
+                address += adjopcode / DBG_LINE_RANGE;
+                line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+
+                if (posCb != NULL) {
+                    int done; 
+                    done = posCb(cnxt, address, line);
+
+                    if (done) {
+                        // early exit
+                        goto end;
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+end:
+    {
+        int reg;
+        for (reg = 0; reg < pCode->registersSize; reg++) {
+            emitLocalCbIfLive (cnxt, reg, insnsSize, localInReg, localCb);
+        }
+    }
+    return;
+
+invalid_stream:
+    IF_LOGE() {
+        char* methodDescriptor = dexProtoCopyMethodDescriptor(&proto);
+        LOGE("Invalid debug info stream. class %s; proto %s",
+                classDescriptor, methodDescriptor);
+        free(methodDescriptor);
+    }
+}