mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-877 LEB128 enhancements, cleanup
This commit is contained in:
parent
0380709881
commit
0e45354f24
58 changed files with 1363 additions and 1234 deletions
|
@ -0,0 +1,124 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import ghidra.docking.settings.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
import ghidra.util.classfinder.ClassTranslator;
|
||||
|
||||
/**
|
||||
* An abstract base class for a LEB128 variable length integer data type.
|
||||
* <p>
|
||||
* See {@link LEB128}.
|
||||
*/
|
||||
public abstract class AbstractLeb128DataType extends BuiltIn implements Dynamic {
|
||||
|
||||
/* package */ static final FormatSettingsDefinition FORMAT = FormatSettingsDefinition.DEF_HEX;
|
||||
|
||||
private static SettingsDefinition[] SETTINGS_DEFS = { FORMAT };
|
||||
|
||||
private final boolean signed;
|
||||
|
||||
/**
|
||||
* Base constructor for a little endian based 128 data type.
|
||||
* @param name name of the leb128 data type that extends this class.
|
||||
* @param signed true if it is signed. false if unsigned.
|
||||
* @param dtm the data type manager to associate with this data type.
|
||||
*/
|
||||
public AbstractLeb128DataType(String name, boolean signed, DataTypeManager dtm) {
|
||||
super(null, name, dtm);
|
||||
this.signed = signed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SettingsDefinition[] getBuiltInSettingsDefinitions() {
|
||||
return SETTINGS_DEFS;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength(MemBuffer buf, int maxLength) {
|
||||
if (maxLength < 0) {
|
||||
maxLength = LEB128.MAX_SUPPORTED_LENGTH;
|
||||
}
|
||||
try (InputStream is = buf.getInputStream(0, maxLength)) {
|
||||
return LEB128.getLength(is);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getValueClass(Settings settings) {
|
||||
return Scalar.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(MemBuffer buf, Settings settings, int maxLength) {
|
||||
if (maxLength < 0) {
|
||||
maxLength = LEB128.MAX_SUPPORTED_LENGTH;
|
||||
}
|
||||
|
||||
try (InputStream is = buf.getInputStream(0, maxLength)) {
|
||||
long val = LEB128.read(is, signed);
|
||||
return new Scalar(64 - Long.numberOfLeadingZeros(val), val, signed);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return null; // memory error, or more than 10 bytes long
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
|
||||
|
||||
Scalar val = (Scalar) getValue(buf, settings, length);
|
||||
if (val == null) {
|
||||
return "??";
|
||||
}
|
||||
|
||||
int radix = FORMAT.getRadix(settings);
|
||||
String postfix = FORMAT.getRepresentationPostfix(settings);
|
||||
|
||||
String valStr = val.toString(radix, false, signed, "", "");
|
||||
return valStr.toUpperCase() + postfix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getReplacementBaseType() {
|
||||
return ByteDataType.dataType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSpecifyLength() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultLabelPrefix() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
|
@ -15,9 +15,10 @@
|
|||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.docking.settings.SettingsDefinition;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
|
@ -290,16 +291,18 @@ public interface DataType {
|
|||
public URL getDocs();
|
||||
|
||||
/**
|
||||
* Get the interpreted data value in the form of the appropriate Object for this DataType.
|
||||
* This method must return a value consistent with {@link #getValueClass(Settings)}.
|
||||
* Returns the interpreted data value as an instance of the
|
||||
* {@link #getValueClass(Settings) advertised value class}.
|
||||
* <p>
|
||||
* For instance, if this datatype is a {@link Pointer} an Address object or null should be returned.
|
||||
* A Byte, returns a {@link Scalar} object.
|
||||
* For instance, {@link Pointer} data types should return an Address object (or null), or
|
||||
* integer data types should return a {@link Scalar} object.
|
||||
*
|
||||
* @param buf the data buffer.
|
||||
* @param buf the data buffer
|
||||
* @param settings the settings to use.
|
||||
* @param length the number of bytes to get the value from.
|
||||
* @return the data Object.
|
||||
* @param length indicates the maximum number of bytes that may be consumed by a
|
||||
* {@link Dynamic} datatype, otherwise this value is ignored. A value of -1 may be specified
|
||||
* to allow a Dynamic datatype to determine the length based upon the actual data bytes
|
||||
* @return the data object, or null if data is invalid
|
||||
*/
|
||||
public Object getValue(MemBuffer buf, Settings settings, int length);
|
||||
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Logic for reading LEB128 values.
|
||||
* <p>
|
||||
* LEB128 is a variable length integer encoding that uses 7 bits per byte, with the high bit
|
||||
* being reserved as a continuation flag, with the least significant bytes coming first
|
||||
* (<b>L</b>ittle <b>E</b>ndian <B>B</b>ase <b>128</b>).
|
||||
* <p>
|
||||
* This implementation only supports reading values that decode to at most 64 bits (to fit into
|
||||
* a java long).
|
||||
* <p>
|
||||
* When reading a value, you must already know if it was written as a signed or unsigned value to
|
||||
* be able to decode it correctly.
|
||||
*/
|
||||
public class LEB128 {
|
||||
/**
|
||||
* Max number of bytes that is supported by the deserialization code.
|
||||
*/
|
||||
public static final int MAX_SUPPORTED_LENGTH = 10;
|
||||
|
||||
/**
|
||||
* Reads an unsigned LEB128 variable length integer from the stream.
|
||||
*
|
||||
* @param is {@link InputStream} to get bytes from
|
||||
* @return leb128 value, as a long
|
||||
* @throws IOException if an I/O error occurs or decoded value is outside the range of a java
|
||||
* 64 bit int (or it used more than {@value #MAX_SUPPORTED_LENGTH} bytes to be encoded), or
|
||||
* there is an error or EOF getting a byte from the InputStream before reaching the end of the
|
||||
* encoded value
|
||||
*/
|
||||
public static long unsigned(InputStream is) throws IOException {
|
||||
return read(is, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed LEB128 variable length integer from the stream.
|
||||
*
|
||||
* @param is {@link InputStream} to get bytes from
|
||||
* @return leb128 value, as a long
|
||||
* @throws IOException if an I/O error occurs or decoded value is outside the range of a java
|
||||
* 64 bit int (or it used more than {@value #MAX_SUPPORTED_LENGTH} bytes to be encoded), or
|
||||
* there is an error or EOF getting a byte from the InputStream before reaching the end of the
|
||||
* encoded value
|
||||
*/
|
||||
public static long signed(InputStream is) throws IOException {
|
||||
return read(is, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a LEB128 number from the stream and returns it as a java 64 bit long int.
|
||||
* <p>
|
||||
* Large unsigned integers that use all 64 bits are returned in a java native
|
||||
* 'long' type, which is signed. It is up to the caller to treat the value as unsigned.
|
||||
* <p>
|
||||
* Large integers that use more than 64 bits will cause an IOException to be thrown.
|
||||
* <p>
|
||||
* @param is {@link InputStream} to get bytes from
|
||||
* @param isSigned true if the value is signed
|
||||
* @return long integer value. Caller must treat it as unsigned if isSigned parameter was
|
||||
* set to false
|
||||
* @throws IOException if an I/O error occurs or decoded value is outside the range of a java
|
||||
* 64 bit int (or it used more than {@value #MAX_SUPPORTED_LENGTH} bytes to be encoded), or
|
||||
* there is an error or EOF getting a byte from the InputStream before reaching the end of the
|
||||
* encoded value
|
||||
*/
|
||||
public static long read(InputStream is, boolean isSigned) throws IOException {
|
||||
int nextByte = 0;
|
||||
int shift = 0;
|
||||
long value = 0;
|
||||
while (true) {
|
||||
nextByte = is.read();
|
||||
if (nextByte < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
if (shift == 70 || (isSigned == false && shift == 63 && nextByte > 1)) {
|
||||
throw new IOException(
|
||||
"Unsupported LEB128 value, too large to fit in 64bit java long variable");
|
||||
}
|
||||
|
||||
// must cast to long before shifting otherwise shift values greater than 32 cause problems
|
||||
value |= ((long) (nextByte & 0x7F)) << shift;
|
||||
shift += 7;
|
||||
|
||||
if ((nextByte & 0x80) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((isSigned) && (shift < Long.SIZE) && ((nextByte & 0x40) != 0)) {
|
||||
// 0x40 is the new 'high' sign bit since 0x80 is the continuation flag.
|
||||
// bitwise-or in all the sign-extension bits we need for the value
|
||||
value |= (-1L << shift);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the variable length LEB128 value.
|
||||
*
|
||||
* @param is InputStream to get bytes from
|
||||
* @return length of the LEB128 value, or -1 if the end of the value is not found
|
||||
* @throws IOException if error getting next byte from stream
|
||||
*/
|
||||
public static int getLength(InputStream is) throws IOException {
|
||||
int length = 0;
|
||||
int nextByte;
|
||||
while ((nextByte = is.read()) >= 0 && length < MAX_SUPPORTED_LENGTH) {
|
||||
length++;
|
||||
if ((nextByte & 0x80) == 0) {
|
||||
return length;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a LEB128 number from a byte array and returns it as a long.
|
||||
* <p>
|
||||
* See {@link #read(InputStream, boolean)}
|
||||
*
|
||||
* @param bytes the bytes representing the LEB128 number
|
||||
* @param offset offset in byte array of where to start reading bytes
|
||||
* @param isSigned true if the value is signed
|
||||
* @return long integer value. Caller must treat it as unsigned if isSigned parameter was
|
||||
* set to false
|
||||
* @throws IOException if array offset is invalid, decoded value is outside the range of a java
|
||||
* 64 bit int (or it used more than {@value #MAX_SUPPORTED_LENGTH} bytes to be encoded), or
|
||||
* the end of the array was reached before reaching the end of the encoded value
|
||||
*/
|
||||
public static long decode(byte[] bytes, int offset, boolean isSigned) throws IOException {
|
||||
InputStream is = new ByteArrayInputStream(bytes, offset, bytes.length - offset);
|
||||
return read(is, isSigned);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import ghidra.util.classfinder.ClassTranslator;
|
||||
|
||||
/**
|
||||
* A Signed Little Endian Base 128 integer data type.
|
||||
*/
|
||||
public class SignedLeb128DataType extends AbstractLeb128DataType {
|
||||
static {
|
||||
ClassTranslator.put("ghidra.app.plugin.exceptionhandlers.gcc.datatype.SignedLeb128DataType",
|
||||
SignedLeb128DataType.class.getName());
|
||||
}
|
||||
|
||||
/** A statically defined SignedLeb128DataType instance.*/
|
||||
public final static SignedLeb128DataType dataType = new SignedLeb128DataType();
|
||||
|
||||
/**
|
||||
* Creates a signed little endian base 128 integer data type.
|
||||
*/
|
||||
public SignedLeb128DataType() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a signed little endian base 128 integer data type.
|
||||
* @param dtm the data type manager to associate with this data type.
|
||||
*/
|
||||
public SignedLeb128DataType(DataTypeManager dtm) {
|
||||
super("sleb128", true, dtm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType clone(DataTypeManager dtm) {
|
||||
if (dtm == getDataTypeManager()) {
|
||||
return this;
|
||||
}
|
||||
return new SignedLeb128DataType(dtm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Signed LEB128-Encoded Number";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import ghidra.util.classfinder.ClassTranslator;
|
||||
|
||||
/**
|
||||
* An Unsigned Little Endian Base 128 integer data type.
|
||||
*/
|
||||
public class UnsignedLeb128DataType extends AbstractLeb128DataType {
|
||||
static {
|
||||
ClassTranslator.put(
|
||||
"ghidra.app.plugin.exceptionhandlers.gcc.datatype.UnsignedLeb128DataType",
|
||||
UnsignedLeb128DataType.class.getName());
|
||||
}
|
||||
|
||||
/** A statically defined UnsignedLeb128DataType instance.*/
|
||||
public final static UnsignedLeb128DataType dataType = new UnsignedLeb128DataType();
|
||||
|
||||
/**
|
||||
* Creates an unsigned little endian base 128 integer data type.
|
||||
*/
|
||||
public UnsignedLeb128DataType() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unsigned little endian base 128 integer data type.
|
||||
* @param dtm the data type manager to associate with this data type.
|
||||
*/
|
||||
public UnsignedLeb128DataType(DataTypeManager dtm) {
|
||||
super("uleb128", false, dtm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType clone(DataTypeManager dtm) {
|
||||
if (dtm == getDataTypeManager()) {
|
||||
return this;
|
||||
}
|
||||
return new UnsignedLeb128DataType(dtm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Unsigned LEB128-Encoded Number";
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.program.model.mem;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -86,7 +87,7 @@ public interface MemBuffer {
|
|||
* @throws MemoryAccessException if memory cannot be read at the specified offset
|
||||
*/
|
||||
default public int getUnsignedByte(int offset) throws MemoryAccessException {
|
||||
return getByte(offset) & 0xff;
|
||||
return Byte.toUnsignedInt(getByte(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,7 +140,7 @@ public interface MemBuffer {
|
|||
* @throws MemoryAccessException if a 2-byte short value cannot be read at the specified offset
|
||||
*/
|
||||
default public int getUnsignedShort(int offset) throws MemoryAccessException {
|
||||
return getShort(offset) & 0xffff;
|
||||
return Short.toUnsignedInt(getShort(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,7 +158,7 @@ public interface MemBuffer {
|
|||
* @throws MemoryAccessException if a 4-byte integer value cannot be read at the specified offset
|
||||
*/
|
||||
default public long getUnsignedInt(int offset) throws MemoryAccessException {
|
||||
return getInt(offset) & 0xFFFF_FFFFL;
|
||||
return Integer.toUnsignedLong(getInt(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,4 +223,31 @@ public interface MemBuffer {
|
|||
throw new MemoryAccessException("Invalid length for read: " + len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream that supplies the bytes of this buffer, starting at offset 0.
|
||||
* <p>
|
||||
* Note: the default implementation will produce invalid results if the underlying
|
||||
* MemBuffer instance is is mutated to point to different memory.
|
||||
*
|
||||
* @return an InputStream that returns the bytes of this mem buffer
|
||||
*/
|
||||
default public InputStream getInputStream() {
|
||||
return new MemBufferInputStream(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream that supplies the bytes of this buffer, starting at the specified offset.
|
||||
* <p>
|
||||
* Note: the default implementation will produce invalid results if the underlying
|
||||
* MemBuffer instance is is mutated to point to different memory.
|
||||
*
|
||||
* @param initialPosition location in membuffer where the stream should start
|
||||
* @param length number of bytes to limit the stream to
|
||||
* @return an InputSTream that returns the bytes of this mem buffer
|
||||
*/
|
||||
default public InputStream getInputStream(int initialPosition, int length) {
|
||||
return new MemBufferInputStream(this, initialPosition, length);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.mem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Adapter between {@link MemBuffer membuffers} and {@link InputStream inputstreams}.
|
||||
*/
|
||||
public class MemBufferInputStream extends InputStream {
|
||||
|
||||
private MemBuffer membuf;
|
||||
private int currentPosition;
|
||||
private long maxPosition; // exclusive
|
||||
|
||||
/**
|
||||
* Creates a new instance, starting a offset 0 of the membuffer, limited to the first 2Gb
|
||||
* of the membuffer.
|
||||
*
|
||||
* @param membuf {@link MemBuffer} to wrap as an inputstream
|
||||
*/
|
||||
public MemBufferInputStream(MemBuffer membuf) {
|
||||
this(membuf, 0, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link MemBufferInputStream}, starting at the specified offset,
|
||||
* limited to the first {@code length} bytes.
|
||||
*
|
||||
* @param membuf {@link MemBuffer} to wrap as an inputstream
|
||||
* @param initialPosition starting position in the membuffer
|
||||
* @param length number of bytes to limit this inputstream to. The sum of
|
||||
* {@code initialPosition} and {@code length} must not exceed {@link Integer#MAX_VALUE}+1
|
||||
*/
|
||||
public MemBufferInputStream(MemBuffer membuf, int initialPosition, int length) {
|
||||
this.maxPosition = initialPosition + length;
|
||||
if (initialPosition < 0 || length < 0 || maxPosition > (long) Integer.MAX_VALUE + 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.membuf = membuf;
|
||||
this.currentPosition = initialPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.maxPosition = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return currentPosition >= 0 && currentPosition < maxPosition
|
||||
? (int) (maxPosition - currentPosition)
|
||||
: 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
try {
|
||||
if (currentPosition < 0 || currentPosition >= maxPosition) {
|
||||
return -1;
|
||||
}
|
||||
int result = Byte.toUnsignedInt(membuf.getByte(currentPosition));
|
||||
currentPosition++;
|
||||
return result;
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -32,7 +31,6 @@ public class MemoryAccessException extends UsrException
|
|||
super();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructs an MemoryAccessException with the specified
|
||||
* detail message.<p>
|
||||
|
@ -42,4 +40,15 @@ public class MemoryAccessException extends UsrException
|
|||
public MemoryAccessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link MemoryAccessException} with a message and cause.
|
||||
* @param msg message
|
||||
* @param cause nested cause
|
||||
*/
|
||||
public MemoryAccessException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
|
||||
} // MemoryAccessException
|
||||
|
|
|
@ -27,16 +27,8 @@ import ghidra.program.model.mem.MemBuffer;
|
|||
|
||||
public class IntegerDataTypeTest extends AbstractGenericTest {
|
||||
|
||||
private static byte[] arr(int... vals) {
|
||||
byte[] result = new byte[vals.length];
|
||||
for (int i = 0; i < vals.length; i++) {
|
||||
result[i] = (byte) vals[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static MemBuffer buf(boolean bigEndian, int... vals) {
|
||||
return new ByteMemBufferImpl(Address.NO_ADDRESS, arr(vals), bigEndian);
|
||||
return new ByteMemBufferImpl(Address.NO_ADDRESS, bytes(vals), bigEndian);
|
||||
}
|
||||
|
||||
private static Settings format(FormatSettingsDefinition setDef) {
|
||||
|
@ -74,13 +66,13 @@ public class IntegerDataTypeTest extends AbstractGenericTest {
|
|||
DataType type = AbstractIntegerDataType.getUnsignedDataType(1, null);
|
||||
|
||||
// Technically, these two are exactly the same test, just different Java syntax
|
||||
assertArrayEquals(arr(0xff), type.encodeValue((byte) 0xff, BE, HEX, 1));
|
||||
assertArrayEquals(arr(0xff), type.encodeValue((byte) -1, BE, HEX, 1));
|
||||
assertArrayEquals(bytes(0xff), type.encodeValue((byte) 0xff, BE, HEX, 1));
|
||||
assertArrayEquals(bytes(0xff), type.encodeValue((byte) -1, BE, HEX, 1));
|
||||
|
||||
assertFails(() -> type.encodeValue((short) 0x100, BE, HEX, 1));
|
||||
assertFails(() -> type.encodeValue((short) -1, BE, HEX, 1));
|
||||
|
||||
assertArrayEquals(arr(0xff), type.encodeValue(0xff, BE, HEX, 1));
|
||||
assertArrayEquals(bytes(0xff), type.encodeValue(0xff, BE, HEX, 1));
|
||||
// This fails, because (int)-1 is 4294967295 when treated unsigned
|
||||
assertFails(() -> type.encodeValue(-1, BE, HEX, 1));
|
||||
}
|
||||
|
@ -92,10 +84,10 @@ public class IntegerDataTypeTest extends AbstractGenericTest {
|
|||
// Sanity check: Renders unsigned
|
||||
assertEquals("80h", type.getRepresentation(buf(true, 0x80), HEX, 1));
|
||||
|
||||
assertArrayEquals(arr(0x00), type.encodeRepresentation("0h", BE, HEX, 1));
|
||||
assertArrayEquals(arr(0x7f), type.encodeRepresentation("7fh", BE, HEX, 1));
|
||||
assertArrayEquals(arr(0x80), type.encodeRepresentation("80h", BE, HEX, 1));
|
||||
assertArrayEquals(arr(0xff), type.encodeRepresentation("ffh", BE, HEX, 1));
|
||||
assertArrayEquals(bytes(0x00), type.encodeRepresentation("0h", BE, HEX, 1));
|
||||
assertArrayEquals(bytes(0x7f), type.encodeRepresentation("7fh", BE, HEX, 1));
|
||||
assertArrayEquals(bytes(0x80), type.encodeRepresentation("80h", BE, HEX, 1));
|
||||
assertArrayEquals(bytes(0xff), type.encodeRepresentation("ffh", BE, HEX, 1));
|
||||
|
||||
assertFails(() -> type.encodeRepresentation("100h", BE, HEX, 1));
|
||||
assertFails(() -> type.encodeRepresentation("-1h", BE, HEX, 1));
|
||||
|
@ -108,13 +100,13 @@ public class IntegerDataTypeTest extends AbstractGenericTest {
|
|||
// Sanity check: Negative hex values render unsigned
|
||||
assertEquals("8000h", type.getRepresentation(buf(true, 0x80, 0x00), HEX, 2));
|
||||
|
||||
assertArrayEquals(arr(0x00, 0x00), type.encodeRepresentation("0h", BE, HEX, 2));
|
||||
assertArrayEquals(arr(0x7f, 0xff), type.encodeRepresentation("7fffh", BE, HEX, 2));
|
||||
assertArrayEquals(arr(0x80, 0x00), type.encodeRepresentation("8000h", BE, HEX, 2));
|
||||
assertArrayEquals(arr(0xff, 0xff), type.encodeRepresentation("ffffh", BE, HEX, 2));
|
||||
assertArrayEquals(bytes(0x00, 0x00), type.encodeRepresentation("0h", BE, HEX, 2));
|
||||
assertArrayEquals(bytes(0x7f, 0xff), type.encodeRepresentation("7fffh", BE, HEX, 2));
|
||||
assertArrayEquals(bytes(0x80, 0x00), type.encodeRepresentation("8000h", BE, HEX, 2));
|
||||
assertArrayEquals(bytes(0xff, 0xff), type.encodeRepresentation("ffffh", BE, HEX, 2));
|
||||
|
||||
assertArrayEquals(arr(0xff, 0xff), type.encodeRepresentation("-1h", BE, HEX, 2));
|
||||
assertArrayEquals(arr(0x80, 0x00), type.encodeRepresentation("-8000h", BE, HEX, 2));
|
||||
assertArrayEquals(bytes(0xff, 0xff), type.encodeRepresentation("-1h", BE, HEX, 2));
|
||||
assertArrayEquals(bytes(0x80, 0x00), type.encodeRepresentation("-8000h", BE, HEX, 2));
|
||||
|
||||
assertFails(() -> type.encodeRepresentation("10000h", BE, HEX, 2));
|
||||
assertFails(() -> type.encodeRepresentation("-8001h", BE, HEX, 2));
|
||||
|
@ -127,13 +119,13 @@ public class IntegerDataTypeTest extends AbstractGenericTest {
|
|||
// Sanity check: Negative hex values render unsigned
|
||||
assertEquals("8000h", type.getRepresentation(buf(false, 0x00, 0x80), HEX, 2));
|
||||
|
||||
assertArrayEquals(arr(0x00, 0x00), type.encodeRepresentation("0h", LE, HEX, 2));
|
||||
assertArrayEquals(arr(0xff, 0x7f), type.encodeRepresentation("7fffh", LE, HEX, 2));
|
||||
assertArrayEquals(arr(0x00, 0x80), type.encodeRepresentation("8000h", LE, HEX, 2));
|
||||
assertArrayEquals(arr(0xff, 0xff), type.encodeRepresentation("ffffh", LE, HEX, 2));
|
||||
assertArrayEquals(bytes(0x00, 0x00), type.encodeRepresentation("0h", LE, HEX, 2));
|
||||
assertArrayEquals(bytes(0xff, 0x7f), type.encodeRepresentation("7fffh", LE, HEX, 2));
|
||||
assertArrayEquals(bytes(0x00, 0x80), type.encodeRepresentation("8000h", LE, HEX, 2));
|
||||
assertArrayEquals(bytes(0xff, 0xff), type.encodeRepresentation("ffffh", LE, HEX, 2));
|
||||
|
||||
assertArrayEquals(arr(0xff, 0xff), type.encodeRepresentation("-1h", LE, HEX, 2));
|
||||
assertArrayEquals(arr(0x00, 0x80), type.encodeRepresentation("-8000h", LE, HEX, 2));
|
||||
assertArrayEquals(bytes(0xff, 0xff), type.encodeRepresentation("-1h", LE, HEX, 2));
|
||||
assertArrayEquals(bytes(0x00, 0x80), type.encodeRepresentation("-8000h", LE, HEX, 2));
|
||||
|
||||
assertFails(() -> type.encodeRepresentation("10000h", LE, HEX, 2));
|
||||
assertFails(() -> type.encodeRepresentation("-8001h", LE, HEX, 2));
|
||||
|
@ -146,10 +138,10 @@ public class IntegerDataTypeTest extends AbstractGenericTest {
|
|||
// Sanity check: Renders unsigned
|
||||
assertEquals("8000h", type.getRepresentation(buf(true, 0x80, 0x00), HEX, 2));
|
||||
|
||||
assertArrayEquals(arr(0x00, 0x00), type.encodeRepresentation("0h", BE, HEX, 2));
|
||||
assertArrayEquals(arr(0x7f, 0xff), type.encodeRepresentation("7fffh", BE, HEX, 2));
|
||||
assertArrayEquals(arr(0x80, 0x00), type.encodeRepresentation("8000h", BE, HEX, 2));
|
||||
assertArrayEquals(arr(0xff, 0xff), type.encodeRepresentation("ffffh", BE, HEX, 2));
|
||||
assertArrayEquals(bytes(0x00, 0x00), type.encodeRepresentation("0h", BE, HEX, 2));
|
||||
assertArrayEquals(bytes(0x7f, 0xff), type.encodeRepresentation("7fffh", BE, HEX, 2));
|
||||
assertArrayEquals(bytes(0x80, 0x00), type.encodeRepresentation("8000h", BE, HEX, 2));
|
||||
assertArrayEquals(bytes(0xff, 0xff), type.encodeRepresentation("ffffh", BE, HEX, 2));
|
||||
|
||||
assertFails(() -> type.encodeRepresentation("-1h", BE, HEX, 2));
|
||||
assertFails(() -> type.encodeRepresentation("-8000h", BE, HEX, 2));
|
||||
|
@ -164,10 +156,10 @@ public class IntegerDataTypeTest extends AbstractGenericTest {
|
|||
// Sanity check: Negative hex values render signed
|
||||
assertEquals("-32768", type.getRepresentation(buf(true, 0x80, 0x00), DEC, 2));
|
||||
|
||||
assertArrayEquals(arr(0x00, 0x00), type.encodeRepresentation("0", BE, DEC, 2));
|
||||
assertArrayEquals(arr(0x7f, 0xff), type.encodeRepresentation("32767", BE, DEC, 2));
|
||||
assertArrayEquals(arr(0x80, 0x00), type.encodeRepresentation("-32768", BE, DEC, 2));
|
||||
assertArrayEquals(arr(0xff, 0xff), type.encodeRepresentation("-1", BE, DEC, 2));
|
||||
assertArrayEquals(bytes(0x00, 0x00), type.encodeRepresentation("0", BE, DEC, 2));
|
||||
assertArrayEquals(bytes(0x7f, 0xff), type.encodeRepresentation("32767", BE, DEC, 2));
|
||||
assertArrayEquals(bytes(0x80, 0x00), type.encodeRepresentation("-32768", BE, DEC, 2));
|
||||
assertArrayEquals(bytes(0xff, 0xff), type.encodeRepresentation("-1", BE, DEC, 2));
|
||||
|
||||
assertFails(() -> type.encodeRepresentation("32768", BE, DEC, 2));
|
||||
assertFails(() -> type.encodeRepresentation("-32769", BE, DEC, 2));
|
||||
|
@ -180,10 +172,10 @@ public class IntegerDataTypeTest extends AbstractGenericTest {
|
|||
// Sanity check: Renders unsigned
|
||||
assertEquals("32768", type.getRepresentation(buf(true, 0x80, 0x00), DEC, 2));
|
||||
|
||||
assertArrayEquals(arr(0x00, 0x00), type.encodeRepresentation("0", BE, DEC, 2));
|
||||
assertArrayEquals(arr(0x7f, 0xff), type.encodeRepresentation("32767", BE, DEC, 2));
|
||||
assertArrayEquals(arr(0x80, 0x00), type.encodeRepresentation("32768", BE, DEC, 2));
|
||||
assertArrayEquals(arr(0xff, 0xff), type.encodeRepresentation("65535", BE, DEC, 2));
|
||||
assertArrayEquals(bytes(0x00, 0x00), type.encodeRepresentation("0", BE, DEC, 2));
|
||||
assertArrayEquals(bytes(0x7f, 0xff), type.encodeRepresentation("32767", BE, DEC, 2));
|
||||
assertArrayEquals(bytes(0x80, 0x00), type.encodeRepresentation("32768", BE, DEC, 2));
|
||||
assertArrayEquals(bytes(0xff, 0xff), type.encodeRepresentation("65535", BE, DEC, 2));
|
||||
|
||||
assertFails(() -> type.encodeRepresentation("-1", BE, DEC, 2));
|
||||
assertFails(() -> type.encodeRepresentation("65536", BE, DEC, 2));
|
||||
|
@ -196,7 +188,7 @@ public class IntegerDataTypeTest extends AbstractGenericTest {
|
|||
// Sanity check
|
||||
assertEquals("100000011b", type.getRepresentation(buf(true, 0x01, 0x03), BIN, 2));
|
||||
|
||||
assertArrayEquals(arr(0x01, 0x03), type.encodeRepresentation("100000011b", BE, BIN, 2));
|
||||
assertArrayEquals(bytes(0x01, 0x03), type.encodeRepresentation("100000011b", BE, BIN, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -206,7 +198,7 @@ public class IntegerDataTypeTest extends AbstractGenericTest {
|
|||
// Sanity check
|
||||
assertEquals("403o", type.getRepresentation(buf(true, 0x01, 0x03), OCT, 2));
|
||||
|
||||
assertArrayEquals(arr(0x01, 0x03), type.encodeRepresentation("403o", BE, OCT, 2));
|
||||
assertArrayEquals(bytes(0x01, 0x03), type.encodeRepresentation("403o", BE, OCT, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -218,7 +210,7 @@ public class IntegerDataTypeTest extends AbstractGenericTest {
|
|||
assertEquals("'A'", stype.getRepresentation(buf(true, 0x41), CHR, 1));
|
||||
assertEquals("'A'", utype.getRepresentation(buf(true, 0x41), CHR, 1));
|
||||
|
||||
assertArrayEquals(arr(0x41), stype.encodeRepresentation("'A'", BE, CHR, 1));
|
||||
assertArrayEquals(arr(0x41), utype.encodeRepresentation("'A'", BE, CHR, 1));
|
||||
assertArrayEquals(bytes(0x41), stype.encodeRepresentation("'A'", BE, CHR, 1));
|
||||
assertArrayEquals(bytes(0x41), utype.encodeRepresentation("'A'", BE, CHR, 1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import static ghidra.program.model.data.LEB128Test.te;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.docking.settings.FormatSettingsDefinition;
|
||||
import ghidra.program.model.data.LEB128Test.TestEntry;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
|
||||
public class LEB128DataTypeTest extends AbstractGenericTest {
|
||||
|
||||
static SettingsBuilder HEX_SETTINGS = new SettingsBuilder()
|
||||
.setFormat(FormatSettingsDefinition.HEX, AbstractLeb128DataType.FORMAT);
|
||||
static SettingsBuilder DECIMAL_SETTINGS = new SettingsBuilder()
|
||||
.setFormat(FormatSettingsDefinition.DECIMAL, AbstractLeb128DataType.FORMAT);
|
||||
|
||||
@Test
|
||||
public void testStringRep() throws IOException {
|
||||
|
||||
assertRepresentation(te(1L, 0x01), false, "1h", "1");
|
||||
assertRepresentation(te(0L, 0x80, 0x80, 0x80, 0x80, 0x80, 0x0), false, "0h", "0");
|
||||
assertRepresentation(te(1L, 0x81, 0x80, 0x80, 0x80, 0x80, 0x0), false, "1h", "1");
|
||||
assertRepresentation(te(-1L, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01),
|
||||
false, "FFFFFFFFFFFFFFFFh", "18446744073709551615");
|
||||
assertRepresentation(te(63L, 0x3f), false, "3Fh", "63");
|
||||
assertRepresentation(te(128L, 0x80, 0x01), false, "80h", "128");
|
||||
assertRepresentation(te(2097151L, 0xff, 0xff, 0x7f), false, "1FFFFFh", "2097151");
|
||||
|
||||
assertRepresentation(te(-1L, 0x7f), true, "-1h", "-1");
|
||||
assertRepresentation(te(-2L, 0x7e), true, "-2h", "-2");
|
||||
assertRepresentation(te(-64L, 0x40), true, "-40h", "-64");
|
||||
assertRepresentation(te(-127, 0x81, 0x7f), true, "-7Fh", "-127");
|
||||
assertRepresentation(te(-128, 0x80, 0x7f), true, "-80h", "-128");
|
||||
assertRepresentation(te(-8193L, 0xff, 0xbf, 0x7f), true, "-2001h", "-8193");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEnoughBytes() throws IOException {
|
||||
// tests what happens when this var length data type is contained within a dtc
|
||||
// that is too small for what the data demands.
|
||||
MemBuffer mb = te(0L, 0x80, 0x0, 0x0, 0x0).mb();
|
||||
UnsignedLeb128DataType uleb128 = UnsignedLeb128DataType.dataType;
|
||||
Scalar value = (Scalar) uleb128.getValue(mb, HEX_SETTINGS, 1);
|
||||
String hexRep = uleb128.getRepresentation(mb, HEX_SETTINGS, 1);
|
||||
|
||||
assertNull(value);
|
||||
assertEquals("??", hexRep);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTooManyBytes() throws IOException {
|
||||
// tests what happens when this var length data type is contained within a dtc
|
||||
// that has more bytes than what the data demands.
|
||||
MemBuffer mb = te(0L, 0x80, 0x0, 0x0, 0x0).mb();
|
||||
UnsignedLeb128DataType uleb128 = UnsignedLeb128DataType.dataType;
|
||||
Scalar value = (Scalar) uleb128.getValue(mb, HEX_SETTINGS, 4);
|
||||
String hexRep = uleb128.getRepresentation(mb, HEX_SETTINGS, 4);
|
||||
|
||||
assertEquals(0, value.getUnsignedValue());
|
||||
assertEquals("0h", hexRep);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetValueWithoutLength() throws IOException {
|
||||
MemBuffer mb = te(0L, 0x80, 0x0, 0x0, 0x0).mb();
|
||||
UnsignedLeb128DataType uleb128 = UnsignedLeb128DataType.dataType;
|
||||
Scalar value = (Scalar) uleb128.getValue(mb, HEX_SETTINGS, -1);
|
||||
assertNotNull(value);
|
||||
}
|
||||
|
||||
private void assertRepresentation(TestEntry te, boolean signed, String hexExpected,
|
||||
String decExpected) throws IOException {
|
||||
|
||||
MemBuffer mb = te.mb();
|
||||
//Scalar value = (Scalar) dt.getValue(mb, settings, length);
|
||||
AbstractLeb128DataType lebdt = signed
|
||||
? SignedLeb128DataType.dataType
|
||||
: UnsignedLeb128DataType.dataType;
|
||||
int length = lebdt.getLength(mb, -1);
|
||||
//Scalar value = (Scalar) dt.getValue(mb, settings, length);
|
||||
String hexRep = lebdt.getRepresentation(mb, HEX_SETTINGS, length);
|
||||
String decRep = lebdt.getRepresentation(mb, DECIMAL_SETTINGS, length);
|
||||
|
||||
assertEquals(te.bytes().length, length);
|
||||
assertEquals(hexExpected, hexRep);
|
||||
assertEquals(decExpected, decRep);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.mem.ByteMemBufferImpl;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.util.NumericUtilities;
|
||||
|
||||
public class LEB128Test extends AbstractGTest {
|
||||
static record TestEntry(long expectedValue, byte[] bytes) {
|
||||
MemBuffer mb() {
|
||||
return new ByteMemBufferImpl(null, bytes, false /* don't matter */);
|
||||
}
|
||||
}
|
||||
|
||||
private static InputStream is(int... intBytes) {
|
||||
return is(bytes(intBytes));
|
||||
}
|
||||
|
||||
private static InputStream is(byte[] bytes) {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||
return bais;
|
||||
}
|
||||
|
||||
static TestEntry te(long expectedValue, int... intBytes) {
|
||||
return new TestEntry(expectedValue, bytes(intBytes));
|
||||
}
|
||||
|
||||
/* package */ List<TestEntry> unsignedTestEntries = List.of(
|
||||
// misc
|
||||
te(0L, 0x80, 0x80, 0x80, 0x80, 0x80, 0x0), // Tests reading a zero value that is encoded in non-optimal way.
|
||||
te(1L, 0x81, 0x80, 0x80, 0x80, 0x80, 0x0), // Tests reading a 1 value that is encoded in non-optimal way.
|
||||
te(-1L, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01), // -1 == MAX unsigned long
|
||||
te(0xf_ffff_ffffL, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01), // more than 32 bits to test shifting > 32bits
|
||||
|
||||
// 1 byte
|
||||
te(1L, 0x01),
|
||||
te(63L, 0x3f),
|
||||
te(64L, 0x40),
|
||||
|
||||
// 1 byte to 2 byte transition
|
||||
te(125L, 0x7d),
|
||||
te(126L, 0x7e),
|
||||
te(127L, 0x7f),
|
||||
te(128L, 0x80, 0x01),
|
||||
te(129L, 0x81, 0x01),
|
||||
te(130L, 0x82, 0x01),
|
||||
te(131L, 0x83, 0x01),
|
||||
|
||||
te(254L, 0xfe, 0x01),
|
||||
te(255L, 0xff, 0x01),
|
||||
te(256L, 0x80, 0x02),
|
||||
te(257L, 0x81, 0x02),
|
||||
|
||||
// 2 byte to 3 byte transition
|
||||
te(16382L, 0xfe, 0x7f),
|
||||
te(16383L, 0xff, 0x7f),
|
||||
te(16384L, 0x80, 0x80, 0x01),
|
||||
te(16385L, 0x81, 0x80, 0x01),
|
||||
|
||||
// 3 byte to 4 byte transition
|
||||
te(2097151L, 0xff, 0xff, 0x7f),
|
||||
te(2097152L, 0x80, 0x80, 0x80, 0x01),
|
||||
te(2097153L, 0x81, 0x80, 0x80, 0x01),
|
||||
|
||||
// 4 byte to 5 byte transition
|
||||
te(268435455L, 0xff, 0xff, 0xff, 0x7f),
|
||||
te(268435456L, 0x80, 0x80, 0x80, 0x80, 0x01),
|
||||
te(268435457L, 0x81, 0x80, 0x80, 0x80, 0x01),
|
||||
|
||||
// 5 byte to 6 byte transition
|
||||
te(34359738367L, 0xff, 0xff, 0xff, 0xff, 0x7f),
|
||||
te(34359738368L, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01),
|
||||
te(34359738369L, 0x81, 0x80, 0x80, 0x80, 0x80, 0x01)
|
||||
//
|
||||
);
|
||||
|
||||
/* package */ List<TestEntry> signedTestEntries = List.of(
|
||||
// misc
|
||||
te(-2130303778817L, 0xff, 0xff, 0xff, 0xff, 0xff, 0x41),
|
||||
|
||||
// 1 byte positive stuff
|
||||
te(0L, 0x00),
|
||||
te(1L, 0x01),
|
||||
|
||||
// 1 byte to 2 byte transition (positive)
|
||||
te(63L, 0x3f),
|
||||
te(64L, 0xc0, 0x00),
|
||||
te(65L, 0xc1, 0x00),
|
||||
te(66L, 0xc2, 0x00),
|
||||
|
||||
te(126L, 0xfe, 0x00),
|
||||
te(127L, 0xff, 0x00),
|
||||
te(128L, 0x80, 0x01),
|
||||
te(129L, 0x81, 0x01),
|
||||
|
||||
te(254L, 0xfe, 0x01),
|
||||
te(255L, 0xff, 0x01),
|
||||
te(256L, 0x80, 0x02),
|
||||
te(257L, 0x81, 0x02),
|
||||
|
||||
// 2 byte to 3 byte transition
|
||||
te(8190L, 0xfe, 0x3f),
|
||||
te(8191L, 0xff, 0x3f),
|
||||
te(8192L, 0x80, 0xc0, 0x00),
|
||||
te(8193L, 0x81, 0xc0, 0x00),
|
||||
|
||||
// 1 byte negative stuff
|
||||
te(-1L, 0x7f),
|
||||
te(-2L, 0x7e),
|
||||
te(-3L, 0x7d),
|
||||
te(-4L, 0x7c),
|
||||
te(-5L, 0x7b),
|
||||
te(-6L, 0x7a),
|
||||
|
||||
// 1 byte to 2 byte transition (negative)
|
||||
te(-64L, 0x40),
|
||||
te(-65L, 0xbf, 0x7f),
|
||||
|
||||
te(-127, 0x81, 0x7f),
|
||||
te(-128, 0x80, 0x7f),
|
||||
te(-129, 0xff, 0x7e),
|
||||
|
||||
// 2 byte to 3 byte transition (negative)
|
||||
te(-8191L, 0x81, 0x40),
|
||||
te(-8192L, 0x80, 0x40),
|
||||
te(-8193L, 0xff, 0xbf, 0x7f),
|
||||
te(-8194L, 0xfe, 0xbf, 0x7f)
|
||||
|
||||
);
|
||||
|
||||
@Test
|
||||
public void testUnsignedTestEntries() throws IOException {
|
||||
testTestEntries(unsignedTestEntries, false, "Unsigned TestEntry");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignedTestEntries() throws IOException {
|
||||
testTestEntries(signedTestEntries, true, "Signed TestEntry");
|
||||
}
|
||||
|
||||
public void testTestEntries(List<TestEntry> testEntries, boolean signed, String name)
|
||||
throws IOException {
|
||||
for (int i = 0; i < testEntries.size(); i++) {
|
||||
TestEntry te = testEntries.get(i);
|
||||
InputStream is = is(te.bytes);
|
||||
long actualValue = LEB128.read(is, signed);
|
||||
int remainder = is.available();
|
||||
assertEquals(String.format(
|
||||
"%s[%d] failed: leb128(%s) != %d. Expected=%d / %x, actual=%d / %x",
|
||||
name, i, NumericUtilities.convertBytesToString(te.bytes), te.expectedValue,
|
||||
te.expectedValue, te.expectedValue, actualValue, actualValue), te.expectedValue,
|
||||
actualValue);
|
||||
assertEquals(String.format("%s[%d] failed: left-over bytes: %d", name, i, remainder),
|
||||
0, is.available());
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testToolargeUnsigned() throws IOException {
|
||||
// Test reading a unsigned LEB128 that is just 1 bit too large for a java 64 bit long int.
|
||||
InputStream is = is(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02);
|
||||
|
||||
long value = LEB128.read(is, false);
|
||||
Assert.fail(
|
||||
"Should not be able to read a LEB128 that is larger than what can fit in java 64bit long int: " +
|
||||
value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTooLargeValueBinaryReaderStreamPosition() throws IOException {
|
||||
// Test that the BinaryReader stream is 'correctly' positioned after the LEB128 bytes after
|
||||
// reading a LEB128 that is too large for a java 64 bit long int.
|
||||
|
||||
byte[] bytes =
|
||||
bytes(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x1, 0x2);
|
||||
InputStream is = is(bytes);
|
||||
|
||||
try {
|
||||
long value = LEB128.read(is, false);
|
||||
Assert.fail(
|
||||
"Should not be able to read a LEB128 that is larger than what can fit in java 64bit long int: " +
|
||||
Long.toUnsignedString(value));
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
// good
|
||||
}
|
||||
|
||||
Assert.assertEquals(bytes.length - 10, is.available());
|
||||
}
|
||||
|
||||
}
|
|
@ -73,6 +73,18 @@ public class SettingsBuilder implements Settings {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link FormatSettingsDefinition} value.
|
||||
*
|
||||
* @param formatEnum int such as {@link FormatSettingsDefinition#DECIMAL}
|
||||
* @param defaultSettings the default settings that apply
|
||||
* @return chainable SettingsBuilder
|
||||
*/
|
||||
public SettingsBuilder setFormat(int formatEnum, FormatSettingsDefinition defaultSettings) {
|
||||
defaultSettings.setChoice(settings, formatEnum);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLong(String name) {
|
||||
return settings.getLong(name);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue