mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 04:05:39 +00:00
Merge remote-tracking branch 'origin/GP-4955_ryanmkurtz_elf--SQUASHED'
(Closes #6912)
This commit is contained in:
commit
3cc122cffa
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -89,11 +89,12 @@ public class FindReferencesToFieldByNameOrOffsetAction extends AbstractFindRefer
|
|||||||
return null; // cancelled
|
return null; // cancelled
|
||||||
}
|
}
|
||||||
|
|
||||||
Long longChoice = parseInt(userChoice);
|
try {
|
||||||
if (longChoice != null) {
|
return new FieldMatcher(dt, NumericUtilities.parseInt(userChoice));
|
||||||
return new FieldMatcher(dt, longChoice.intValue());
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
return new FieldMatcher(dt, userChoice);
|
||||||
}
|
}
|
||||||
return new FieldMatcher(dt, userChoice);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] getCompositeFieldNames(Composite composite) {
|
private String[] getCompositeFieldNames(Composite composite) {
|
||||||
@ -112,8 +113,4 @@ public class FindReferencesToFieldByNameOrOffsetAction extends AbstractFindRefer
|
|||||||
|
|
||||||
return names.toArray(String[]::new);
|
return names.toArray(String[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long parseInt(String s) {
|
|
||||||
return NumericUtilities.parseNumber(s, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.DialogComponentProvider;
|
import docking.DialogComponentProvider;
|
||||||
import docking.DockingWindowManager;
|
import docking.DockingWindowManager;
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
@ -246,12 +248,12 @@ public class AskDialog<T> extends DialogComponentProvider {
|
|||||||
|
|
||||||
protected Integer getValueAsInt() {
|
protected Integer getValueAsInt() {
|
||||||
String text = getValueAsString();
|
String text = getValueAsString();
|
||||||
return text != null ? NumericUtilities.parseInt(text) : null;
|
return !StringUtils.isBlank(text) ? NumericUtilities.parseInt(text) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Long getValueAsLong() {
|
protected Long getValueAsLong() {
|
||||||
String text = getValueAsString();
|
String text = getValueAsString();
|
||||||
return text != null ? NumericUtilities.parseLong(text) : null;
|
return !StringUtils.isBlank(text) ? NumericUtilities.parseLong(text) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Double getValueAsDouble() {
|
protected Double getValueAsDouble() {
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -220,9 +220,9 @@ public class ElfLoaderOptionsFactory {
|
|||||||
if (!String.class.isAssignableFrom(option.getValueClass())) {
|
if (!String.class.isAssignableFrom(option.getValueClass())) {
|
||||||
return "Invalid type for option: " + name + " - " + option.getValueClass();
|
return "Invalid type for option: " + name + " - " + option.getValueClass();
|
||||||
}
|
}
|
||||||
String value = (String) option.getValue();
|
String value = ((String) option.getValue());
|
||||||
try {
|
try {
|
||||||
space.getAddress(Long.parseUnsignedLong(value, 16), true);// verify valid address
|
space.getAddress(NumericUtilities.parseHexLong(value), true);// verify valid address
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
return "Invalid " + name + " - expecting hexidecimal address offset";
|
return "Invalid " + name + " - expecting hexidecimal address offset";
|
||||||
|
@ -18,6 +18,7 @@ package ghidra.app.util.bin.format.pdb;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
|
|
||||||
import ghidra.app.util.SymbolPathParser;
|
import ghidra.app.util.SymbolPathParser;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
@ -134,14 +135,15 @@ public class DefaultPdbMember extends PdbMember {
|
|||||||
try {
|
try {
|
||||||
int colonIndex = bitSizeOffsetStr.indexOf(':');
|
int colonIndex = bitSizeOffsetStr.indexOf(':');
|
||||||
if (colonIndex > 0) {
|
if (colonIndex > 0) {
|
||||||
bitFieldOffset = (int) NumericUtilities.parseNumber(
|
bitFieldOffset =
|
||||||
bitSizeOffsetStr.substring(colonIndex + 1));
|
NumericUtilities.parseInt(bitSizeOffsetStr.substring(colonIndex + 1));
|
||||||
bitSizeOffsetStr = bitSizeOffsetStr.substring(0, colonIndex);
|
bitSizeOffsetStr = bitSizeOffsetStr.substring(0, colonIndex);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dataTypeParser.setMissingBitOffsetError();
|
dataTypeParser.setMissingBitOffsetError();
|
||||||
}
|
}
|
||||||
bitFieldSize = (int) NumericUtilities.parseNumber(bitSizeOffsetStr);
|
NumberUtils.toInt(bitSizeOffsetStr, 0);
|
||||||
|
bitFieldSize = NumericUtilities.parseInt(bitSizeOffsetStr);
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
Msg.error(this, "Invalid PDB bitfield specification: " + name);
|
Msg.error(this, "Invalid PDB bitfield specification: " + name);
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -151,8 +151,7 @@ public class NumberRangeInputDialog extends DialogComponentProvider {
|
|||||||
String trimmed = rangeText.trim();
|
String trimmed = rangeText.trim();
|
||||||
if (!trimmed.contains(RANGE_DELIMITER)) {
|
if (!trimmed.contains(RANGE_DELIMITER)) {
|
||||||
try {
|
try {
|
||||||
long parsedLong = NumericUtilities.parseLong(trimmed);
|
int intValue = NumericUtilities.parseInt(trimmed);
|
||||||
int intValue = (int) parsedLong;
|
|
||||||
rangeList.addRange(intValue, intValue);
|
rangeList.addRange(intValue, intValue);
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
@ -165,12 +164,8 @@ public class NumberRangeInputDialog extends DialogComponentProvider {
|
|||||||
// this must be a range
|
// this must be a range
|
||||||
String[] startAndEnd = trimmed.split(RANGE_DELIMITER);
|
String[] startAndEnd = trimmed.split(RANGE_DELIMITER);
|
||||||
try {
|
try {
|
||||||
long parsedLong = NumericUtilities.parseLong(startAndEnd[0]);
|
int startInt = NumericUtilities.parseInt(startAndEnd[0]);
|
||||||
int startInt = (int) parsedLong;
|
int endInt = NumericUtilities.parseInt(startAndEnd[1]);
|
||||||
|
|
||||||
parsedLong = NumericUtilities.parseLong(startAndEnd[1]);
|
|
||||||
int endInt = (int) parsedLong;
|
|
||||||
|
|
||||||
rangeList.addRange(startInt, endInt);
|
rangeList.addRange(startInt, endInt);
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -172,8 +172,9 @@ public class GValidatedTextField extends JTextField {
|
|||||||
@Override
|
@Override
|
||||||
public void validate(String oldText, String newText) throws ValidationFailedException {
|
public void validate(String oldText, String newText) throws ValidationFailedException {
|
||||||
try {
|
try {
|
||||||
long oldLong = NumericUtilities.parseLong(oldText);
|
|
||||||
long newLong = NumericUtilities.parseLong(newText);
|
long oldLong = sanitizeInput(oldText);
|
||||||
|
long newLong = sanitizeInput(newText);
|
||||||
validateLong(oldLong, newLong);
|
validateLong(oldLong, newLong);
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
@ -181,7 +182,52 @@ public class GValidatedTextField extends JTextField {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public void validateLong(long oldLong, long newLong) throws ValidationFailedException {
|
public void validateLong(long oldLong, long newLong) throws ValidationFailedException {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to {@link NumericUtilities#parseLong(String)}, but we return 0 if the string
|
||||||
|
* is null, empty, or looks like a partially entered value (such as "-", "0x", etc)
|
||||||
|
*
|
||||||
|
* @param s the string to parse
|
||||||
|
* @return the parsed {@code long} value, or 0 if the string is null, empty, or looks
|
||||||
|
* like a partially entered value (such as "-", "0x", etc)
|
||||||
|
* @throws NumberFormatException if the string does not represent a valid {@code long}
|
||||||
|
* value
|
||||||
|
*/
|
||||||
|
private long sanitizeInput(String s) throws NumberFormatException {
|
||||||
|
// Trim the string and return 0 if it's empty. This could be a partial input.
|
||||||
|
s = (s == null ? "" : s.trim());
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chop off the optional sign character of the string, we'll add it back later
|
||||||
|
long sign = 1;
|
||||||
|
if (s.startsWith("-")) {
|
||||||
|
sign = -1;
|
||||||
|
s = s.substring(1);
|
||||||
|
}
|
||||||
|
else if (s.startsWith("+")) {
|
||||||
|
s = s.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chop off the optional hex prefix...we'll decide to do hex later
|
||||||
|
boolean hexPrefix = s.startsWith("0x") || s.startsWith("0X");
|
||||||
|
if (hexPrefix) {
|
||||||
|
s = s.substring(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we might have chopped off prefixes, check again and return 0 if it's empty.
|
||||||
|
// This could be a partial input.
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sign *
|
||||||
|
(hexPrefix ? NumericUtilities.parseHexLong(s) : NumericUtilities.parseLong(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package ghidra.util;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ import util.CollectionUtils;
|
|||||||
public final class NumericUtilities {
|
public final class NumericUtilities {
|
||||||
public static final BigInteger MAX_UNSIGNED_LONG = new BigInteger("ffffffffffffffff", 16);
|
public static final BigInteger MAX_UNSIGNED_LONG = new BigInteger("ffffffffffffffff", 16);
|
||||||
public static final BigInteger MAX_SIGNED_LONG = new BigInteger("7fffffffffffffff", 16);
|
public static final BigInteger MAX_SIGNED_LONG = new BigInteger("7fffffffffffffff", 16);
|
||||||
|
public static final BigInteger MAX_UNSIGNED_INT = new BigInteger("ffffffff", 16);
|
||||||
public static final long MAX_UNSIGNED_INT32_AS_LONG = 0xffffffffL;
|
public static final long MAX_UNSIGNED_INT32_AS_LONG = 0xffffffffL;
|
||||||
|
|
||||||
private final static String HEX_PREFIX_X = "0X";
|
private final static String HEX_PREFIX_X = "0X";
|
||||||
@ -64,7 +66,9 @@ public final class NumericUtilities {
|
|||||||
*
|
*
|
||||||
* @param numStr the number string
|
* @param numStr the number string
|
||||||
* @return the long value or 0
|
* @return the long value or 0
|
||||||
|
* @deprecated use {@link #parseLong(String)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.3", forRemoval = true)
|
||||||
public static long parseNumber(String numStr) {
|
public static long parseNumber(String numStr) {
|
||||||
return parseNumber(numStr, Long.valueOf(0));
|
return parseNumber(numStr, Long.valueOf(0));
|
||||||
}
|
}
|
||||||
@ -75,7 +79,9 @@ public final class NumericUtilities {
|
|||||||
* @param s the string to parse
|
* @param s the string to parse
|
||||||
* @param defaultValue the default value to use if the string cannot be parsed
|
* @param defaultValue the default value to use if the string cannot be parsed
|
||||||
* @return the long value
|
* @return the long value
|
||||||
|
* @deprecated use {@link #parseLong(String, long)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.3", forRemoval = true)
|
||||||
public static Long parseNumber(String s, Long defaultValue) {
|
public static Long parseNumber(String s, Long defaultValue) {
|
||||||
s = (s == null ? "" : s.trim());
|
s = (s == null ? "" : s.trim());
|
||||||
if (s.length() == 0) {
|
if (s.length() == 0) {
|
||||||
@ -104,90 +110,149 @@ public final class NumericUtilities {
|
|||||||
* the top bit set to be implicitly parsed as negative values.
|
* the top bit set to be implicitly parsed as negative values.
|
||||||
*
|
*
|
||||||
* @param s the string to parse
|
* @param s the string to parse
|
||||||
* @return the {@code int} value, or 0 if the string to parse is null or blank
|
* @return the parsed {@code int} value
|
||||||
* @throws NumberFormatException if the string does not represent a valid {@code int} value
|
* @throws NumberFormatException if the string does not represent a valid {@code int} value
|
||||||
*/
|
*/
|
||||||
public static int parseInt(String s) {
|
public static int parseInt(String s) throws NumberFormatException {
|
||||||
String origStr = s;
|
return parseHelper(s, false, BigInteger::intValue, MAX_UNSIGNED_INT);
|
||||||
int sign = 1;
|
|
||||||
|
|
||||||
s = (s == null ? "" : s.trim());
|
|
||||||
if (s.length() == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (s.startsWith("-")) {
|
|
||||||
sign = -1;
|
|
||||||
s = s.substring(1);
|
|
||||||
}
|
|
||||||
int radix = 10;
|
|
||||||
|
|
||||||
if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) {
|
|
||||||
if (s.length() > 10) {
|
|
||||||
throw new NumberFormatException(s + " has too many digits.");
|
|
||||||
}
|
|
||||||
s = s.substring(2);
|
|
||||||
radix = 16;
|
|
||||||
}
|
|
||||||
if (s.length() == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
BigInteger bi = new BigInteger(s, radix);
|
|
||||||
return bi.intValue() * sign;
|
|
||||||
}
|
|
||||||
catch (NumberFormatException e) {
|
|
||||||
// This is a little hacky, but the message should be complete and report about the
|
|
||||||
// original string
|
|
||||||
NumberFormatException e2 =
|
|
||||||
new NumberFormatException("Cannot parse int from " + origStr);
|
|
||||||
e2.setStackTrace(e.getStackTrace());
|
|
||||||
throw e2;
|
|
||||||
}
|
|
||||||
catch (ArithmeticException e) {
|
|
||||||
throw new NumberFormatException(origStr + " is too big.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the given decimal/hex string as a {@code long} value. This method allows values with
|
* Parses the given decimal/hex string as an {@code int} value. This method allows values with
|
||||||
* the top bit set to be implicitly parsed as negative values.
|
* the top bit set to be implicitly parsed as negative values.
|
||||||
*
|
*
|
||||||
* @param s the string to parse
|
* @param s the string to parse
|
||||||
* @return the {@code long} value, or 0 if the string to parse is null or blank
|
* @param defaultValue the default value to return if the string does not represent a valid
|
||||||
|
* {@code int} value
|
||||||
|
* @return the parsed {@code int} value or the {@code defaultValue} if the string does not
|
||||||
|
* represent a valid {@code int} value
|
||||||
|
*/
|
||||||
|
public static int parseInt(String s, int defaultValue) {
|
||||||
|
try {
|
||||||
|
return parseInt(s);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given decimal/hex string as an {@code long} value. This method allows values with
|
||||||
|
* the top bit set to be implicitly parsed as negative values.
|
||||||
|
*
|
||||||
|
* @param s the string to parse
|
||||||
|
* @return the parsed {@code long} value
|
||||||
* @throws NumberFormatException if the string does not represent a valid {@code long} value
|
* @throws NumberFormatException if the string does not represent a valid {@code long} value
|
||||||
*/
|
*/
|
||||||
public static long parseLong(String s) {
|
public static long parseLong(String s) throws NumberFormatException {
|
||||||
String origStr = s;
|
return parseHelper(s, false, BigInteger::longValue, MAX_UNSIGNED_LONG);
|
||||||
long sign = 1;
|
}
|
||||||
|
|
||||||
s = (s == null ? "" : s.trim());
|
/**
|
||||||
if (s.length() == 0) {
|
* Parses the given decimal/hex string as an {@code long} value. This method allows values with
|
||||||
return 0;
|
* the top bit set to be implicitly parsed as negative values.
|
||||||
|
*
|
||||||
|
* @param s the string to parse
|
||||||
|
* @param defaultValue the default value to return if the string does not represent a valid
|
||||||
|
* {@code long} value
|
||||||
|
* @return the parsed {@code long} value or the {@code defaultValue} if the string does not
|
||||||
|
* represent a valid {@code long} value
|
||||||
|
*/
|
||||||
|
public static long parseLong(String s, long defaultValue) {
|
||||||
|
try {
|
||||||
|
return parseLong(s);
|
||||||
}
|
}
|
||||||
if (s.startsWith("-")) {
|
catch (NumberFormatException e) {
|
||||||
sign = -1;
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given hex string as a {@code long} value.
|
||||||
|
* <p>
|
||||||
|
* Note: The string is treated as hex regardless of whether or not it contains the {@code 0x}
|
||||||
|
* prefix.
|
||||||
|
*
|
||||||
|
* @param s the string to parse
|
||||||
|
* @return the parsed {@code long} value
|
||||||
|
* @throws NumberFormatException if the string does not represent a valid value
|
||||||
|
*/
|
||||||
|
public static long parseHexLong(String s) throws NumberFormatException {
|
||||||
|
return parseHelper(s, true, BigInteger::longValue, MAX_UNSIGNED_LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given hex string as a {@link BigInteger} value.
|
||||||
|
* <p>
|
||||||
|
* Note: The string is treated as hex regardless of whether or not it contains the {@code 0x}
|
||||||
|
* prefix.
|
||||||
|
*
|
||||||
|
* @param s the string to parse
|
||||||
|
* @return the parsed {@link BigInteger} value
|
||||||
|
* @throws NumberFormatException if the string does not represent a valid value
|
||||||
|
* @deprecated use {@link #parseHexLong(String)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.3", forRemoval = true)
|
||||||
|
public static BigInteger parseHexBigInteger(String s) throws NumberFormatException {
|
||||||
|
return parseHelper(s, true, Function.identity(), MAX_UNSIGNED_LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given decimal/hex string as a custom type. This method allows values with the top
|
||||||
|
* bit set to be implicitly parsed as negative values.
|
||||||
|
*
|
||||||
|
* @param s the string to parse
|
||||||
|
* @param forceHex true if the string to parse should be treated as hex, even if it doesn't
|
||||||
|
* start with {@code 0x}; otherwise, false;
|
||||||
|
* @param func a {@link Function} used to convert the parsed {@link BigInteger} to a custom type
|
||||||
|
* @param max the maximum value that can be used to represent the type of value being parsed if
|
||||||
|
* it were treated as unsigned
|
||||||
|
* @param <T> The type of value being parsed
|
||||||
|
* @return the parsed value
|
||||||
|
* @throws NumberFormatException if the string does not represent a valid value
|
||||||
|
*/
|
||||||
|
private static <T> T parseHelper(String s, boolean forceHex, Function<BigInteger, T> func,
|
||||||
|
BigInteger max) throws NumberFormatException {
|
||||||
|
String origStr = s;
|
||||||
|
|
||||||
|
// Trim the string, and throw exception if it's null/empty
|
||||||
|
s = (s == null ? "" : s.trim());
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
throw new NumberFormatException("String to parse is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chop off the optional sign character of the string, we'll add it back later
|
||||||
|
String sign = "";
|
||||||
|
if (s.startsWith("-") || s.startsWith("+")) {
|
||||||
|
sign = s.substring(0, 1);
|
||||||
s = s.substring(1);
|
s = s.substring(1);
|
||||||
}
|
}
|
||||||
int radix = 10;
|
|
||||||
|
|
||||||
if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) {
|
// Process the radix
|
||||||
if (s.length() > 18) {
|
boolean hexPrefix = s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X);
|
||||||
throw new NumberFormatException(s + " has too many digits.");
|
int radix = forceHex || hexPrefix ? 16 : 10;
|
||||||
}
|
if (hexPrefix) {
|
||||||
s = s.substring(2);
|
s = s.substring(2);
|
||||||
radix = 16;
|
|
||||||
}
|
}
|
||||||
if (s.length() == 0) {
|
|
||||||
return 0;
|
// Make sure next character is not + or - (protects against things like "0x-ffff")
|
||||||
|
if (!s.isEmpty() && (s.charAt(0) == '-' || s.charAt(0) == '+')) {
|
||||||
|
throw new NumberFormatException("Cannot parse " + origStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to convert the string to the desired type
|
||||||
try {
|
try {
|
||||||
BigInteger bi = new BigInteger(s, radix);
|
|
||||||
return bi.longValue() * sign;
|
// Check size
|
||||||
|
if (new BigInteger(s, radix).compareTo(max) > 0) {
|
||||||
|
throw new NumberFormatException(s + " exceeds maximum data type size.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return func.apply(new BigInteger(sign + s, radix));
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
// A little hacky, but the message should be complete and report the original string
|
// A little hacky, but the message should be complete and report the original string
|
||||||
NumberFormatException e2 =
|
NumberFormatException e2 = new NumberFormatException("Cannot parse " + origStr);
|
||||||
new NumberFormatException("Cannot parse long from " + origStr);
|
|
||||||
e2.setStackTrace(e.getStackTrace());
|
e2.setStackTrace(e.getStackTrace());
|
||||||
throw e2;
|
throw e2;
|
||||||
}
|
}
|
||||||
@ -196,47 +261,6 @@ public final class NumericUtilities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the given string as a hex long value, detecting whether or not it begins with a hex
|
|
||||||
* prefix, and if not, parses as a long int value.
|
|
||||||
* @param s the string to parse
|
|
||||||
* @return the long value
|
|
||||||
* @throws NumberFormatException if the string is blank
|
|
||||||
*/
|
|
||||||
public static long parseHexLong(String s) {
|
|
||||||
return parseHexBigInteger(s).longValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the given hex string as a BigIntge value, detecting whether or not it begins with a
|
|
||||||
* hex prefix, and if not, parses as a long int value.
|
|
||||||
* @param s the string to parse
|
|
||||||
* @return the long value
|
|
||||||
* @throws NumberFormatException if the string is blank
|
|
||||||
*/
|
|
||||||
public static BigInteger parseHexBigInteger(String s) {
|
|
||||||
|
|
||||||
s = (s == null ? "" : s.trim());
|
|
||||||
if (s.length() == 0) {
|
|
||||||
throw new NumberFormatException(s + " no digits.");
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean negative = false;
|
|
||||||
if (s.startsWith("-")) {
|
|
||||||
negative = true;
|
|
||||||
s = s.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) {
|
|
||||||
s = s.substring(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (negative) {
|
|
||||||
s = "-" + s;
|
|
||||||
}
|
|
||||||
return new BigInteger(s, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BigInteger decodeMagnitude(int p, String s) {
|
private static BigInteger decodeMagnitude(int p, String s) {
|
||||||
// Special case, so it doesn't get chewed by octal parser
|
// Special case, so it doesn't get chewed by octal parser
|
||||||
if ("0".equals(s)) {
|
if ("0".equals(s)) {
|
||||||
@ -314,7 +338,7 @@ public final class NumericUtilities {
|
|||||||
* @return the string
|
* @return the string
|
||||||
*/
|
*/
|
||||||
public final static String toSignedHexString(long value) {
|
public final static String toSignedHexString(long value) {
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuilder buf = new StringBuilder();
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
buf.append("-");
|
buf.append("-");
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.util;
|
package ghidra.util;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -362,60 +361,194 @@ public class NumericUtilitiesTest {
|
|||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
NumericUtilities.decodeBigInteger("+");
|
NumericUtilities.decodeBigInteger("+");
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
NumericUtilities.decodeBigInteger("-");
|
NumericUtilities.decodeBigInteger("-");
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
NumericUtilities.decodeBigInteger("0x");
|
NumericUtilities.decodeBigInteger("0x");
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
NumericUtilities.decodeBigInteger("0b");
|
NumericUtilities.decodeBigInteger("0b");
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
NumericUtilities.decodeBigInteger("a01");
|
NumericUtilities.decodeBigInteger("a01");
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
NumericUtilities.decodeBigInteger("081");
|
NumericUtilities.decodeBigInteger("081");
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
NumericUtilities.decodeBigInteger("0x9g");
|
NumericUtilities.decodeBigInteger("0x9g");
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
NumericUtilities.decodeBigInteger(" 10");
|
NumericUtilities.decodeBigInteger(" 10");
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
NumericUtilities.decodeBigInteger("10 ");
|
NumericUtilities.decodeBigInteger("10 ");
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParse() {
|
||||||
|
|
||||||
|
// parseInt()
|
||||||
|
assertEquals(-1, NumericUtilities.parseInt("0xffffffff"));
|
||||||
|
assertEquals(-1, NumericUtilities.parseInt("+0xffffffff"));
|
||||||
|
assertEquals(1, NumericUtilities.parseInt("-0xffffffff"));
|
||||||
|
assertEquals(0, NumericUtilities.parseInt("0x100000000", 0));
|
||||||
|
assertEquals(-1, NumericUtilities.parseInt("4294967295")); // 0xffffffff
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseInt("0x100000000"));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseInt("4294967296")); // 0x100000000
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseInt("0x-5"));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseInt(null));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseInt(" "));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseLong()
|
||||||
|
assertEquals(-1, NumericUtilities.parseLong("0xffffffffffffffff"));
|
||||||
|
assertEquals(-1, NumericUtilities.parseLong("+0xffffffffffffffff"));
|
||||||
|
assertEquals(1, NumericUtilities.parseLong("-0xffffffffffffffff"));
|
||||||
|
assertEquals(0, NumericUtilities.parseLong("0x10000000000000000", 0));
|
||||||
|
assertEquals(-1, NumericUtilities.parseLong("18446744073709551615")); // 0xffffffffffffffff
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseLong("0x10000000000000000"));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseLong("18446744073709551616")); // 0x10000000000000000
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseInt("0x-5"));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseLong(null));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseLong(" "));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseHexLong()
|
||||||
|
assertEquals(-1, NumericUtilities.parseHexLong("ffffffffffffffff"));
|
||||||
|
assertEquals(-1, NumericUtilities.parseHexLong("0xffffffffffffffff"));
|
||||||
|
assertEquals(-1, NumericUtilities.parseHexLong("+ffffffffffffffff"));
|
||||||
|
assertEquals(-1, NumericUtilities.parseHexLong("+0xffffffffffffffff"));
|
||||||
|
assertEquals(1, NumericUtilities.parseHexLong("-ffffffffffffffff"));
|
||||||
|
assertEquals(1, NumericUtilities.parseHexLong("-0xffffffffffffffff"));
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseHexLong("0x10000000000000000"));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseHexLong("0x-5"));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseHexLong(null));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertEquals(0, NumericUtilities.parseHexLong(" "));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user