/*
 * Copyright 2012, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.jf.dexlib2.dexbacked;

import org.jf.util.ExceptionWithContext;
import org.jf.util.Utf8Utils;

import javax.annotation.Nonnull;

public class DexReader<T extends DexBuffer> {
    @Nonnull public final T dexBuf;
    private int offset;

    public DexReader(@Nonnull T dexBuf, int offset) {
        this.dexBuf = dexBuf;
        this.offset = offset;
    }

    public int getOffset() { return offset; }
    public void setOffset(int offset) { this.offset = offset; }

    public int readSleb128() {
        int end = dexBuf.baseOffset + offset;
        int currentByteValue;
        int result;
        byte[] buf = dexBuf.buf;

        result = buf[end++] & 0xff;
        if (result <= 0x7f) {
            result = (result << 25) >> 25;
        } else {
            currentByteValue = buf[end++] & 0xff;
            result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7);
            if (currentByteValue <= 0x7f) {
                result = (result << 18) >> 18;
            } else {
                currentByteValue = buf[end++] & 0xff;
                result |= (currentByteValue & 0x7f) << 14;
                if (currentByteValue <= 0x7f) {
                    result = (result << 11) >> 11;
                } else {
                    currentByteValue = buf[end++] & 0xff;
                    result |= (currentByteValue & 0x7f) << 21;
                    if (currentByteValue <= 0x7f) {
                        result = (result << 4) >> 4;
                    } else {
                        currentByteValue = buf[end++] & 0xff;
                        if (currentByteValue > 0x7f) {
                            throw new ExceptionWithContext(
                                    "Invalid sleb128 integer encountered at offset 0x%x", offset);
                        }
                        result |= currentByteValue << 28;
                    }
                }
            }
        }

        offset = end - dexBuf.baseOffset;
        return result;
    }

    public int peekSleb128Size() {
        int end = dexBuf.baseOffset + offset;
        int currentByteValue;
        int result;
        byte[] buf = dexBuf.buf;

        result = buf[end++] & 0xff;
        if (result > 0x7f) {
            currentByteValue = buf[end++] & 0xff;
            if (currentByteValue > 0x7f) {
                currentByteValue = buf[end++] & 0xff;
                if (currentByteValue > 0x7f) {
                    currentByteValue = buf[end++] & 0xff;
                    if (currentByteValue > 0x7f) {
                        currentByteValue = buf[end++] & 0xff;
                        if (currentByteValue > 0x7f) {
                            throw new ExceptionWithContext(
                                "Invalid sleb128 integer encountered at offset 0x%x", offset);
                        }
                    }
                }
            }
        }

        return end - (dexBuf.baseOffset + offset);
    }

    public int readSmallUleb128() {
        return readUleb128(false);
    }

    public int peekSmallUleb128Size() {
        return peekUleb128Size(false);
    }

    private int readUleb128(boolean allowLarge) {
        int end = dexBuf.baseOffset + offset;
        int currentByteValue;
        int result;
        byte[] buf = dexBuf.buf;

        result = buf[end++] & 0xff;
        if (result > 0x7f) {
            currentByteValue = buf[end++] & 0xff;
            result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7);
            if (currentByteValue > 0x7f) {
                currentByteValue = buf[end++] & 0xff;
                result |= (currentByteValue & 0x7f) << 14;
                if (currentByteValue > 0x7f) {
                    currentByteValue = buf[end++] & 0xff;
                    result |= (currentByteValue & 0x7f) << 21;
                    if (currentByteValue > 0x7f) {
                        currentByteValue = buf[end++];

                        // MSB shouldn't be set on last byte
                        if (currentByteValue < 0) {
                            throw new ExceptionWithContext(
                                    "Invalid uleb128 integer encountered at offset 0x%x", offset);
                        } else if ((currentByteValue & 0xf) > 0x07) {
                            if (!allowLarge) {
                                // for non-large uleb128s, we assume most significant bit of the result will not be
                                // set, so that it can fit into a signed integer without wrapping
                                throw new ExceptionWithContext(
                                        "Encountered valid uleb128 that is out of range at offset 0x%x", offset);
                            }
                        }
                        result |= currentByteValue << 28;
                    }
                }
            }
        }

        offset = end - dexBuf.baseOffset;
        return result;
    }

    private int peekUleb128Size(boolean allowLarge) {
        int end = dexBuf.baseOffset + offset;
        int currentByteValue;
        int result;
        byte[] buf = dexBuf.buf;

        result = buf[end++] & 0xff;
        if (result > 0x7f) {
            currentByteValue = buf[end++] & 0xff;
            if (currentByteValue > 0x7f) {
                currentByteValue = buf[end++] & 0xff;
                if (currentByteValue > 0x7f) {
                    currentByteValue = buf[end++] & 0xff;
                    if (currentByteValue > 0x7f) {
                        currentByteValue = buf[end++];

                        // MSB shouldn't be set on last byte
                        if (currentByteValue < 0) {
                            throw new ExceptionWithContext(
                                "Invalid uleb128 integer encountered at offset 0x%x", offset);
                        } else if ((currentByteValue & 0xf) > 0x07) {
                            if (!allowLarge) {
                                // for non-large uleb128s, we assume most significant bit of the result will not be
                                // set, so that it can fit into a signed integer without wrapping
                                throw new ExceptionWithContext(
                                    "Encountered valid uleb128 that is out of range at offset 0x%x", offset);
                            }
                        }
                    }
                }
            }
        }

        return end - (dexBuf.baseOffset + offset);
    }


    
Reads a "large" uleb128. That is, one that may legitimately be greater than a signed int. The value is returned as if it were signed. i.e. a value of 0xFFFFFFFF would be returned as -1. It is up to the caller to handle the value appropriately.
/** * Reads a "large" uleb128. That is, one that may legitimately be greater than a signed int. * * The value is returned as if it were signed. i.e. a value of 0xFFFFFFFF would be returned as -1. It is up to the * caller to handle the value appropriately. */
public int readLargeUleb128() { return readUleb128(true); }
Reads a "big" uleb128 that can legitimately be > 2^31. The value is returned as a signed integer, with the expected semantics of re-interpreting an unsigned value as a signed value.
Returns:The unsigned value, reinterpreted as a signed int
/** * Reads a "big" uleb128 that can legitimately be > 2^31. The value is returned as a signed integer, with the * expected semantics of re-interpreting an unsigned value as a signed value. * * @return The unsigned value, reinterpreted as a signed int */
public int readBigUleb128() { int end = dexBuf.baseOffset + offset; int currentByteValue; int result; byte[] buf = dexBuf.buf; result = buf[end++] & 0xff; if (result > 0x7f) { currentByteValue = buf[end++] & 0xff; result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7); if (currentByteValue > 0x7f) { currentByteValue = buf[end++] & 0xff; result |= (currentByteValue & 0x7f) << 14; if (currentByteValue > 0x7f) { currentByteValue = buf[end++] & 0xff; result |= (currentByteValue & 0x7f) << 21; if (currentByteValue > 0x7f) { currentByteValue = buf[end++]; // MSB shouldn't be set on last byte if (currentByteValue < 0) { throw new ExceptionWithContext( "Invalid uleb128 integer encountered at offset 0x%x", offset); } result |= currentByteValue << 28; } } } } offset = end - dexBuf.baseOffset; return result; } public int peekBigUleb128Size() { int end = dexBuf.baseOffset + offset; int currentByteValue; int result; byte[] buf = dexBuf.buf; result = buf[end++] & 0xff; if (result > 0x7f) { currentByteValue = buf[end++] & 0xff; if (currentByteValue > 0x7f) { currentByteValue = buf[end++] & 0xff; if (currentByteValue > 0x7f) { currentByteValue = buf[end++] & 0xff; if (currentByteValue > 0x7f) { currentByteValue = buf[end++]; // MSB shouldn't be set on last byte if (currentByteValue < 0) { throw new ExceptionWithContext( "Invalid uleb128 integer encountered at offset 0x%x", offset); } } } } } return end - (dexBuf.baseOffset + offset); } public void skipUleb128() { int end = dexBuf.baseOffset + offset; byte currentByteValue; byte[] buf = dexBuf.buf; currentByteValue = buf[end++]; if (currentByteValue < 0) { // if the MSB is set currentByteValue = buf[end++]; if (currentByteValue < 0) { // if the MSB is set currentByteValue = buf[end++]; if (currentByteValue < 0) { // if the MSB is set currentByteValue = buf[end++]; if (currentByteValue < 0) { // if the MSB is set currentByteValue = buf[end++]; if (currentByteValue < 0) { throw new ExceptionWithContext( "Invalid uleb128 integer encountered at offset 0x%x", offset); } } } } } offset = end - dexBuf.baseOffset; } public int readSmallUint() { int o = offset; int result = dexBuf.readSmallUint(o); offset = o + 4; return result; } public int readOptionalUint() { int o = offset; int result = dexBuf.readOptionalUint(o); offset = o + 4; return result; } public int peekUshort() { return dexBuf.readUshort(offset); } public int readUshort() { int o = offset; int result = dexBuf.readUshort(offset); offset = o + 2; return result; } public int peekUbyte() { return dexBuf.readUbyte(offset); } public int readUbyte() { int o = offset; int result = dexBuf.readUbyte(offset); offset = o + 1; return result; } public long readLong() { int o = offset; long result = dexBuf.readLong(offset); offset = o + 8; return result; } public int readInt() { int o = offset; int result = dexBuf.readInt(offset); offset = o + 4; return result; } public int readShort() { int o = offset; int result = dexBuf.readShort(offset); offset = o + 2; return result; } public int readByte() { int o = offset; int result = dexBuf.readByte(offset); offset = o + 1; return result; } public void skipByte() { offset++; } public void moveRelative(int i) { offset += i; } public int readSmallUint(int offset) { return dexBuf.readSmallUint(offset); } public int readUshort(int offset) { return dexBuf.readUshort(offset); } public int readUbyte(int offset) { return dexBuf.readUbyte(offset); } public long readLong(int offset) { return dexBuf.readLong(offset); } public int readInt(int offset) { return dexBuf.readInt(offset); } public int readShort(int offset) { return dexBuf.readShort(offset); } public int readByte(int offset) { return dexBuf.readByte(offset); } public int readSizedInt(int bytes) { int o = dexBuf.baseOffset + offset; byte[] buf = dexBuf.buf; int result; switch (bytes) { case 4: result = (buf[o] & 0xff) | ((buf[o+1] & 0xff) << 8) | ((buf[o+2] & 0xff) << 16) | (buf[o+3] << 24); break; case 3: result = (buf[o] & 0xff) | ((buf[o+1] & 0xff) << 8) | ((buf[o+2]) << 16); break; case 2: result = (buf[o] & 0xff) | ((buf[o+1]) << 8); break; case 1: result = buf[o]; break; default: throw new ExceptionWithContext("Invalid size %d for sized int at offset 0x%x", bytes, offset); } offset = o + bytes - dexBuf.baseOffset; return result; } public int readSizedSmallUint(int bytes) { int o = dexBuf.baseOffset + offset; byte[] buf = dexBuf.buf; int result = 0; switch (bytes) { case 4: int b = buf[o+3]; if (b < 0) { throw new ExceptionWithContext( "Encountered valid sized uint that is out of range at offset 0x%x", offset); } result = b << 24; // fall-through case 3: result |= (buf[o+2] & 0xff) << 16; // fall-through case 2: result |= (buf[o+1] & 0xff) << 8; // fall-through case 1: result |= (buf[o] & 0xff); break; default: throw new ExceptionWithContext("Invalid size %d for sized uint at offset 0x%x", bytes, offset); } offset = o + bytes - dexBuf.baseOffset; return result; } public int readSizedRightExtendedInt(int bytes) { int o = dexBuf.baseOffset + offset; byte[] buf = dexBuf.buf; int result; switch (bytes) { case 4: result = (buf[o] & 0xff) | ((buf[o+1] & 0xff) << 8) | ((buf[o+2] & 0xff) << 16) | (buf[o+3] << 24); break; case 3: result = (buf[o] & 0xff) << 8 | ((buf[o+1] & 0xff) << 16) | (buf[o+2] << 24); break; case 2: result = (buf[o] & 0xff) << 16 | (buf[o+1] << 24); break; case 1: result = buf[o] << 24; break; default: throw new ExceptionWithContext( "Invalid size %d for sized, right extended int at offset 0x%x", bytes, offset); } offset = o + bytes - dexBuf.baseOffset; return result; } public long readSizedRightExtendedLong(int bytes) { int o = dexBuf.baseOffset + offset; byte[] buf = dexBuf.buf; long result; switch (bytes) { case 8: result = (buf[o] & 0xff) | ((buf[o+1] & 0xff) << 8) | ((buf[o+2] & 0xff) << 16) | ((buf[o+3] & 0xffL) << 24) | ((buf[o+4] & 0xffL) << 32) | ((buf[o+5] & 0xffL) << 40) | ((buf[o+6] & 0xffL) << 48) | (((long)buf[o+7]) << 56); break; case 7: result = ((buf[o] & 0xff)) << 8 | ((buf[o+1] & 0xff) << 16) | ((buf[o+2] & 0xffL) << 24) | ((buf[o+3] & 0xffL) << 32) | ((buf[o+4] & 0xffL) << 40) | ((buf[o+5] & 0xffL) << 48) | (((long)buf[o+6]) << 56); break; case 6: result = ((buf[o] & 0xff)) << 16 | ((buf[o+1] & 0xffL) << 24) | ((buf[o+2] & 0xffL) << 32) | ((buf[o+3] & 0xffL) << 40) | ((buf[o+4] & 0xffL) << 48) | (((long)buf[o+5]) << 56); break; case 5: result = ((buf[o] & 0xffL)) << 24 | ((buf[o+1] & 0xffL) << 32) | ((buf[o+2] & 0xffL) << 40) | ((buf[o+3] & 0xffL) << 48) | (((long)buf[o+4]) << 56); break; case 4: result = ((buf[o] & 0xffL)) << 32 | ((buf[o+1] & 0xffL) << 40) | ((buf[o+2] & 0xffL) << 48) | (((long)buf[o+3]) << 56); break; case 3: result = ((buf[o] & 0xffL)) << 40 | ((buf[o+1] & 0xffL) << 48) | (((long)buf[o+2]) << 56); break; case 2: result = ((buf[o] & 0xffL)) << 48 | (((long)buf[o+1]) << 56); break; case 1: result = ((long)buf[o]) << 56; break; default: throw new ExceptionWithContext( "Invalid size %d for sized, right extended long at offset 0x%x", bytes, offset); } offset = o + bytes - dexBuf.baseOffset; return result; } public long readSizedLong(int bytes) { int o = dexBuf.baseOffset + offset; byte[] buf = dexBuf.buf; long result; switch (bytes) { case 8: result = (buf[o] & 0xff) | ((buf[o+1] & 0xff) << 8) | ((buf[o+2] & 0xff) << 16) | ((buf[o+3] & 0xffL) << 24) | ((buf[o+4] & 0xffL) << 32) | ((buf[o+5] & 0xffL) << 40) | ((buf[o+6] & 0xffL) << 48) | (((long)buf[o+7]) << 56); break; case 7: result = (buf[o] & 0xff) | ((buf[o+1] & 0xff) << 8) | ((buf[o+2] & 0xff) << 16) | ((buf[o+3] & 0xffL) << 24) | ((buf[o+4] & 0xffL) << 32) | ((buf[o+5] & 0xffL) << 40) | ((long)(buf[o+6]) << 48); break; case 6: result = (buf[o] & 0xff) | ((buf[o+1] & 0xff) << 8) | ((buf[o+2] & 0xff) << 16) | ((buf[o+3] & 0xffL) << 24) | ((buf[o+4] & 0xffL) << 32) | ((long)(buf[o+5]) << 40); break; case 5: result = (buf[o] & 0xff) | ((buf[o+1] & 0xff) << 8) | ((buf[o+2] & 0xff) << 16) | ((buf[o+3] & 0xffL) << 24) | ((long)(buf[o+4]) << 32); break; case 4: result = (buf[o] & 0xff) | ((buf[o+1] & 0xff) << 8) | ((buf[o+2] & 0xff) << 16) | (((long)buf[o+3]) << 24); break; case 3: result = (buf[o] & 0xff) | ((buf[o+1] & 0xff) << 8) | (buf[o+2] << 16); break; case 2: result = (buf[o] & 0xff) | (buf[o+1] << 8); break; case 1: result = buf[o]; break; default: throw new ExceptionWithContext("Invalid size %d for sized long at offset 0x%x", bytes, offset); } offset = o + bytes - dexBuf.baseOffset; return result; } public String readString(int utf16Length) { int[] ret = new int[1]; String value = Utf8Utils.utf8BytesWithUtf16LengthToString( dexBuf.buf, dexBuf.baseOffset + offset, utf16Length, ret); offset += ret[0]; return value; } public int peekStringLength(int utf16Length) { int[] ret = new int[1]; Utf8Utils.utf8BytesWithUtf16LengthToString( dexBuf.buf, dexBuf.baseOffset + offset, utf16Length, ret); return ret[0]; } }