mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-19 13:27:47 +08:00
Merge remote-tracking branch 'origin/GP-6627_dev747368_cleanup_numericutils_byteiterator--SQUASHED'
This commit is contained in:
+3
-3
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -101,7 +101,7 @@ public abstract class BasicChecksumAlgorithm extends ChecksumAlgorithm {
|
||||
if (monitor.isCancelled()) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
long b = bytes.next() & 0xFF;
|
||||
long b = Byte.toUnsignedLong(bytes.nextByte());
|
||||
long next = (size == SupportedByteSize.CHECKSUM8) ? b
|
||||
: b << ((numBytes - 1) - i % numBytes) * 8;
|
||||
if (xor) {
|
||||
|
||||
+3
-3
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -87,7 +87,7 @@ public class CRC16CCITTChecksumAlgorithm extends ChecksumAlgorithm {
|
||||
if (monitor.isCancelled()) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
byte b = it.next();
|
||||
byte b = it.nextByte();
|
||||
int value = 0;
|
||||
if (b < 0) {
|
||||
value = b + 256;
|
||||
|
||||
+3
-3
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -88,7 +88,7 @@ public class CRC16ChecksumAlgorithm extends ChecksumAlgorithm {
|
||||
if (monitor.isCancelled())
|
||||
throw new CancelledException();
|
||||
|
||||
byte b = it.next();
|
||||
byte b = it.nextByte();
|
||||
crc = crctab16[(int) ((crc ^ b) & 0xff)] ^ (crc >> 8);
|
||||
}
|
||||
if (onesComp) {
|
||||
|
||||
+3
-3
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -53,7 +53,7 @@ public abstract class DigestChecksumAlgorithm extends ChecksumAlgorithm {
|
||||
if (monitor.isCancelled()) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
digester.update(bytes.next());
|
||||
digester.update(bytes.nextByte());
|
||||
}
|
||||
checksum = digester.digest();
|
||||
}
|
||||
|
||||
+10
-22
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* 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.
|
||||
* 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.
|
||||
@@ -16,33 +15,22 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.checksums;
|
||||
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.util.MemoryByteIterator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.util.MemoryByteIterator;
|
||||
|
||||
class MemoryInputStream extends InputStream {
|
||||
MemoryByteIterator it;
|
||||
|
||||
MemoryInputStream(Memory mem, AddressSetView set) {
|
||||
it = new MemoryByteIterator(mem, set);
|
||||
}
|
||||
/**
|
||||
* @see java.io.InputStream#read()
|
||||
*/
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
try {
|
||||
if (it.hasNext()) {
|
||||
return it.next() & 0xff;
|
||||
}
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
StackTraceElement ste = e.getStackTrace()[0];
|
||||
throw new IOException(e.toString());
|
||||
}
|
||||
return -1;
|
||||
public int read() throws IOException {
|
||||
return it.hasNext() ? Byte.toUnsignedInt(it.nextByte()) : -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ public abstract class ByteCopier {
|
||||
TaskMonitor monitor) {
|
||||
|
||||
Memory memory = currentProgram.getMemory();
|
||||
ByteIterator bytes = new ByteIterator(addresses, memory);
|
||||
MemoryByteIterator bytes = new MemoryByteIterator(memory, addresses);
|
||||
return NumericUtilities.convertBytesToString(bytes, delimiter);
|
||||
}
|
||||
|
||||
@@ -545,53 +545,6 @@ public abstract class ByteCopier {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An iterator of bytes from memory. This class exists because the {@link MemoryByteIterator}
|
||||
* throws an exception from its next() method, which will not work for us.
|
||||
*/
|
||||
private static class ByteIterator implements Iterator<Byte> {
|
||||
|
||||
private MemoryByteIterator byteIterator;
|
||||
private Byte next;
|
||||
|
||||
ByteIterator(AddressSetView addresses, Memory memory) {
|
||||
byteIterator = new MemoryByteIterator(memory, addresses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
|
||||
if (next != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!byteIterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
next = byteIterator.next();
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
Msg.error(this, "Unable to read next byte", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte next() {
|
||||
|
||||
if (next == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
Byte result = next;
|
||||
next = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProgrammingByteStringTransferable implements Transferable {
|
||||
|
||||
private List<DataFlavor> flavorList;
|
||||
|
||||
+94
-30
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package ghidra.program.model.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Spliterators;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
@@ -25,17 +28,11 @@ import ghidra.program.model.listing.Program;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
|
||||
|
||||
public class MemoryByteIteratorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
private TestEnv env;
|
||||
private Program program;
|
||||
private AddressFactory af;
|
||||
/**
|
||||
* @param arg0
|
||||
*/
|
||||
public MemoryByteIteratorTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
private Address addr(String a) {
|
||||
return af.getAddress(a);
|
||||
}
|
||||
@@ -45,46 +42,113 @@ public class MemoryByteIteratorTest extends AbstractGhidraHeadedIntegrationTest
|
||||
builder.createMemory("test1", "0x1001000", 0x6600);
|
||||
builder.setBytes("0x1001000", "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
|
||||
builder.setBytes("0x1001140", "DE AD BE EF");
|
||||
builder.setBytes("0x1004ffe", "EE FF"); // these values are right before the buffer boundary
|
||||
builder.setBytes("0x1005000", "AA BB CC DD"); // these values are right after the buffer boundary
|
||||
return builder.getProgram();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
env = new TestEnv();
|
||||
program = buildProgram("notepad", ProgramBuilder._TOY);
|
||||
af = program.getAddressFactory();
|
||||
af = program.getAddressFactory();
|
||||
}
|
||||
@After
|
||||
public void tearDown() {
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
env.release(program);
|
||||
env.dispose();
|
||||
}
|
||||
@Test
|
||||
public void testIterator() throws Exception {
|
||||
|
||||
@Test
|
||||
public void testEmptyRange() {
|
||||
MemoryByteIterator it = new MemoryByteIterator(program.getMemory(), new AddressSet());
|
||||
long count =
|
||||
StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false).count();
|
||||
assertEquals(0, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1Byte() {
|
||||
MemoryByteIterator it =
|
||||
new MemoryByteIterator(program.getMemory(), new AddressSet(addr("0x1001000")));
|
||||
long count =
|
||||
StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false).count();
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2Byte() {
|
||||
MemoryByteIterator it = new MemoryByteIterator(program.getMemory(),
|
||||
new AddressSet(addr("0x1001000"), addr("0x1001001")));
|
||||
long count =
|
||||
StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false).count();
|
||||
assertEquals(2, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterator() throws Exception {
|
||||
AddressSet set = new AddressSet(addr("0x1000000"), addr("0x100100f"));
|
||||
set.addRange(addr("0x1001140"), addr("0x1001144"));
|
||||
MemoryByteIterator it = new MemoryByteIterator(program.getMemory(), set);
|
||||
int total = 0;
|
||||
int n = 0;
|
||||
while(it.hasNext()) {
|
||||
int count = 0;
|
||||
while (it.hasNext()) {
|
||||
byte b = it.next();
|
||||
n++;
|
||||
count++;
|
||||
total += b;
|
||||
}
|
||||
assertEquals(21, n);
|
||||
assertEquals(21, count);
|
||||
assertEquals(-80, total);
|
||||
|
||||
}
|
||||
@Test
|
||||
public void testIterator2() throws Exception {
|
||||
|
||||
@Test
|
||||
public void testIterator2() throws Exception {
|
||||
AddressSet set = new AddressSet(addr("0x1001000"), addr("0x10075ff"));
|
||||
MemoryByteIterator it = new MemoryByteIterator(program.getMemory(), set);
|
||||
int n = 0;
|
||||
while(it.hasNext()) {
|
||||
it.next();
|
||||
n++;
|
||||
long count =
|
||||
StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false).count();
|
||||
assertEquals(0x6600, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBytesNearBufferBoundary() {
|
||||
AddressSet set = new AddressSet(addr("0x1001000"), addr("0x10075ff"));
|
||||
MemoryByteIterator it = new MemoryByteIterator(program.getMemory(), set);
|
||||
int index = 0;
|
||||
int testcount = 0;
|
||||
while (it.hasNext()) {
|
||||
int b = Byte.toUnsignedInt(it.nextByte());
|
||||
switch (index) {
|
||||
//@formatter:off
|
||||
case MemoryByteIterator.MAX_BUF_SIZE - 2: assertEquals(0xee, b); testcount++; break;
|
||||
case MemoryByteIterator.MAX_BUF_SIZE - 1: assertEquals(0xff, b); testcount++; break;
|
||||
case MemoryByteIterator.MAX_BUF_SIZE: assertEquals(0xaa, b); testcount++; break;
|
||||
case MemoryByteIterator.MAX_BUF_SIZE + 1: assertEquals(0xbb, b); testcount++; break;
|
||||
//@formatter:on
|
||||
}
|
||||
index++;
|
||||
}
|
||||
assertEquals(0x6600, n);
|
||||
|
||||
assertEquals(4, testcount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOOBAddrRange() {
|
||||
// Test that invalid / unmapped addresses cause the iterator to signal end-of-iteration
|
||||
AddressSet set = new AddressSet(addr("0x10075ff"), addr("0x1007700"));
|
||||
MemoryByteIterator it = new MemoryByteIterator(program.getMemory(), set);
|
||||
long count =
|
||||
StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false).count();
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOOBAddrRange2() {
|
||||
// Test that invalid / unmapped addresses cause the iterator to signal end-of-iteration
|
||||
AddressSet set = new AddressSet(addr("0x1007600"), addr("0x1007700"));
|
||||
MemoryByteIterator it = new MemoryByteIterator(program.getMemory(), set);
|
||||
long count =
|
||||
StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false).count();
|
||||
assertEquals(0, count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,12 @@ package ghidra.util;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import util.CollectionUtils;
|
||||
|
||||
public final class NumericUtilities {
|
||||
public static final BigInteger MAX_UNSIGNED_LONG = new BigInteger("ffffffffffffffff", 16);
|
||||
public static final BigInteger MAX_SIGNED_LONG = new BigInteger("7fffffffffffffff", 16);
|
||||
@@ -38,23 +35,9 @@ public final class NumericUtilities {
|
||||
private final static String BIN_PREFIX = "0B";
|
||||
private final static String OCT_PREFIX = "0";
|
||||
|
||||
private final static Set<Class<? extends Number>> INTEGER_TYPES = new HashSet<>();
|
||||
static {
|
||||
INTEGER_TYPES.add(Byte.class);
|
||||
INTEGER_TYPES.add(Short.class);
|
||||
INTEGER_TYPES.add(Integer.class);
|
||||
INTEGER_TYPES.add(Long.class);
|
||||
}
|
||||
private static final Set<Class<? extends Number>> FLOATINGPOINT_TYPES = new HashSet<>();
|
||||
static {
|
||||
FLOATINGPOINT_TYPES.add(Float.class);
|
||||
FLOATINGPOINT_TYPES.add(Double.class);
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
private static IntegerRadixRenderer SIGNED_INTEGER_RENDERER = new SignedIntegerRadixRenderer();
|
||||
private static IntegerRadixRenderer UNSIGNED_INTEGER_RENDERER = new UnsignedIntegerRadixRenderer();
|
||||
private static IntegerRadixRenderer DEFAULT_INTEGER_RENDERER = new DefaultIntegerRadixRenderer();
|
||||
private final static Set<Class<? extends Number>> INTEGER_TYPES = Set.of(Byte.class, Short.class, Integer.class, Long.class);
|
||||
private static final Set<Class<? extends Number>> FLOATINGPOINT_TYPES = Set.of(Float.class, Double.class);
|
||||
// @formatter:on
|
||||
|
||||
private NumericUtilities() {
|
||||
@@ -678,7 +661,7 @@ public final class NumericUtilities {
|
||||
/**
|
||||
* Provide renderings of <code>number</code> in different bases:
|
||||
* <ul>
|
||||
* <li><code>0</code> - renders <code>number</code> as an escaped character sequence</li>
|
||||
* <li><code>0</code> - renders <code>number</code> as a big endian escaped character sequence</li>
|
||||
* <li><code>2</code> - renders <code>number</code> as a <code>base-2</code> integer</li>
|
||||
* <li><code>8</code> - renders <code>number</code> as a <code>base-8</code> integer</li>
|
||||
* <li><code>10</code> - renders <code>number</code> as a <code>base-10</code> integer</li>
|
||||
@@ -807,32 +790,16 @@ public final class NumericUtilities {
|
||||
*/
|
||||
public static String formatNumber(long number, int radix, SignednessFormatMode mode) {
|
||||
if (radix == 0) {
|
||||
byte[] bytes = new byte[8];
|
||||
bytes[0] = (byte) (number >> 56);
|
||||
bytes[1] = (byte) (number >> 48);
|
||||
bytes[2] = (byte) (number >> 40);
|
||||
bytes[3] = (byte) (number >> 32);
|
||||
bytes[4] = (byte) (number >> 24);
|
||||
bytes[5] = (byte) (number >> 16);
|
||||
bytes[6] = (byte) (number >> 8);
|
||||
bytes[7] = (byte) (number >> 0);
|
||||
byte[] bytes = BigEndianDataConverter.INSTANCE.getBytes(number);
|
||||
return StringUtilities.toQuotedString(bytes);
|
||||
}
|
||||
|
||||
IntegerRadixRenderer renderer = null;
|
||||
switch (mode) {
|
||||
case SIGNED:
|
||||
renderer = SIGNED_INTEGER_RENDERER;
|
||||
break;
|
||||
case UNSIGNED:
|
||||
renderer = UNSIGNED_INTEGER_RENDERER;
|
||||
break;
|
||||
case DEFAULT:
|
||||
renderer = DEFAULT_INTEGER_RENDERER;
|
||||
break;
|
||||
}
|
||||
return renderer.toString(number, radix);
|
||||
|
||||
BiFunction<Long, Integer, String> renderer = switch (mode) {
|
||||
case SIGNED -> NumericUtilities::signedIntegerRadixToString;
|
||||
case UNSIGNED -> NumericUtilities::unsignedIntegerRadixToString;
|
||||
case DEFAULT -> NumericUtilities::defaultIntegerRadixToString;
|
||||
};
|
||||
return renderer.apply(number, radix);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -888,20 +855,6 @@ public final class NumericUtilities {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given byte into a two character String, padding with a leading 0 if needed.
|
||||
*
|
||||
* @param b the byte
|
||||
* @return the byte string
|
||||
*/
|
||||
public static String toString(byte b) {
|
||||
String bs = Integer.toHexString(b & 0xff);
|
||||
if (bs.length() == 1) {
|
||||
return "0" + bs;
|
||||
}
|
||||
return bs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a byte array into a hexadecimal string.
|
||||
*
|
||||
@@ -909,21 +862,18 @@ public final class NumericUtilities {
|
||||
* @return hex string representation
|
||||
*/
|
||||
public static String convertBytesToString(byte[] bytes) {
|
||||
return convertBytesToString(bytes, null);
|
||||
return convertBytesToString(bytes, 0, bytes.length, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a byte array into a hexadecimal string.
|
||||
*
|
||||
* @param bytes byte array
|
||||
* @param delimeter the text between byte strings
|
||||
* @param delimiter the text between byte strings, {@code null} ok
|
||||
* @return hex string representation
|
||||
*/
|
||||
public static String convertBytesToString(byte[] bytes, String delimeter) {
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
return convertBytesToString(bytes, 0, bytes.length, delimeter);
|
||||
public static String convertBytesToString(byte[] bytes, String delimiter) {
|
||||
return convertBytesToString(bytes, 0, bytes.length, delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -932,49 +882,77 @@ public final class NumericUtilities {
|
||||
* @param bytes byte array
|
||||
* @param start start index
|
||||
* @param len number of bytes to convert
|
||||
* @param delimeter the text between byte strings
|
||||
* @param delimiter the text between byte strings, {@code null} ok
|
||||
* @return hex string representation
|
||||
*/
|
||||
public static String convertBytesToString(byte[] bytes, int start, int len, String delimeter) {
|
||||
public static String convertBytesToString(byte[] bytes, int start, int len, String delimiter) {
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
delimiter = Objects.requireNonNullElse(delimiter, "");
|
||||
|
||||
Iterator<Byte> iterator = IteratorUtils.arrayIterator(bytes, start, start + len);
|
||||
return convertBytesToString(iterator, delimeter);
|
||||
int end = start + len;
|
||||
Objects.checkFromToIndex(start, end, bytes.length);
|
||||
|
||||
StringBuilder sb = new StringBuilder(len * (2 + delimiter.length()));
|
||||
for (int i = start; i < end; i++) {
|
||||
if (!sb.isEmpty()) {
|
||||
sb.append(delimiter);
|
||||
}
|
||||
String s = Integer.toHexString(Byte.toUnsignedInt(bytes[i]));
|
||||
if (s.length() < 2) {
|
||||
sb.append('0');
|
||||
}
|
||||
sb.append(s);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a bytes into a hexadecimal string.
|
||||
* Convert bytes into a hexadecimal string.
|
||||
*
|
||||
* @param bytes an iterator of bytes
|
||||
* @param delimiter the text between byte strings; null is allowed
|
||||
* @param delimiter the text between byte strings, {@code null} ok
|
||||
* @return hex string representation
|
||||
*/
|
||||
public static String convertBytesToString(Iterator<Byte> bytes, String delimiter) {
|
||||
delimiter = Objects.requireNonNullElse(delimiter, "");
|
||||
|
||||
return convertBytesToString(() -> bytes, delimiter);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (bytes.hasNext()) {
|
||||
Byte b = bytes.next();
|
||||
if (!sb.isEmpty()) {
|
||||
sb.append(delimiter);
|
||||
}
|
||||
String s = Integer.toHexString(Byte.toUnsignedInt(b));
|
||||
if (s.length() < 2) {
|
||||
sb.append('0');
|
||||
}
|
||||
sb.append(s);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a bytes into a hexadecimal string.
|
||||
* Convert bytes into a hexadecimal string.
|
||||
*
|
||||
* @param bytes an iterable of bytes
|
||||
* @param delimiter the text between byte strings; null is allowed
|
||||
* @param delimiter the text between byte strings, {@code null} ok
|
||||
* @return hex string representation
|
||||
*/
|
||||
public static String convertBytesToString(Iterable<Byte> bytes, String delimiter) {
|
||||
return convertBytesToString(CollectionUtils.asStream(bytes), delimiter);
|
||||
return convertBytesToString(bytes.iterator(), delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a bytes into a hexadecimal string.
|
||||
* Convert bytes into a hexadecimal string.
|
||||
*
|
||||
* @param bytes an stream of bytes
|
||||
* @param delimiter the text between byte strings; null is allowed
|
||||
* @param delimiter the text between byte strings, {@code null} ok
|
||||
* @return hex string representation
|
||||
*/
|
||||
public static String convertBytesToString(Stream<Byte> bytes, String delimiter) {
|
||||
|
||||
delimiter = (delimiter == null) ? "" : delimiter;
|
||||
return bytes.map(NumericUtilities::toString).collect(Collectors.joining(delimiter));
|
||||
return convertBytesToString(bytes.iterator(), delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1020,94 +998,53 @@ public final class NumericUtilities {
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the protocol for rendering integer-type numbers in different signed-ness modes.
|
||||
* Renders provided numbers as signed values.
|
||||
*
|
||||
* @param number value
|
||||
* @param radix 2,8,10,16
|
||||
* @return formatted string
|
||||
*/
|
||||
private static interface IntegerRadixRenderer {
|
||||
/**
|
||||
* Format the given number in the provided radix base.
|
||||
*
|
||||
* @param number the number to render
|
||||
* @param radix the base in which to render
|
||||
* @return a string representing the provided number in the given base
|
||||
*/
|
||||
public String toString(long number, int radix);
|
||||
|
||||
private static String signedIntegerRadixToString(long number, int radix) {
|
||||
return switch (radix) {
|
||||
case 2 -> Long.toString(number, radix) + "b";
|
||||
case 8 -> Long.toString(number, radix) + "o";
|
||||
case 10 -> Long.toString(number, radix);
|
||||
case 16 -> Long.toString(number, radix) + "h";
|
||||
default -> throw new IllegalArgumentException("Unsupported radix " + radix);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders provided numbers as signed values
|
||||
* Renders provided numbers as unsigned values.
|
||||
*
|
||||
* @param number value
|
||||
* @param radix 2,8,10,16
|
||||
* @return formatted string
|
||||
*/
|
||||
private final static class SignedIntegerRadixRenderer implements IntegerRadixRenderer {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* All values are rendered in their <i>signed</i> form
|
||||
**/
|
||||
@Override
|
||||
public String toString(long number, int radix) {
|
||||
switch (radix) {
|
||||
case 2:
|
||||
return Long.toString(number, radix) + "b";
|
||||
case 8:
|
||||
return Long.toString(number, radix) + "o";
|
||||
case 10:
|
||||
return Long.toString(number, radix);
|
||||
case 16:
|
||||
return Long.toString(number, radix) + "h";
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported radix");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders provided numbers as unsigned values
|
||||
*/
|
||||
private final static class UnsignedIntegerRadixRenderer implements IntegerRadixRenderer {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* All values are rendered in their <i>unsigned</i> form
|
||||
**/
|
||||
@Override
|
||||
public String toString(long number, int radix) {
|
||||
switch (radix) {
|
||||
case 2:
|
||||
return Long.toBinaryString(number) + "b";
|
||||
case 8:
|
||||
return Long.toOctalString(number) + "o";
|
||||
case 10:
|
||||
return Long.toUnsignedString(number);
|
||||
case 16:
|
||||
return Long.toHexString(number) + "h";
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported radix");
|
||||
}
|
||||
private static String unsignedIntegerRadixToString(long number, int radix) {
|
||||
return switch (radix) {
|
||||
case 2 -> Long.toBinaryString(number) + "b";
|
||||
case 8 -> Long.toOctalString(number) + "o";
|
||||
case 10 -> Long.toUnsignedString(number);
|
||||
case 16 -> Long.toHexString(number) + "h";
|
||||
default -> throw new IllegalArgumentException("Unsupported radix " + radix);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders provided numbers in a more human-friendly manner
|
||||
*
|
||||
* @param number value
|
||||
* @param radix 2,8,10,16
|
||||
* @return formatted string
|
||||
*/
|
||||
private final static class DefaultIntegerRadixRenderer implements IntegerRadixRenderer {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* Values to be rendered in binary, octal, or hexadecimal bases are rendered as unsigned,
|
||||
* numbers rendered in decimal are rendered as signed.
|
||||
*/
|
||||
@Override
|
||||
public String toString(long number, int radix) {
|
||||
switch (radix) {
|
||||
case 2:
|
||||
return new UnsignedIntegerRadixRenderer().toString(number, radix);
|
||||
case 8:
|
||||
return new UnsignedIntegerRadixRenderer().toString(number, radix);
|
||||
case 10:
|
||||
return new SignedIntegerRadixRenderer().toString(number, radix);
|
||||
case 16:
|
||||
return new UnsignedIntegerRadixRenderer().toString(number, radix);
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported radix");
|
||||
}
|
||||
private static String defaultIntegerRadixToString(long number, int radix) {
|
||||
return switch (radix) {
|
||||
case 2 -> unsignedIntegerRadixToString(number, radix);
|
||||
case 8 -> unsignedIntegerRadixToString(number, radix);
|
||||
case 10 -> signedIntegerRadixToString(number, radix);
|
||||
case 16 -> unsignedIntegerRadixToString(number, radix);
|
||||
default -> throw new IllegalArgumentException("Unsupported radix " + radix);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+50
-32
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package ghidra.program.model.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
@@ -22,13 +25,14 @@ import ghidra.program.model.mem.MemoryAccessException;
|
||||
/**
|
||||
* Class to iterate over the bytes in memory for an address set.
|
||||
*/
|
||||
public class MemoryByteIterator {
|
||||
private static final int BUF_SIZE = 16 * 1024;
|
||||
public class MemoryByteIterator implements Iterator<Byte> {
|
||||
public static final int MAX_BUF_SIZE = 16 * 1024;
|
||||
private Memory mem;
|
||||
private AddressSet addrSet;
|
||||
byte[] buf;
|
||||
int count = 0;
|
||||
int pos;
|
||||
|
||||
private byte[] buf;
|
||||
private int bufSize;
|
||||
private int pos;
|
||||
|
||||
/**
|
||||
* Construct a memoryIterator
|
||||
@@ -37,39 +41,53 @@ public class MemoryByteIterator {
|
||||
*/
|
||||
public MemoryByteIterator(Memory mem, AddressSetView set) {
|
||||
this.mem = mem;
|
||||
addrSet = set.intersect(mem);
|
||||
buf = new byte[BUF_SIZE];
|
||||
|
||||
this.addrSet = set.intersect(mem);
|
||||
this.buf = new byte[(int) Math.min(MAX_BUF_SIZE, set.getNumAddresses())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are more bytes to iterate over
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return count != 0 || !addrSet.isEmpty();
|
||||
ensureBuffer();
|
||||
return pos < bufSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte next() {
|
||||
return nextByte();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next byte.
|
||||
* @throws MemoryAccessException if the next byte could not be read
|
||||
* {@return the next primitive byte. Use this method if you want to avoid the cost of
|
||||
* boxing Bytes the normal next() method returns}
|
||||
*
|
||||
* @throws NoSuchElementException if the iteration has no more elements
|
||||
*/
|
||||
public byte next() throws MemoryAccessException {
|
||||
if (count == 0) {
|
||||
AddressRange range = addrSet.iterator().next();
|
||||
Address start = range.getMinAddress();
|
||||
long size = range.getLength();
|
||||
if (size > BUF_SIZE) {
|
||||
range = new AddressRangeImpl(start, start.add(BUF_SIZE - 1));
|
||||
size = BUF_SIZE;
|
||||
}
|
||||
count = (int) size;
|
||||
pos = 0;
|
||||
addrSet.delete(range);
|
||||
|
||||
mem.getBytes(start, buf, 0, count);
|
||||
public byte nextByte() {
|
||||
ensureBuffer();
|
||||
if (pos < bufSize) {
|
||||
byte result = buf[pos];
|
||||
pos++;
|
||||
return result;
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
private void ensureBuffer() {
|
||||
if (pos >= bufSize && !addrSet.isEmpty()) {
|
||||
AddressRange firstRange = addrSet.getFirstRange();
|
||||
Address addr = firstRange.getMinAddress();
|
||||
|
||||
int readSize = (int) Math.min(firstRange.getLength(), buf.length);
|
||||
addrSet.deleteFromMin(addr.add(readSize - 1)); // remove from addrSet before possible exception in getBytes()
|
||||
|
||||
pos = 0;
|
||||
try {
|
||||
bufSize = mem.getBytes(addr, buf, 0, readSize);
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
bufSize = 0;
|
||||
}
|
||||
}
|
||||
count--;
|
||||
return buf[pos++];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user