FileDocCategorySizeDatePackage
SwitchData.javaAPI DocAndroid 5.1 API7986Thu Mar 12 22:18:30 GMT 2015com.android.dx.dex.code

SwitchData

public final class SwitchData extends VariableSizeInsn
Pseudo-instruction which holds switch data. The switch data is a map of values to target addresses, and this class writes the data in either a "packed" or "sparse" form.

Fields Summary
private final CodeAddress
user
{@code non-null;} address representing the instruction that uses this instance
private final com.android.dx.util.IntList
cases
{@code non-null;} sorted list of switch cases (keys)
private final CodeAddress[]
targets
{@code non-null;} corresponding list of code addresses; the branch target for each case
private final boolean
packed
whether the output table will be packed (vs. sparse)
Constructors Summary
public SwitchData(com.android.dx.rop.code.SourcePosition position, CodeAddress user, com.android.dx.util.IntList cases, CodeAddress[] targets)
Constructs an instance. The output address of this instance is initially unknown ({@code -1}).

param
position {@code non-null;} source position
param
user {@code non-null;} address representing the instruction that uses this instance
param
cases {@code non-null;} sorted list of switch cases (keys)
param
targets {@code non-null;} corresponding list of code addresses; the branch target for each case

        super(position, RegisterSpecList.EMPTY);

        if (user == null) {
            throw new NullPointerException("user == null");
        }

        if (cases == null) {
            throw new NullPointerException("cases == null");
        }

        if (targets == null) {
            throw new NullPointerException("targets == null");
        }

        int sz = cases.size();

        if (sz != targets.length) {
            throw new IllegalArgumentException("cases / targets mismatch");
        }

        if (sz > 65535) {
            throw new IllegalArgumentException("too many cases");
        }

        this.user = user;
        this.cases = cases;
        this.targets = targets;
        this.packed = shouldPack(cases);
    
Methods Summary
protected java.lang.StringargString()
{@inheritDoc}

        StringBuffer sb = new StringBuffer(100);

        int sz = targets.length;
        for (int i = 0; i < sz; i++) {
            sb.append("\n    ");
            sb.append(cases.get(i));
            sb.append(": ");
            sb.append(targets[i]);
        }

        return sb.toString();
    
public intcodeSize()
{@inheritDoc}

        return packed ? (int) packedCodeSize(cases) :
            (int) sparseCodeSize(cases);
    
public booleanisPacked()
Returns whether or not this instance's data will be output as packed.

return
{@code true} iff the data is to be packed

        return packed;
    
protected java.lang.StringlistingString0(boolean noteIndices)
{@inheritDoc}

        int baseAddress = user.getAddress();
        StringBuffer sb = new StringBuffer(100);
        int sz = targets.length;

        sb.append(packed ? "packed" : "sparse");
        sb.append("-switch-payload // for switch @ ");
        sb.append(Hex.u2(baseAddress));

        for (int i = 0; i < sz; i++) {
            int absTarget = targets[i].getAddress();
            int relTarget = absTarget - baseAddress;
            sb.append("\n  ");
            sb.append(cases.get(i));
            sb.append(": ");
            sb.append(Hex.u4(absTarget));
            sb.append(" // ");
            sb.append(Hex.s4(relTarget));
        }

        return sb.toString();
    
private static longpackedCodeSize(com.android.dx.util.IntList cases)
Gets the size of a packed table for the given cases, in 16-bit code units.

param
cases {@code non-null;} sorted list of cases
return
{@code >= -1;} the packed table size or {@code -1} if the cases couldn't possibly be represented as a packed table

        int sz = cases.size();
        long low = cases.get(0);
        long high = cases.get(sz - 1);
        long result = ((high - low + 1)) * 2 + 4;

        return (result <= 0x7fffffff) ? result : -1;
    
private static booleanshouldPack(com.android.dx.util.IntList cases)
Determines whether the given list of cases warrant being packed.

param
cases {@code non-null;} sorted list of cases
return
{@code true} iff the table encoding the cases should be packed

        int sz = cases.size();

        if (sz < 2) {
            return true;
        }

        long packedSize = packedCodeSize(cases);
        long sparseSize = sparseCodeSize(cases);

        /*
         * We pick the packed representation if it is possible and
         * would be as small or smaller than 5/4 of the sparse
         * representation. That is, we accept some size overhead on
         * the packed representation, since that format is faster to
         * execute at runtime.
         */
        return (packedSize >= 0) && (packedSize <= ((sparseSize * 5) / 4));
    
private static longsparseCodeSize(com.android.dx.util.IntList cases)
Gets the size of a sparse table for the given cases, in 16-bit code units.

param
cases {@code non-null;} sorted list of cases
return
{@code > 0;} the sparse table size

        int sz = cases.size();

        return (sz * 4L) + 2;
    
public DalvInsnwithRegisters(com.android.dx.rop.code.RegisterSpecList registers)
{@inheritDoc}

        return new SwitchData(getPosition(), user, cases, targets);
    
public voidwriteTo(com.android.dx.util.AnnotatedOutput out)
{@inheritDoc}

        int baseAddress = user.getAddress();
        int defaultTarget = Dops.PACKED_SWITCH.getFormat().codeSize();
        int sz = targets.length;

        if (packed) {
            int firstCase = (sz == 0) ? 0 : cases.get(0);
            int lastCase = (sz == 0) ? 0 : cases.get(sz - 1);
            int outSz = lastCase - firstCase + 1;

            out.writeShort(Opcodes.PACKED_SWITCH_PAYLOAD);
            out.writeShort(outSz);
            out.writeInt(firstCase);

            int caseAt = 0;
            for (int i = 0; i < outSz; i++) {
                int outCase = firstCase + i;
                int oneCase = cases.get(caseAt);
                int relTarget;

                if (oneCase > outCase) {
                    relTarget = defaultTarget;
                } else {
                    relTarget = targets[caseAt].getAddress() - baseAddress;
                    caseAt++;
                }

                out.writeInt(relTarget);
            }
        } else {
            out.writeShort(Opcodes.SPARSE_SWITCH_PAYLOAD);
            out.writeShort(sz);

            for (int i = 0; i < sz; i++) {
                out.writeInt(cases.get(i));
            }

            for (int i = 0; i < sz; i++) {
                int relTarget = targets[i].getAddress() - baseAddress;
                out.writeInt(relTarget);
            }
        }