Switch to new parameter syntax

The new syntax is:
.param p1, firstParamName

which is closer to the existing local syntax:
.local v0, someLocal:I
This commit is contained in:
Ben Gruver 2013-04-16 00:24:46 -07:00
parent 4a2b9ef40c
commit ec28400394
11 changed files with 394 additions and 66 deletions

View File

@ -756,14 +756,13 @@ the annotations. If it turns out that they are parameter annotations, we include
add them to the $statements_and_directives::methodAnnotations list*/ add them to the $statements_and_directives::methodAnnotations list*/
parameter_directive parameter_directive
@init {List<CommonTree> annotations = new ArrayList<CommonTree>();} @init {List<CommonTree> annotations = new ArrayList<CommonTree>();}
: PARAMETER_DIRECTIVE : PARAMETER_DIRECTIVE REGISTER COMMA simple_name
STRING_LITERAL?
({input.LA(1) == ANNOTATION_DIRECTIVE}? annotation {annotations.add($annotation.tree);})* ({input.LA(1) == ANNOTATION_DIRECTIVE}? annotation {annotations.add($annotation.tree);})*
( END_PARAMETER_DIRECTIVE ( END_PARAMETER_DIRECTIVE
-> ^(I_PARAMETER[$start, "I_PARAMETER"] STRING_LITERAL? ^(I_ANNOTATIONS annotation*)) -> ^(I_PARAMETER[$start, "I_PARAMETER"] REGISTER simple_name ^(I_ANNOTATIONS annotation*))
| /*epsilon*/ {$statements_and_directives::methodAnnotations.addAll(annotations);} | /*epsilon*/ {$statements_and_directives::methodAnnotations.addAll(annotations);}
-> ^(I_PARAMETER[$start, "I_PARAMETER"] STRING_LITERAL? ^(I_ANNOTATIONS)) -> ^(I_PARAMETER[$start, "I_PARAMETER"] REGISTER simple_name ^(I_ANNOTATIONS))
); );
ordered_debug_directive ordered_debug_directive

View File

@ -63,6 +63,7 @@ import org.jf.dexlib2.immutable.instruction.*;
import org.jf.dexlib2.immutable.reference.*; import org.jf.dexlib2.immutable.reference.*;
import org.jf.dexlib2.immutable.value.*; import org.jf.dexlib2.immutable.value.*;
import org.jf.dexlib2.util.MethodUtil; import org.jf.dexlib2.util.MethodUtil;
import org.jf.util.LinearSearch;
} }
@ -341,6 +342,7 @@ method returns[Method ret]
int currentAddress; int currentAddress;
HashMap<Integer, Integer> packedSwitchDeclarations; HashMap<Integer, Integer> packedSwitchDeclarations;
HashMap<Integer, Integer> sparseSwitchDeclarations; HashMap<Integer, Integer> sparseSwitchDeclarations;
boolean isStatic;
int totalMethodRegisters; int totalMethodRegisters;
int methodParameterRegisters; int methodParameterRegisters;
} }
@ -349,11 +351,11 @@ method returns[Method ret]
$method::totalMethodRegisters = 0; $method::totalMethodRegisters = 0;
$method::methodParameterRegisters = 0; $method::methodParameterRegisters = 0;
int accessFlags = 0; int accessFlags = 0;
boolean isStatic = false;
$method::labels = new HashMap<String, Integer>(); $method::labels = new HashMap<String, Integer>();
$method::currentAddress = 0; $method::currentAddress = 0;
$method::packedSwitchDeclarations = new HashMap<Integer, Integer>(); $method::packedSwitchDeclarations = new HashMap<Integer, Integer>();
$method::sparseSwitchDeclarations = new HashMap<Integer, Integer>(); $method::sparseSwitchDeclarations = new HashMap<Integer, Integer>();
$method::isStatic = false;
} }
: :
^(I_METHOD ^(I_METHOD
@ -361,8 +363,9 @@ method returns[Method ret]
access_list access_list
{ {
accessFlags = $access_list.value; accessFlags = $access_list.value;
isStatic = AccessFlags.STATIC.isSet(accessFlags); $method::isStatic = AccessFlags.STATIC.isSet(accessFlags);
$method::methodParameterRegisters = MethodUtil.getParameterRegisterCount($method_name_and_prototype.parameters, isStatic); $method::methodParameterRegisters =
MethodUtil.getParameterRegisterCount($method_name_and_prototype.parameters, $method::isStatic);
} }
(registers_directive (registers_directive
{ {
@ -456,7 +459,7 @@ method returns[Method ret]
$ret = new ImmutableMethod( $ret = new ImmutableMethod(
classType, classType,
$method_name_and_prototype.name, $method_name_and_prototype.name,
$parameters.parameters, $method_name_and_prototype.parameters,
$method_name_and_prototype.returnType, $method_name_and_prototype.returnType,
accessFlags, accessFlags,
$annotations.annotations, $annotations.annotations,
@ -470,11 +473,20 @@ method_prototype returns[List<String> parameters, String returnType]
$parameters = $field_type_list.types; $parameters = $field_type_list.types;
}; };
method_name_and_prototype returns[String name, List<String> parameters, String returnType] method_name_and_prototype returns[String name, List<SmaliMethodParameter> parameters, String returnType]
: SIMPLE_NAME method_prototype : SIMPLE_NAME method_prototype
{ {
$name = $SIMPLE_NAME.text; $name = $SIMPLE_NAME.text;
$parameters = $method_prototype.parameters; $parameters = Lists.newArrayList();
int paramRegister = 0;
for (String type: $method_prototype.parameters) {
$parameters.add(new SmaliMethodParameter(paramRegister++, type));
char c = type.charAt(0);
if (c == 'J' || c == 'J') {
paramRegister++;
}
}
$returnType = $method_prototype.returnType; $returnType = $method_prototype.returnType;
}; };
@ -594,34 +606,42 @@ address returns[int address]
$address = Integer.parseInt($I_ADDRESS.text); $address = Integer.parseInt($I_ADDRESS.text);
}; };
parameters[List<String> parameterTypes] returns[List<MethodParameter> parameters] parameters[List<SmaliMethodParameter> parameters]
@init : ^(I_PARAMETERS (parameter[parameters])*);
{
Iterator<String> parameterIter = parameterTypes.iterator();
$parameters = Lists.newArrayList();
}
: ^(I_PARAMETERS (parameter[parameterIter] { $parameters.add($parameter.parameter); })*)
{
String paramType;
while (parameterIter.hasNext()) {
paramType = parameterIter.next();
$parameters.add(new ImmutableMethodParameter(paramType, null, null));
}
};
parameter[Iterator<String> parameterTypes] returns[MethodParameter parameter] parameter[List<SmaliMethodParameter> parameters]
: ^(I_PARAMETER string_literal? annotations : ^(I_PARAMETER REGISTER SIMPLE_NAME annotations)
{ {
if (!$parameterTypes.hasNext()) { final int registerNumber = parseRegister_short($REGISTER.text);
throw new SemanticException(input, $I_PARAMETER, "Too many .parameter directives specified."); int totalMethodRegisters = $method::totalMethodRegisters;
} int methodParameterRegisters = $method::methodParameterRegisters;
String type = $parameterTypes.next();
String name = $string_literal.value;
Set<Annotation> annotations = $annotations.annotations;
$parameter = new ImmutableMethodParameter(type, annotations, name); if (registerNumber >= totalMethodRegisters) {
throw new SemanticException(input, $I_PARAMETER, "Register \%s is larger than the maximum register v\%d " +
"for this method", $REGISTER.text, totalMethodRegisters-1);
} }
); final int indexGuess = registerNumber - (totalMethodRegisters - methodParameterRegisters) - ($method::isStatic?0:1);
if (indexGuess < 0) {
throw new SemanticException(input, $I_PARAMETER, "Register \%s is not a parameter register.",
$REGISTER.text);
}
int parameterIndex = LinearSearch.linearSearch(parameters, SmaliMethodParameter.COMPARATOR,
new WithRegister() { public int getRegister() { return indexGuess; } },
indexGuess);
if (parameterIndex < 0) {
throw new SemanticException(input, $I_PARAMETER, "Register \%s is the second half of a wide parameter.",
$REGISTER.text);
}
SmaliMethodParameter methodParameter = parameters.get(parameterIndex);
methodParameter.name = $SIMPLE_NAME.text;
if ($annotations.annotations != null && $annotations.annotations.size() > 0) {
methodParameter.annotations = $annotations.annotations;
}
};
ordered_debug_directives returns[List<DebugItem> debugItems] ordered_debug_directives returns[List<DebugItem> debugItems]
@init {debugItems = Lists.newArrayList();} @init {debugItems = Lists.newArrayList();}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2013, 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.smali;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Ints;
import org.jf.dexlib2.base.BaseMethodParameter;
import org.jf.dexlib2.iface.Annotation;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Comparator;
import java.util.Set;
public class SmaliMethodParameter extends BaseMethodParameter implements WithRegister {
public final int register;
@Nonnull public final String type;
@Nonnull public Set<? extends Annotation> annotations;
@Nullable public String name;
public SmaliMethodParameter(int register, @Nonnull String type) {
this.register = register;
this.type = type;
this.annotations = ImmutableSet.of();
}
@Override public int getRegister() { return register; }
@Nonnull @Override public String getType() { return type; }
@Nonnull @Override public Set<? extends Annotation> getAnnotations() { return annotations; }
@Nullable @Override public String getName() { return name; }
@Nullable @Override public String getSignature() { return null; }
public static final Comparator<WithRegister> COMPARATOR = new Comparator<WithRegister>() {
@Override public int compare(WithRegister o1, WithRegister o2) {
return Ints.compare(o1.getRegister(), o2.getRegister());
}
};
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2013, 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.smali;
public interface WithRegister {
int getRegister();
}

View File

@ -254,8 +254,8 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
".catch" { return newToken(CATCH_DIRECTIVE); } ".catch" { return newToken(CATCH_DIRECTIVE); }
".catchall" { return newToken(CATCHALL_DIRECTIVE); } ".catchall" { return newToken(CATCHALL_DIRECTIVE); }
".line" { return newToken(LINE_DIRECTIVE); } ".line" { return newToken(LINE_DIRECTIVE); }
".parameter" { return newToken(PARAMETER_DIRECTIVE); } ".param" { return newToken(PARAMETER_DIRECTIVE); }
".end parameter" { return newToken(END_PARAMETER_DIRECTIVE); } ".end param" { return newToken(END_PARAMETER_DIRECTIVE); }
".local" { return newToken(LOCAL_DIRECTIVE); } ".local" { return newToken(LOCAL_DIRECTIVE); }
".end local" { return newToken(END_LOCAL_DIRECTIVE); } ".end local" { return newToken(END_LOCAL_DIRECTIVE); }
".restart local" { return newToken(RESTART_LOCAL_DIRECTIVE); } ".restart local" { return newToken(RESTART_LOCAL_DIRECTIVE); }

View File

@ -23,8 +23,8 @@
.catch .catch
.catchall .catchall
.line .line
.parameter .param
.end parameter .end param
.local .local
.end local .end local
.restart local .restart local
@ -52,5 +52,7 @@
. .
.1234.1234 .1234.1234
. .
.parameter
.end parameter

View File

@ -22,8 +22,8 @@ END_SPARSE_SWITCH_DIRECTIVE(".end sparse-switch")
CATCH_DIRECTIVE(".catch") CATCH_DIRECTIVE(".catch")
CATCHALL_DIRECTIVE(".catchall") CATCHALL_DIRECTIVE(".catchall")
LINE_DIRECTIVE(".line") LINE_DIRECTIVE(".line")
PARAMETER_DIRECTIVE(".parameter") PARAMETER_DIRECTIVE(".param")
END_PARAMETER_DIRECTIVE(".end parameter") END_PARAMETER_DIRECTIVE(".end param")
LOCAL_DIRECTIVE(".local") LOCAL_DIRECTIVE(".local")
END_LOCAL_DIRECTIVE(".end local") END_LOCAL_DIRECTIVE(".end local")
RESTART_LOCAL_DIRECTIVE(".restart local") RESTART_LOCAL_DIRECTIVE(".restart local")
@ -61,4 +61,6 @@ INVALID_TOKEN(".end")
INVALID_TOKEN(".") INVALID_TOKEN(".")
DOUBLE_LITERAL(".1234") DOUBLE_LITERAL(".1234")
DOUBLE_LITERAL(".1234") DOUBLE_LITERAL(".1234")
INVALID_TOKEN(".") INVALID_TOKEN(".")
INVALID_TOKEN(".parameter")
INVALID_TOKEN(".end parameter")

View File

@ -100,7 +100,7 @@
# direct methods # direct methods
.method constructor <init>(Lcom/android/internal/telephony/cdma/CDMAPhone;)V .method constructor <init>(Lcom/android/internal/telephony/cdma/CDMAPhone;)V
.registers 2 .registers 2
.parameter "phone" .param p1, phone
.prologue .prologue
.line 42 .line 42
@ -112,7 +112,7 @@
.method protected getEFPath(I)Ljava/lang/String; .method protected getEFPath(I)Ljava/lang/String;
.registers 3 .registers 3
.parameter "efid" .param p1, efid
.prologue .prologue
.line 71 .line 71
@ -145,7 +145,7 @@
.method CardStateFromRILInt(I)Lcom/android/internal/telephony/IccCardStatus$CardState; .method CardStateFromRILInt(I)Lcom/android/internal/telephony/IccCardStatus$CardState;
.registers 6 .registers 6
.parameter "state" .param p1, state
.prologue .prologue
.line 59 .line 59
@ -214,11 +214,11 @@
.method public setCallForwardingOption(IILjava/lang/String;ILandroid/os/Message;)V .method public setCallForwardingOption(IILjava/lang/String;ILandroid/os/Message;)V
.registers 13 .registers 13
.parameter "commandInterfaceCFAction" .param p1, commandInterfaceCFAction
.parameter "commandInterfaceCFReason" .param p2, commandInterfaceCFReason
.parameter "dialingNumber" .param p3, dialingNumber
.parameter "timerSeconds" .param p4, timerSeconds
.parameter "onComplete" .param p5, onComplete
.prologue .prologue
const/4 v3, 0x1 const/4 v3, 0x1

View File

@ -266,8 +266,10 @@ CLOSE_PAREN(")")
VOID_TYPE("V") VOID_TYPE("V")
REGISTERS_DIRECTIVE(".registers") REGISTERS_DIRECTIVE(".registers")
POSITIVE_INTEGER_LITERAL("2") POSITIVE_INTEGER_LITERAL("2")
PARAMETER_DIRECTIVE(".parameter") PARAMETER_DIRECTIVE(".param")
STRING_LITERAL("\"phone\"") REGISTER("p1")
COMMA(",")
SIMPLE_NAME("phone")
PROLOGUE_DIRECTIVE(".prologue") PROLOGUE_DIRECTIVE(".prologue")
LINE_DIRECTIVE(".line") LINE_DIRECTIVE(".line")
POSITIVE_INTEGER_LITERAL("42") POSITIVE_INTEGER_LITERAL("42")
@ -298,8 +300,10 @@ CLOSE_PAREN(")")
CLASS_DESCRIPTOR("Ljava/lang/String;") CLASS_DESCRIPTOR("Ljava/lang/String;")
REGISTERS_DIRECTIVE(".registers") REGISTERS_DIRECTIVE(".registers")
POSITIVE_INTEGER_LITERAL("3") POSITIVE_INTEGER_LITERAL("3")
PARAMETER_DIRECTIVE(".parameter") PARAMETER_DIRECTIVE(".param")
STRING_LITERAL("\"efid\"") REGISTER("p1")
COMMA(",")
SIMPLE_NAME("efid")
PROLOGUE_DIRECTIVE(".prologue") PROLOGUE_DIRECTIVE(".prologue")
LINE_DIRECTIVE(".line") LINE_DIRECTIVE(".line")
POSITIVE_INTEGER_LITERAL("71") POSITIVE_INTEGER_LITERAL("71")
@ -369,8 +373,10 @@ CLOSE_PAREN(")")
CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;") CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;")
REGISTERS_DIRECTIVE(".registers") REGISTERS_DIRECTIVE(".registers")
POSITIVE_INTEGER_LITERAL("6") POSITIVE_INTEGER_LITERAL("6")
PARAMETER_DIRECTIVE(".parameter") PARAMETER_DIRECTIVE(".param")
STRING_LITERAL("\"state\"") REGISTER("p1")
COMMA(",")
SIMPLE_NAME("state")
PROLOGUE_DIRECTIVE(".prologue") PROLOGUE_DIRECTIVE(".prologue")
LINE_DIRECTIVE(".line") LINE_DIRECTIVE(".line")
POSITIVE_INTEGER_LITERAL("59") POSITIVE_INTEGER_LITERAL("59")
@ -551,16 +557,26 @@ CLOSE_PAREN(")")
VOID_TYPE("V") VOID_TYPE("V")
REGISTERS_DIRECTIVE(".registers") REGISTERS_DIRECTIVE(".registers")
POSITIVE_INTEGER_LITERAL("13") POSITIVE_INTEGER_LITERAL("13")
PARAMETER_DIRECTIVE(".parameter") PARAMETER_DIRECTIVE(".param")
STRING_LITERAL("\"commandInterfaceCFAction\"") REGISTER("p1")
PARAMETER_DIRECTIVE(".parameter") COMMA(",")
STRING_LITERAL("\"commandInterfaceCFReason\"") SIMPLE_NAME("commandInterfaceCFAction")
PARAMETER_DIRECTIVE(".parameter") PARAMETER_DIRECTIVE(".param")
STRING_LITERAL("\"dialingNumber\"") REGISTER("p2")
PARAMETER_DIRECTIVE(".parameter") COMMA(",")
STRING_LITERAL("\"timerSeconds\"") SIMPLE_NAME("commandInterfaceCFReason")
PARAMETER_DIRECTIVE(".parameter") PARAMETER_DIRECTIVE(".param")
STRING_LITERAL("\"onComplete\"") REGISTER("p3")
COMMA(",")
SIMPLE_NAME("dialingNumber")
PARAMETER_DIRECTIVE(".param")
REGISTER("p4")
COMMA(",")
SIMPLE_NAME("timerSeconds")
PARAMETER_DIRECTIVE(".param")
REGISTER("p5")
COMMA(",")
SIMPLE_NAME("onComplete")
PROLOGUE_DIRECTIVE(".prologue") PROLOGUE_DIRECTIVE(".prologue")
INSTRUCTION_FORMAT11n("const/4") INSTRUCTION_FORMAT11n("const/4")
REGISTER("v3") REGISTER("v3")

View File

@ -0,0 +1,85 @@
/*
* Copyright 2013, 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.util;
import java.util.Comparator;
import java.util.List;
public class LinearSearch {
/**
* Performs a linear search in a sorted list for key, starting at initialGuess
*
* @param list The sorted list to search
* @param comparator The comparator to use
* @param key The key to search for
* @param initialGuess An initial guess of the location.
* @return If found, the index of the item. If not found, -return + 1 is the index at which the item would be
* inserted
*/
public static <T> int linearSearch(List<? extends T> list, Comparator<T> comparator, T key, int initialGuess) {
int guess = initialGuess;
if (guess >= list.size()) {
guess = list.size()-1;
}
int comparison = comparator.compare(list.get(guess), key);
if (comparison == 0) {
return guess;
}
if (comparison < 0) {
guess++;
while (guess < list.size()) {
comparison = comparator.compare(list.get(guess), key);
if (comparison == 0) {
return guess;
}
if (comparison > 0) {
return -(guess+1);
}
guess++;
}
return -(list.size()+1);
} else {
guess--;
while (guess >= 0) {
comparison = comparator.compare(list.get(guess), key);
if (comparison == 0) {
return guess;
}
if (comparison < 0) {
return -(guess+2);
}
guess--;
}
return -1;
}
}
}

View File

@ -0,0 +1,101 @@
/*
* Copyright 2013, 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.util;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import junit.framework.Assert;
import org.junit.Test;
import java.util.List;
public class LinearSearchTest {
@Test
public void testLinearSearch() {
List<Integer> list = Lists.newArrayList(0, 1, 3, 4);
doTest(list, 5, 10);
doTest(list, 5, 4);
doTest(list, 5, 3);
doTest(list, 5, 2);
doTest(list, 5, 1);
doTest(list, 5, 0);
doTest(list, 4, 10);
doTest(list, 4, 4);
doTest(list, 4, 3);
doTest(list, 4, 2);
doTest(list, 4, 1);
doTest(list, 4, 0);
doTest(list, 3, 10);
doTest(list, 3, 4);
doTest(list, 3, 3);
doTest(list, 3, 2);
doTest(list, 3, 1);
doTest(list, 3, 0);
doTest(list, 2, 10);
doTest(list, 2, 4);
doTest(list, 2, 3);
doTest(list, 2, 2);
doTest(list, 2, 1);
doTest(list, 2, 0);
doTest(list, 1, 10);
doTest(list, 1, 4);
doTest(list, 1, 3);
doTest(list, 1, 2);
doTest(list, 1, 1);
doTest(list, 1, 0);
doTest(list, 0, 10);
doTest(list, 0, 4);
doTest(list, 0, 3);
doTest(list, 0, 2);
doTest(list, 0, 1);
doTest(list, 0, 0);
doTest(list, -1, 10);
doTest(list, -1, 4);
doTest(list, -1, 3);
doTest(list, -1, 2);
doTest(list, -1, 1);
doTest(list, -1, 0);
}
private void doTest(List<Integer> list, int key, int guess) {
int expectedIndex = Ordering.natural().binarySearch(list, key);
Assert.assertEquals(expectedIndex, LinearSearch.linearSearch(list, Ordering.<Integer>natural(), key, guess));
}
}