mirror of
https://github.com/revanced/smali.git
synced 2025-05-28 20:00:13 +02:00
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:
parent
4a2b9ef40c
commit
ec28400394
@ -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*/
|
||||
parameter_directive
|
||||
@init {List<CommonTree> annotations = new ArrayList<CommonTree>();}
|
||||
: PARAMETER_DIRECTIVE
|
||||
STRING_LITERAL?
|
||||
: PARAMETER_DIRECTIVE REGISTER COMMA simple_name
|
||||
({input.LA(1) == ANNOTATION_DIRECTIVE}? annotation {annotations.add($annotation.tree);})*
|
||||
|
||||
( 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);}
|
||||
-> ^(I_PARAMETER[$start, "I_PARAMETER"] STRING_LITERAL? ^(I_ANNOTATIONS))
|
||||
-> ^(I_PARAMETER[$start, "I_PARAMETER"] REGISTER simple_name ^(I_ANNOTATIONS))
|
||||
);
|
||||
|
||||
ordered_debug_directive
|
||||
|
@ -63,6 +63,7 @@ import org.jf.dexlib2.immutable.instruction.*;
|
||||
import org.jf.dexlib2.immutable.reference.*;
|
||||
import org.jf.dexlib2.immutable.value.*;
|
||||
import org.jf.dexlib2.util.MethodUtil;
|
||||
import org.jf.util.LinearSearch;
|
||||
|
||||
|
||||
}
|
||||
@ -341,6 +342,7 @@ method returns[Method ret]
|
||||
int currentAddress;
|
||||
HashMap<Integer, Integer> packedSwitchDeclarations;
|
||||
HashMap<Integer, Integer> sparseSwitchDeclarations;
|
||||
boolean isStatic;
|
||||
int totalMethodRegisters;
|
||||
int methodParameterRegisters;
|
||||
}
|
||||
@ -349,11 +351,11 @@ method returns[Method ret]
|
||||
$method::totalMethodRegisters = 0;
|
||||
$method::methodParameterRegisters = 0;
|
||||
int accessFlags = 0;
|
||||
boolean isStatic = false;
|
||||
$method::labels = new HashMap<String, Integer>();
|
||||
$method::currentAddress = 0;
|
||||
$method::packedSwitchDeclarations = new HashMap<Integer, Integer>();
|
||||
$method::sparseSwitchDeclarations = new HashMap<Integer, Integer>();
|
||||
$method::isStatic = false;
|
||||
}
|
||||
:
|
||||
^(I_METHOD
|
||||
@ -361,8 +363,9 @@ method returns[Method ret]
|
||||
access_list
|
||||
{
|
||||
accessFlags = $access_list.value;
|
||||
isStatic = AccessFlags.STATIC.isSet(accessFlags);
|
||||
$method::methodParameterRegisters = MethodUtil.getParameterRegisterCount($method_name_and_prototype.parameters, isStatic);
|
||||
$method::isStatic = AccessFlags.STATIC.isSet(accessFlags);
|
||||
$method::methodParameterRegisters =
|
||||
MethodUtil.getParameterRegisterCount($method_name_and_prototype.parameters, $method::isStatic);
|
||||
}
|
||||
(registers_directive
|
||||
{
|
||||
@ -456,7 +459,7 @@ method returns[Method ret]
|
||||
$ret = new ImmutableMethod(
|
||||
classType,
|
||||
$method_name_and_prototype.name,
|
||||
$parameters.parameters,
|
||||
$method_name_and_prototype.parameters,
|
||||
$method_name_and_prototype.returnType,
|
||||
accessFlags,
|
||||
$annotations.annotations,
|
||||
@ -470,11 +473,20 @@ method_prototype returns[List<String> parameters, String returnType]
|
||||
$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
|
||||
{
|
||||
$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;
|
||||
};
|
||||
|
||||
@ -594,34 +606,42 @@ address returns[int address]
|
||||
$address = Integer.parseInt($I_ADDRESS.text);
|
||||
};
|
||||
|
||||
parameters[List<String> parameterTypes] returns[List<MethodParameter> parameters]
|
||||
@init
|
||||
{
|
||||
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));
|
||||
}
|
||||
};
|
||||
parameters[List<SmaliMethodParameter> parameters]
|
||||
: ^(I_PARAMETERS (parameter[parameters])*);
|
||||
|
||||
parameter[Iterator<String> parameterTypes] returns[MethodParameter parameter]
|
||||
: ^(I_PARAMETER string_literal? annotations
|
||||
{
|
||||
if (!$parameterTypes.hasNext()) {
|
||||
throw new SemanticException(input, $I_PARAMETER, "Too many .parameter directives specified.");
|
||||
}
|
||||
String type = $parameterTypes.next();
|
||||
String name = $string_literal.value;
|
||||
Set<Annotation> annotations = $annotations.annotations;
|
||||
parameter[List<SmaliMethodParameter> parameters]
|
||||
: ^(I_PARAMETER REGISTER SIMPLE_NAME annotations)
|
||||
{
|
||||
final int registerNumber = parseRegister_short($REGISTER.text);
|
||||
int totalMethodRegisters = $method::totalMethodRegisters;
|
||||
int methodParameterRegisters = $method::methodParameterRegisters;
|
||||
|
||||
$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]
|
||||
@init {debugItems = Lists.newArrayList();}
|
||||
|
67
smali/src/main/java/org/jf/smali/SmaliMethodParameter.java
Normal file
67
smali/src/main/java/org/jf/smali/SmaliMethodParameter.java
Normal 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());
|
||||
}
|
||||
};
|
||||
}
|
36
smali/src/main/java/org/jf/smali/WithRegister.java
Normal file
36
smali/src/main/java/org/jf/smali/WithRegister.java
Normal 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();
|
||||
}
|
@ -254,8 +254,8 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
|
||||
".catch" { return newToken(CATCH_DIRECTIVE); }
|
||||
".catchall" { return newToken(CATCHALL_DIRECTIVE); }
|
||||
".line" { return newToken(LINE_DIRECTIVE); }
|
||||
".parameter" { return newToken(PARAMETER_DIRECTIVE); }
|
||||
".end parameter" { return newToken(END_PARAMETER_DIRECTIVE); }
|
||||
".param" { return newToken(PARAMETER_DIRECTIVE); }
|
||||
".end param" { return newToken(END_PARAMETER_DIRECTIVE); }
|
||||
".local" { return newToken(LOCAL_DIRECTIVE); }
|
||||
".end local" { return newToken(END_LOCAL_DIRECTIVE); }
|
||||
".restart local" { return newToken(RESTART_LOCAL_DIRECTIVE); }
|
||||
|
@ -23,8 +23,8 @@
|
||||
.catch
|
||||
.catchall
|
||||
.line
|
||||
.parameter
|
||||
.end parameter
|
||||
.param
|
||||
.end param
|
||||
.local
|
||||
.end local
|
||||
.restart local
|
||||
@ -52,5 +52,7 @@
|
||||
.
|
||||
.1234.1234
|
||||
.
|
||||
.parameter
|
||||
.end parameter
|
||||
|
||||
|
||||
|
@ -22,8 +22,8 @@ END_SPARSE_SWITCH_DIRECTIVE(".end sparse-switch")
|
||||
CATCH_DIRECTIVE(".catch")
|
||||
CATCHALL_DIRECTIVE(".catchall")
|
||||
LINE_DIRECTIVE(".line")
|
||||
PARAMETER_DIRECTIVE(".parameter")
|
||||
END_PARAMETER_DIRECTIVE(".end parameter")
|
||||
PARAMETER_DIRECTIVE(".param")
|
||||
END_PARAMETER_DIRECTIVE(".end param")
|
||||
LOCAL_DIRECTIVE(".local")
|
||||
END_LOCAL_DIRECTIVE(".end local")
|
||||
RESTART_LOCAL_DIRECTIVE(".restart local")
|
||||
@ -61,4 +61,6 @@ INVALID_TOKEN(".end")
|
||||
INVALID_TOKEN(".")
|
||||
DOUBLE_LITERAL(".1234")
|
||||
DOUBLE_LITERAL(".1234")
|
||||
INVALID_TOKEN(".")
|
||||
INVALID_TOKEN(".")
|
||||
INVALID_TOKEN(".parameter")
|
||||
INVALID_TOKEN(".end parameter")
|
@ -100,7 +100,7 @@
|
||||
# direct methods
|
||||
.method constructor <init>(Lcom/android/internal/telephony/cdma/CDMAPhone;)V
|
||||
.registers 2
|
||||
.parameter "phone"
|
||||
.param p1, phone
|
||||
|
||||
.prologue
|
||||
.line 42
|
||||
@ -112,7 +112,7 @@
|
||||
|
||||
.method protected getEFPath(I)Ljava/lang/String;
|
||||
.registers 3
|
||||
.parameter "efid"
|
||||
.param p1, efid
|
||||
|
||||
.prologue
|
||||
.line 71
|
||||
@ -145,7 +145,7 @@
|
||||
|
||||
.method CardStateFromRILInt(I)Lcom/android/internal/telephony/IccCardStatus$CardState;
|
||||
.registers 6
|
||||
.parameter "state"
|
||||
.param p1, state
|
||||
|
||||
.prologue
|
||||
.line 59
|
||||
@ -214,11 +214,11 @@
|
||||
|
||||
.method public setCallForwardingOption(IILjava/lang/String;ILandroid/os/Message;)V
|
||||
.registers 13
|
||||
.parameter "commandInterfaceCFAction"
|
||||
.parameter "commandInterfaceCFReason"
|
||||
.parameter "dialingNumber"
|
||||
.parameter "timerSeconds"
|
||||
.parameter "onComplete"
|
||||
.param p1, commandInterfaceCFAction
|
||||
.param p2, commandInterfaceCFReason
|
||||
.param p3, dialingNumber
|
||||
.param p4, timerSeconds
|
||||
.param p5, onComplete
|
||||
|
||||
.prologue
|
||||
const/4 v3, 0x1
|
||||
|
@ -266,8 +266,10 @@ CLOSE_PAREN(")")
|
||||
VOID_TYPE("V")
|
||||
REGISTERS_DIRECTIVE(".registers")
|
||||
POSITIVE_INTEGER_LITERAL("2")
|
||||
PARAMETER_DIRECTIVE(".parameter")
|
||||
STRING_LITERAL("\"phone\"")
|
||||
PARAMETER_DIRECTIVE(".param")
|
||||
REGISTER("p1")
|
||||
COMMA(",")
|
||||
SIMPLE_NAME("phone")
|
||||
PROLOGUE_DIRECTIVE(".prologue")
|
||||
LINE_DIRECTIVE(".line")
|
||||
POSITIVE_INTEGER_LITERAL("42")
|
||||
@ -298,8 +300,10 @@ CLOSE_PAREN(")")
|
||||
CLASS_DESCRIPTOR("Ljava/lang/String;")
|
||||
REGISTERS_DIRECTIVE(".registers")
|
||||
POSITIVE_INTEGER_LITERAL("3")
|
||||
PARAMETER_DIRECTIVE(".parameter")
|
||||
STRING_LITERAL("\"efid\"")
|
||||
PARAMETER_DIRECTIVE(".param")
|
||||
REGISTER("p1")
|
||||
COMMA(",")
|
||||
SIMPLE_NAME("efid")
|
||||
PROLOGUE_DIRECTIVE(".prologue")
|
||||
LINE_DIRECTIVE(".line")
|
||||
POSITIVE_INTEGER_LITERAL("71")
|
||||
@ -369,8 +373,10 @@ CLOSE_PAREN(")")
|
||||
CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;")
|
||||
REGISTERS_DIRECTIVE(".registers")
|
||||
POSITIVE_INTEGER_LITERAL("6")
|
||||
PARAMETER_DIRECTIVE(".parameter")
|
||||
STRING_LITERAL("\"state\"")
|
||||
PARAMETER_DIRECTIVE(".param")
|
||||
REGISTER("p1")
|
||||
COMMA(",")
|
||||
SIMPLE_NAME("state")
|
||||
PROLOGUE_DIRECTIVE(".prologue")
|
||||
LINE_DIRECTIVE(".line")
|
||||
POSITIVE_INTEGER_LITERAL("59")
|
||||
@ -551,16 +557,26 @@ CLOSE_PAREN(")")
|
||||
VOID_TYPE("V")
|
||||
REGISTERS_DIRECTIVE(".registers")
|
||||
POSITIVE_INTEGER_LITERAL("13")
|
||||
PARAMETER_DIRECTIVE(".parameter")
|
||||
STRING_LITERAL("\"commandInterfaceCFAction\"")
|
||||
PARAMETER_DIRECTIVE(".parameter")
|
||||
STRING_LITERAL("\"commandInterfaceCFReason\"")
|
||||
PARAMETER_DIRECTIVE(".parameter")
|
||||
STRING_LITERAL("\"dialingNumber\"")
|
||||
PARAMETER_DIRECTIVE(".parameter")
|
||||
STRING_LITERAL("\"timerSeconds\"")
|
||||
PARAMETER_DIRECTIVE(".parameter")
|
||||
STRING_LITERAL("\"onComplete\"")
|
||||
PARAMETER_DIRECTIVE(".param")
|
||||
REGISTER("p1")
|
||||
COMMA(",")
|
||||
SIMPLE_NAME("commandInterfaceCFAction")
|
||||
PARAMETER_DIRECTIVE(".param")
|
||||
REGISTER("p2")
|
||||
COMMA(",")
|
||||
SIMPLE_NAME("commandInterfaceCFReason")
|
||||
PARAMETER_DIRECTIVE(".param")
|
||||
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")
|
||||
INSTRUCTION_FORMAT11n("const/4")
|
||||
REGISTER("v3")
|
||||
|
85
util/src/main/java/org/jf/util/LinearSearch.java
Normal file
85
util/src/main/java/org/jf/util/LinearSearch.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
101
util/src/test/java/org/jf/util/LinearSearchTest.java
Normal file
101
util/src/test/java/org/jf/util/LinearSearchTest.java
Normal 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));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user