mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-5864: Add EmulatorUtilities
This commit is contained in:
parent
39c6a6db59
commit
d577925d75
43 changed files with 990 additions and 273 deletions
|
@ -28,6 +28,7 @@ import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
|
||||||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils.Extrema;
|
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils.Extrema;
|
||||||
import ghidra.app.services.DebuggerEmulationService;
|
import ghidra.app.services.DebuggerEmulationService;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.pcode.emu.EmulatorUtilities;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -142,7 +143,7 @@ public class ProgramEmulationUtils {
|
||||||
EMU_SESSION_SCHEMA = EMU_CTX.getSchema(new SchemaName("EmuSession"));
|
EMU_SESSION_SCHEMA = EMU_CTX.getSchema(new SchemaName("EmuSession"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String BLOCK_NAME_STACK = "STACK";
|
public static final String BLOCK_NAME_STACK = EmulatorUtilities.BLOCK_NAME_STACK;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conventional prefix for first snapshot to identify "pure emulation" traces.
|
* Conventional prefix for first snapshot to identify "pure emulation" traces.
|
||||||
|
@ -428,7 +429,7 @@ public class ProgramEmulationUtils {
|
||||||
final AddressRange alloc;
|
final AddressRange alloc;
|
||||||
if (cSpec.stackGrowsNegative()) {
|
if (cSpec.stackGrowsNegative()) {
|
||||||
Address max = spAddr.subtractWrap(1);
|
Address max = spAddr.subtractWrap(1);
|
||||||
Address min = spAddr.subtractWrapSpace(size);
|
Address min = spAddr.subtractWrap(size);
|
||||||
if (min.compareTo(max) > 0) {
|
if (min.compareTo(max) > 0) {
|
||||||
alloc = new AddressRangeImpl(max.getAddressSpace().getMinAddress(), max);
|
alloc = new AddressRangeImpl(max.getAddressSpace().getMinAddress(), max);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import generic.util.MergeSortingIterator;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.trace.model.*;
|
import ghidra.trace.model.*;
|
||||||
import ghidra.trace.model.Lifespan.DefaultLifeSet;
|
import ghidra.trace.model.Lifespan.DefaultLifeSet;
|
||||||
|
@ -370,7 +371,7 @@ public class DBTraceTimeViewport implements TraceTimeViewport {
|
||||||
List<Iterator<T>> iters = getOrderedSpans().stream()
|
List<Iterator<T>> iters = getOrderedSpans().stream()
|
||||||
.map(rng -> iterFunc.apply(rng.lmax()))
|
.map(rng -> iterFunc.apply(rng.lmax()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
return new UniqIterator<>(new MergeSortingIterator<>(iters, comparator));
|
return new DistinctIterator<>(new MergeSortingIterator<>(iters, comparator));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,7 +19,7 @@ import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
|
|
||||||
import generic.NestedIterator;
|
import generic.util.FlattenedIterator;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.trace.database.DBTraceUtils;
|
import ghidra.trace.database.DBTraceUtils;
|
||||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||||
|
@ -361,7 +361,7 @@ public abstract class AbstractBaseDBTraceCodeUnitsMemoryView<T extends DBTraceCo
|
||||||
* @return an iterable of units
|
* @return an iterable of units
|
||||||
*/
|
*/
|
||||||
public Iterable<? extends T> get(long snap, AddressSetView set, boolean forward) {
|
public Iterable<? extends T> get(long snap, AddressSetView set, boolean forward) {
|
||||||
return () -> NestedIterator.start(set.iterator(forward),
|
return () -> FlattenedIterator.start(set.iterator(forward),
|
||||||
r -> get(snap, r, forward).iterator());
|
r -> get(snap, r, forward).iterator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.trace.database.listing;
|
package ghidra.trace.database.listing;
|
||||||
|
|
||||||
import generic.NestedIterator;
|
import generic.util.FlattenedIterator;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.trace.model.*;
|
import ghidra.trace.model.*;
|
||||||
import ghidra.trace.model.listing.TraceBaseCodeUnitsView;
|
import ghidra.trace.model.listing.TraceBaseCodeUnitsView;
|
||||||
|
@ -139,7 +139,7 @@ public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnit
|
||||||
* @see TraceBaseCodeUnitsView#get(long, AddressSetView, boolean)
|
* @see TraceBaseCodeUnitsView#get(long, AddressSetView, boolean)
|
||||||
*/
|
*/
|
||||||
public Iterable<? extends T> get(long snap, AddressSetView set, boolean forward) {
|
public Iterable<? extends T> get(long snap, AddressSetView set, boolean forward) {
|
||||||
return () -> NestedIterator.start(set.iterator(forward),
|
return () -> FlattenedIterator.start(set.iterator(forward),
|
||||||
r -> this.get(snap, r, forward).iterator());
|
r -> this.get(snap, r, forward).iterator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import generic.util.MergeSortingIterator;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.trace.model.TraceAddressSnapRange;
|
import ghidra.trace.model.TraceAddressSnapRange;
|
||||||
import ghidra.trace.model.listing.TraceCodeUnit;
|
import ghidra.trace.model.listing.TraceCodeUnit;
|
||||||
|
|
|
@ -20,7 +20,8 @@ import java.util.*;
|
||||||
|
|
||||||
import org.apache.commons.collections4.IteratorUtils;
|
import org.apache.commons.collections4.IteratorUtils;
|
||||||
|
|
||||||
import generic.NestedIterator;
|
import generic.util.MergeSortingIterator;
|
||||||
|
import generic.util.FlattenedIterator;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.database.code.InstructionDB;
|
import ghidra.program.database.code.InstructionDB;
|
||||||
import ghidra.program.database.function.OverlappingFunctionException;
|
import ghidra.program.database.function.OverlappingFunctionException;
|
||||||
|
@ -348,7 +349,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
||||||
}
|
}
|
||||||
// TODO: The property map doesn't heed forking.
|
// TODO: The property map doesn't heed forking.
|
||||||
return new WrappingCodeUnitIterator(
|
return new WrappingCodeUnitIterator(
|
||||||
NestedIterator
|
FlattenedIterator
|
||||||
.start(map.getAddressSetView(Lifespan.at(program.snap)).iterator(forward),
|
.start(map.getAddressSetView(Lifespan.at(program.snap)).iterator(forward),
|
||||||
rng -> getTopCodeIterator(
|
rng -> getTopCodeIterator(
|
||||||
s -> codeOperations.codeUnits().get(s, rng, forward).iterator(),
|
s -> codeOperations.codeUnits().get(s, rng, forward).iterator(),
|
||||||
|
@ -371,7 +372,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
||||||
}
|
}
|
||||||
// TODO: The property map doesn't heed forking.
|
// TODO: The property map doesn't heed forking.
|
||||||
return new WrappingCodeUnitIterator(
|
return new WrappingCodeUnitIterator(
|
||||||
NestedIterator
|
FlattenedIterator
|
||||||
.start(map.getAddressSetView(Lifespan.at(program.snap)).iterator(addr, forward),
|
.start(map.getAddressSetView(Lifespan.at(program.snap)).iterator(addr, forward),
|
||||||
rng -> getTopCodeIterator(
|
rng -> getTopCodeIterator(
|
||||||
s -> codeOperations.codeUnits().get(s, rng, forward).iterator(),
|
s -> codeOperations.codeUnits().get(s, rng, forward).iterator(),
|
||||||
|
@ -394,7 +395,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
||||||
return new WrappingCodeUnitIterator(Collections.emptyIterator());
|
return new WrappingCodeUnitIterator(Collections.emptyIterator());
|
||||||
}
|
}
|
||||||
// TODO: The property map doesn't heed forking.
|
// TODO: The property map doesn't heed forking.
|
||||||
return new WrappingCodeUnitIterator(NestedIterator.start(
|
return new WrappingCodeUnitIterator(FlattenedIterator.start(
|
||||||
new IntersectionAddressSetView(map.getAddressSetView(Lifespan.at(program.snap)),
|
new IntersectionAddressSetView(map.getAddressSetView(Lifespan.at(program.snap)),
|
||||||
addrSet).iterator(forward),
|
addrSet).iterator(forward),
|
||||||
rng -> getTopCodeIterator(
|
rng -> getTopCodeIterator(
|
||||||
|
|
|
@ -23,7 +23,7 @@ import java.util.function.Predicate;
|
||||||
|
|
||||||
import javax.help.UnsupportedOperationException;
|
import javax.help.UnsupportedOperationException;
|
||||||
|
|
||||||
import generic.NestedIterator;
|
import generic.util.FlattenedIterator;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.listing.Variable;
|
import ghidra.program.model.listing.Variable;
|
||||||
|
@ -242,7 +242,7 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
|
||||||
protected Iterator<Reference> getReferenceIteratorForSnap(long snap, Address startAddr) {
|
protected Iterator<Reference> getReferenceIteratorForSnap(long snap, Address startAddr) {
|
||||||
AddressIterator addresses =
|
AddressIterator addresses =
|
||||||
refs.getReferenceSources(Lifespan.at(snap)).getAddresses(startAddr, true);
|
refs.getReferenceSources(Lifespan.at(snap)).getAddresses(startAddr, true);
|
||||||
return NestedIterator.start(addresses, a -> {
|
return FlattenedIterator.start(addresses, a -> {
|
||||||
return refs.getReferencesFrom(snap, a).iterator();
|
return refs.getReferencesFrom(snap, a).iterator();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import javax.swing.Icon;
|
||||||
|
|
||||||
import org.apache.commons.collections4.IteratorUtils;
|
import org.apache.commons.collections4.IteratorUtils;
|
||||||
|
|
||||||
import generic.NestedIterator;
|
import generic.util.FlattenedIterator;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Bookmark;
|
import ghidra.program.model.listing.Bookmark;
|
||||||
import ghidra.program.model.listing.BookmarkType;
|
import ghidra.program.model.listing.BookmarkType;
|
||||||
|
@ -310,7 +310,7 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Bookmark> getBookmarksIterator() {
|
public Iterator<Bookmark> getBookmarksIterator() {
|
||||||
// TODO: This seems terribly inefficient. We'll have to see how/when it's used.
|
// TODO: This seems terribly inefficient. We'll have to see how/when it's used.
|
||||||
return NestedIterator.start(bookmarkManager.getActiveSpaces().iterator(),
|
return FlattenedIterator.start(bookmarkManager.getActiveSpaces().iterator(),
|
||||||
space -> filteredIterator(space.getAllBookmarks().iterator(),
|
space -> filteredIterator(space.getAllBookmarks().iterator(),
|
||||||
bm -> program.viewport.containsAnyUpper(bm.getLifespan())));
|
bm -> program.viewport.containsAnyUpper(bm.getLifespan())));
|
||||||
}
|
}
|
||||||
|
@ -327,7 +327,7 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
|
||||||
AddressSet allMemory = factory.getAddressSet();
|
AddressSet allMemory = factory.getAddressSet();
|
||||||
AddressSet within = forward ? factory.getAddressSet(startAddress, allMemory.getMaxAddress())
|
AddressSet within = forward ? factory.getAddressSet(startAddress, allMemory.getMaxAddress())
|
||||||
: factory.getAddressSet(allMemory.getMinAddress(), startAddress);
|
: factory.getAddressSet(allMemory.getMinAddress(), startAddress);
|
||||||
return NestedIterator.start(within.iterator(forward), rng -> {
|
return FlattenedIterator.start(within.iterator(forward), rng -> {
|
||||||
DBTraceBookmarkSpace space =
|
DBTraceBookmarkSpace space =
|
||||||
bookmarkManager.getBookmarkSpace(rng.getAddressSpace(), false);
|
bookmarkManager.getBookmarkSpace(rng.getAddressSpace(), false);
|
||||||
if (space == null) {
|
if (space == null) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import generic.NestedIterator;
|
import generic.util.FlattenedIterator;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.ContextChangeException;
|
import ghidra.program.model.listing.ContextChangeException;
|
||||||
|
@ -110,7 +110,7 @@ public class DBTraceProgramViewProgramContext extends AbstractProgramContext {
|
||||||
setRegisterValue(start, end, new RegisterValue(register, value));
|
setRegisterValue(start, end, new RegisterValue(register, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NestedAddressRangeIterator<U> extends NestedIterator<U, AddressRange>
|
private static class NestedAddressRangeIterator<U> extends FlattenedIterator<U, AddressRange>
|
||||||
implements AddressRangeIterator {
|
implements AddressRangeIterator {
|
||||||
protected NestedAddressRangeIterator(Iterator<U> it,
|
protected NestedAddressRangeIterator(Iterator<U> it,
|
||||||
Function<U, Iterator<? extends AddressRange>> f) {
|
Function<U, Iterator<? extends AddressRange>> f) {
|
||||||
|
|
|
@ -18,8 +18,7 @@ package ghidra.trace.database.program;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import generic.NestedIterator;
|
import generic.util.*;
|
||||||
import generic.util.PeekableIterator;
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
|
@ -342,7 +341,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SymbolIterator getSymbols(AddressSetView set, SymbolType type, boolean forward) {
|
public SymbolIterator getSymbols(AddressSetView set, SymbolType type, boolean forward) {
|
||||||
return new SymbolIteratorAdapter(NestedIterator.start(set.iterator(), range -> {
|
return new SymbolIteratorAdapter(FlattenedIterator.start(set.iterator(), range -> {
|
||||||
if (range.getAddressSpace().isMemorySpace()) {
|
if (range.getAddressSpace().isMemorySpace()) {
|
||||||
if (type == SymbolType.LABEL) {
|
if (type == SymbolType.LABEL) {
|
||||||
return symbolManager.labels()
|
return symbolManager.labels()
|
||||||
|
@ -426,7 +425,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SymbolIterator getPrimarySymbolIterator(AddressSetView asv, boolean forward) {
|
public SymbolIterator getPrimarySymbolIterator(AddressSetView asv, boolean forward) {
|
||||||
return new PrimarySymbolIterator(NestedIterator.start(asv.iterator(forward),
|
return new PrimarySymbolIterator(FlattenedIterator.start(asv.iterator(forward),
|
||||||
range -> symbolManager.labels()
|
range -> symbolManager.labels()
|
||||||
.getIntersecting(Lifespan.at(program.snap), range, true, forward)
|
.getIntersecting(Lifespan.at(program.snap), range, true, forward)
|
||||||
.iterator()));
|
.iterator()));
|
||||||
|
|
|
@ -19,7 +19,7 @@ import java.util.*;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.function.*;
|
import java.util.function.*;
|
||||||
|
|
||||||
import generic.NestedIterator;
|
import generic.util.FlattenedIterator;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.util.LockHold;
|
import ghidra.util.LockHold;
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ public interface DBTraceDelegatingManager<M> {
|
||||||
return new AbstractCollection<>() {
|
return new AbstractCollection<>() {
|
||||||
@Override
|
@Override
|
||||||
public Iterator<T> iterator() {
|
public Iterator<T> iterator() {
|
||||||
return NestedIterator.start(spaces.iterator(), func.andThen(Iterable::iterator));
|
return FlattenedIterator.start(spaces.iterator(), func.andThen(Iterable::iterator));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,7 +19,7 @@ import java.io.IOException;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
|
|
||||||
import db.DBHandle;
|
import db.DBHandle;
|
||||||
import generic.NestedIterator;
|
import generic.util.FlattenedIterator;
|
||||||
import ghidra.framework.data.OpenMode;
|
import ghidra.framework.data.OpenMode;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.trace.database.DBTrace;
|
import ghidra.trace.database.DBTrace;
|
||||||
|
@ -119,7 +119,7 @@ public class DBTraceStackManager implements TraceStackManager, DBTraceManager {
|
||||||
@Override
|
@Override
|
||||||
// TODO: Should probably include a lifespan parameter?
|
// TODO: Should probably include a lifespan parameter?
|
||||||
public Iterable<TraceStackFrame> getFramesIn(AddressSetView set) {
|
public Iterable<TraceStackFrame> getFramesIn(AddressSetView set) {
|
||||||
return () -> NestedIterator.start(set.iterator(),
|
return () -> FlattenedIterator.start(set.iterator(),
|
||||||
rng -> trace.getObjectManager()
|
rng -> trace.getObjectManager()
|
||||||
.getObjectsIntersecting(Lifespan.ALL, rng, TraceStackFrame.KEY_PC,
|
.getObjectsIntersecting(Lifespan.ALL, rng, TraceStackFrame.KEY_PC,
|
||||||
TraceStackFrame.class)
|
TraceStackFrame.class)
|
||||||
|
|
|
@ -18,9 +18,9 @@ package ghidra.trace.database.symbol;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import generic.util.MergeSortingIterator;
|
||||||
import ghidra.trace.model.symbol.*;
|
import ghidra.trace.model.symbol.*;
|
||||||
import ghidra.util.LazyCollection;
|
import ghidra.util.LazyCollection;
|
||||||
import ghidra.util.MergeSortingIterator;
|
|
||||||
|
|
||||||
public class DBTraceSymbolMultipleTypesView<T extends AbstractDBTraceSymbol>
|
public class DBTraceSymbolMultipleTypesView<T extends AbstractDBTraceSymbol>
|
||||||
implements TraceSymbolView<T> {
|
implements TraceSymbolView<T> {
|
||||||
|
|
|
@ -20,11 +20,11 @@ import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import generic.util.AbstractPeekableIterator;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressRange;
|
import ghidra.program.model.address.AddressRange;
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
import ghidra.trace.model.TraceAddressSnapRange;
|
import ghidra.trace.model.TraceAddressSnapRange;
|
||||||
import ghidra.util.AbstractPeekableIterator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An iterator of overlapping objects return from two given iterators.
|
* An iterator of overlapping objects return from two given iterators.
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.trace.util;
|
package ghidra.trace.util;
|
||||||
|
|
||||||
|
import generic.util.AbstractPeekableIterator;
|
||||||
import ghidra.trace.model.Lifespan;
|
import ghidra.trace.model.Lifespan;
|
||||||
import ghidra.trace.model.Lifespan.DefaultLifeSet;
|
import ghidra.trace.model.Lifespan.DefaultLifeSet;
|
||||||
import ghidra.trace.model.Lifespan.MutableLifeSet;
|
import ghidra.trace.model.Lifespan.MutableLifeSet;
|
||||||
|
@ -22,7 +23,6 @@ import ghidra.trace.model.Trace;
|
||||||
import ghidra.trace.model.time.TraceSnapshot;
|
import ghidra.trace.model.time.TraceSnapshot;
|
||||||
import ghidra.trace.model.time.TraceTimeManager;
|
import ghidra.trace.model.time.TraceTimeManager;
|
||||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||||
import ghidra.util.AbstractPeekableIterator;
|
|
||||||
|
|
||||||
public class TraceViewportSpanIterator extends AbstractPeekableIterator<Lifespan> {
|
public class TraceViewportSpanIterator extends AbstractPeekableIterator<Lifespan> {
|
||||||
private final TraceTimeManager timeManager;
|
private final TraceTimeManager timeManager;
|
||||||
|
|
|
@ -20,7 +20,7 @@ import static ghidra.util.MathUtilities.cmin;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import ghidra.util.AbstractPeekableIterator;
|
import generic.util.AbstractPeekableIterator;
|
||||||
import ghidra.util.AddressIteratorAdapter;
|
import ghidra.util.AddressIteratorAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,23 +18,24 @@ package ghidra.util;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import generic.util.AbstractPeekableIterator;
|
||||||
import generic.util.PeekableIterator;
|
import generic.util.PeekableIterator;
|
||||||
|
import generic.util.PeekableIterators;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A filtering iterator which removes repeated objects
|
* A filtering iterator which removes repeated objects
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* This operates in style to the uniq command on UNIX, which only removes immediate repeats. To
|
* This iterator only removes immediate repeats (similar to the uniq command on UNIX). To obtain a
|
||||||
* obtain a truly unique iteration, the wrapped iterator must visit elements in sorted order.
|
* truly distinct iteration, the wrapped iterator must visit elements in sorted order.
|
||||||
*
|
*
|
||||||
* @param <T> the type of elements
|
* @param <T> the type of elements
|
||||||
*/
|
*/
|
||||||
public class UniqIterator<T> extends AbstractPeekableIterator<T> {
|
public class DistinctIterator<T> extends AbstractPeekableIterator<T> {
|
||||||
protected boolean first;
|
protected boolean first;
|
||||||
protected T last;
|
protected T last;
|
||||||
protected final PeekableIterator<T> wrapped;
|
protected final PeekableIterator<T> wrapped;
|
||||||
|
|
||||||
public UniqIterator(Iterator<T> wrapped) {
|
public DistinctIterator(Iterator<T> wrapped) {
|
||||||
this.wrapped = PeekableIterators.castOrWrap(wrapped);
|
this.wrapped = PeekableIterators.castOrWrap(wrapped);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import java.util.function.Consumer;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
import generic.NestedIterator;
|
import generic.util.FlattenedIterator;
|
||||||
import generic.util.PeekableIterator;
|
import generic.util.PeekableIterator;
|
||||||
import ghidra.util.LockHold;
|
import ghidra.util.LockHold;
|
||||||
import ghidra.util.database.*;
|
import ghidra.util.database.*;
|
||||||
|
@ -289,7 +289,7 @@ public abstract class AbstractConstraintsTree< //
|
||||||
}
|
}
|
||||||
nodes.add(n);
|
nodes.add(n);
|
||||||
}
|
}
|
||||||
return NestedIterator.start(nodes.iterator(), n -> iterator(n, query));
|
return FlattenedIterator.start(nodes.iterator(), n -> iterator(n, query));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Iterator<DR> orderedIterator(Q query) {
|
protected Iterator<DR> orderedIterator(Q query) {
|
||||||
|
|
|
@ -0,0 +1,280 @@
|
||||||
|
/* ###
|
||||||
|
* 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.pcode.emu;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import ghidra.pcode.exec.PcodeArithmetic;
|
||||||
|
import ghidra.pcode.exec.PcodeExecutorState;
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
|
import ghidra.util.DifferenceAddressSetView;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for working with plain emulators (not trace- or debugger-bound) and programs in
|
||||||
|
* scripts.
|
||||||
|
*/
|
||||||
|
public enum EmulatorUtilities {
|
||||||
|
;
|
||||||
|
/**
|
||||||
|
* The conventional name of the memory block used to specify the location of the stack. This
|
||||||
|
* should only be the case for single-threaded emulation.
|
||||||
|
*/
|
||||||
|
public static final String BLOCK_NAME_STACK = "STACK";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default block size used to copy program bytes into the emulator
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_BLOCK_SIZE = 4096;
|
||||||
|
/**
|
||||||
|
* The default max size to assume for the stack
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_STACK_SIZE = 0x4000;
|
||||||
|
/**
|
||||||
|
* These utilities will avoid choosing a stack range lower than this bound, as most platforms
|
||||||
|
* will never map this page (even in kernel space) so that 0-valued pointers are never valid.
|
||||||
|
*/
|
||||||
|
public static final long PAGE_ZERO_END = 0x1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the bytes from the given program into the given emulator's memory.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This copies each initialized block of memory from the given program into the emulator's
|
||||||
|
* shared machine state. Because the machine can have memory of any given type, it will use the
|
||||||
|
* machine's arithmetic to create values from the program's concrete data. Data is copied in
|
||||||
|
* blocks of the given size, which can be tweaked for performance. The default value, used by
|
||||||
|
* {@link #loadProgram(PcodeMachine, Program)} is {@value #DEFAULT_BLOCK_SIZE}.
|
||||||
|
*
|
||||||
|
* @param <T> the type of values used by the emulator
|
||||||
|
* @param machine the emulator whose memory to initialize
|
||||||
|
* @param program the program whose bytes should be copied into the emulator
|
||||||
|
* @param blockSize the size of the temporary buffer used for copying
|
||||||
|
* @throws MemoryAccessException if the program's memory cannot be read
|
||||||
|
*/
|
||||||
|
public static <T> void loadProgram(PcodeMachine<T> machine, Program program, int blockSize)
|
||||||
|
throws MemoryAccessException {
|
||||||
|
byte[] buf = new byte[blockSize];
|
||||||
|
PcodeExecutorState<T> state = machine.getSharedState();
|
||||||
|
for (MemoryBlock block : program.getMemory().getBlocks()) {
|
||||||
|
if (!block.isInitialized()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (AddressRange rng : new AddressRangeChunker(block.getAddressRange(),
|
||||||
|
buf.length)) {
|
||||||
|
int len = block.getBytes(rng.getMinAddress(), buf);
|
||||||
|
state.setConcrete(rng.getMinAddress(),
|
||||||
|
len == buf.length ? buf : Arrays.copyOf(buf, len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the bytes from the given program into the given emulator's memory.
|
||||||
|
*
|
||||||
|
* @see #loadProgram(PcodeMachine, Program, int)
|
||||||
|
* @param machine the emulator whose memory to initialize
|
||||||
|
* @param program the program whose bytes should be copied into the emulator
|
||||||
|
* @throws MemoryAccessException if the program's memory cannot be read
|
||||||
|
*/
|
||||||
|
public static void loadProgram(PcodeMachine<?> machine, Program program)
|
||||||
|
throws MemoryAccessException {
|
||||||
|
loadProgram(machine, program, DEFAULT_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose an assumed stack range by examining the entry point for a contextual value of the
|
||||||
|
* stack pointer.
|
||||||
|
*
|
||||||
|
* @param program the program whose context to examine
|
||||||
|
* @param entry the entry point where context should be examined
|
||||||
|
* @param stackSize the assumed max size of the stack
|
||||||
|
* @return the range assumed to be reserved for the stack, or null if no stack pointer value is
|
||||||
|
* in the context
|
||||||
|
*/
|
||||||
|
public static AddressRange chooseStackRangeFromContext(Program program, Address entry,
|
||||||
|
int stackSize) {
|
||||||
|
ProgramContext ctx = program.getProgramContext();
|
||||||
|
CompilerSpec cSpec = program.getCompilerSpec();
|
||||||
|
Register sp = cSpec.getStackPointer();
|
||||||
|
RegisterValue spVal = ctx.getRegisterValue(sp, entry);
|
||||||
|
if (spVal == null || !spVal.hasValue()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address spAddr = cSpec.getStackBaseSpace().getAddress(spVal.getUnsignedValue().longValue());
|
||||||
|
if (cSpec.stackGrowsNegative()) {
|
||||||
|
Address max = spAddr.subtractWrap(1);
|
||||||
|
Address min = spAddr.subtractWrap(stackSize);
|
||||||
|
if (min.compareTo(max) > 0) {
|
||||||
|
return new AddressRangeImpl(max.getAddressSpace().getMinAddress(), max);
|
||||||
|
}
|
||||||
|
return new AddressRangeImpl(min, max);
|
||||||
|
}
|
||||||
|
// Grows positive
|
||||||
|
Address min = spAddr;
|
||||||
|
Address max = spAddr.addWrap(stackSize - 1);
|
||||||
|
if (min.compareTo(max) > 0) {
|
||||||
|
return new AddressRangeImpl(min, min.getAddressSpace().getMaxAddress());
|
||||||
|
}
|
||||||
|
return new AddressRangeImpl(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose an assumed stack range by examining the program's memory map for a
|
||||||
|
* {@value #BLOCK_NAME_STACK} block.
|
||||||
|
*
|
||||||
|
* @param program the program to examine
|
||||||
|
* @return the range assumed to be reserved for the stack, or null if no
|
||||||
|
* {@value #BLOCK_NAME_STACK} block is found.
|
||||||
|
*/
|
||||||
|
public static AddressRange chooseStackRangeFromBlock(Program program) {
|
||||||
|
AddressSpace space = program.getCompilerSpec().getStackBaseSpace();
|
||||||
|
MemoryBlock stackBlock = program.getMemory().getBlock(BLOCK_NAME_STACK);
|
||||||
|
if (stackBlock == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (space != stackBlock.getStart().getAddressSpace().getPhysicalSpace()) {
|
||||||
|
Msg.showError(EmulatorUtilities.class, null, "Invalid STACK block",
|
||||||
|
"The STACK block must be in the stack's base space. Ignoring.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new AddressRangeImpl(
|
||||||
|
stackBlock.getStart().getPhysicalAddress(),
|
||||||
|
stackBlock.getEnd().getPhysicalAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose an assumed stack range
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This will first examine the entry point's context for a stack pointer value using
|
||||||
|
* {@link #chooseStackRangeFromContext(Program, Address, int)}. Then, it will examine the
|
||||||
|
* progam's memory map using {@link #chooseStackRangeFromBlock(Program)}. Finally, it will
|
||||||
|
* search for a slack address range of the requested size. That is, it seeks a range that does
|
||||||
|
* not intersect any existing memory block. If possible, this will avoid choosing a stack range
|
||||||
|
* that intersects [0, 4096), so that 0-valued pointers are in fact invalid.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that a stack is not formally "allocated." Instead, the range is used to initialize a
|
||||||
|
* thread's stack pointer. Unless instrumentation is added to detect a stack overflow, nothing
|
||||||
|
* really prevents the program from exceeding the returned range. Thus, {@code stackSize} should
|
||||||
|
* be large enough to accommodate the target. Additionally, the user or client code should be
|
||||||
|
* prepared for undefined behavior caused by an unmitigated stack overflow.
|
||||||
|
*
|
||||||
|
* @param program the program
|
||||||
|
* @param entry the entry point, in case context there defines an initial stack pointer
|
||||||
|
* @param stackSize the maximum expected size of the stack
|
||||||
|
* @return the chosen range assumed to be used for the stack
|
||||||
|
*/
|
||||||
|
public static AddressRange chooseStackRange(Program program, Address entry, int stackSize) {
|
||||||
|
AddressRange customByContext = chooseStackRangeFromContext(program, entry, stackSize);
|
||||||
|
if (customByContext != null) {
|
||||||
|
return customByContext;
|
||||||
|
}
|
||||||
|
AddressRange customByBlock = chooseStackRangeFromBlock(program);
|
||||||
|
if (customByBlock != null) {
|
||||||
|
return customByBlock;
|
||||||
|
}
|
||||||
|
// Search for a range of the given size outside any block
|
||||||
|
AddressSpace space = program.getCompilerSpec().getStackBaseSpace();
|
||||||
|
Address max = space.getMaxAddress();
|
||||||
|
AddressSet eligible;
|
||||||
|
if (max.getOffsetAsBigInteger().compareTo(BigInteger.valueOf(0x1000)) < 0) {
|
||||||
|
eligible = new AddressSet(space.getMinAddress(), max);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
eligible = new AddressSet(space.getAddress(0x1000), max);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressSetView left = new DifferenceAddressSetView(eligible, program.getMemory());
|
||||||
|
for (AddressRange candidate : left) {
|
||||||
|
if (Long.compareUnsigned(candidate.getLength(), stackSize) >= 0) {
|
||||||
|
try {
|
||||||
|
return new AddressRangeImpl(candidate.getMinAddress(), stackSize);
|
||||||
|
}
|
||||||
|
catch (AddressOverflowException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose an assumed stack range of size {@value #DEFAULT_STACK_SIZE}
|
||||||
|
*
|
||||||
|
* @see #chooseStackRange(Program, Address, int)
|
||||||
|
* @param program the program
|
||||||
|
* @param entry the entry point, in case context there defines an initial stack pointer
|
||||||
|
* @return the chosen range assumed to be used for the stack
|
||||||
|
*/
|
||||||
|
public static AddressRange chooseStackRange(Program program, Address entry) {
|
||||||
|
return chooseStackRange(program, entry, DEFAULT_STACK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a thread to emulate a given function
|
||||||
|
*
|
||||||
|
* @param <T> the type of values in the emulator
|
||||||
|
* @param thread the thread whose state to initialize
|
||||||
|
* @param function the function to prepare to enter
|
||||||
|
* @param stackSize the maximum expected size of the stack
|
||||||
|
*/
|
||||||
|
public static <T> void initializeForFunction(PcodeThread<T> thread, Function function,
|
||||||
|
int stackSize) {
|
||||||
|
PcodeArithmetic<T> arithmetic = thread.getArithmetic();
|
||||||
|
|
||||||
|
Program program = function.getProgram();
|
||||||
|
Address entry = function.getEntryPoint();
|
||||||
|
CompilerSpec cSpec = program.getCompilerSpec();
|
||||||
|
Register sp = cSpec.getStackPointer();
|
||||||
|
ThreadPcodeExecutorState<T> state = thread.getState();
|
||||||
|
|
||||||
|
ProgramProcessorContext ctx =
|
||||||
|
new ProgramProcessorContext(program.getProgramContext(), entry);
|
||||||
|
for (Register reg : ctx.getRegisters()) {
|
||||||
|
if (!reg.isBaseRegister()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
RegisterValue rv = ctx.getRegisterValue(reg);
|
||||||
|
if (rv == null || !rv.hasAnyValue()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* NOTE: In theory, there's no need to combine masked values, if this is a fresh
|
||||||
|
* emulator. If I had to guess, the client would want their values to take precedence,
|
||||||
|
* so they should overwrite the values after calling this method. Combining can be
|
||||||
|
* problematic, because the emulator could return some abstraction for the current
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
state.setRegisterValue(rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressRange stack = chooseStackRange(program, entry);
|
||||||
|
long stackOffset = cSpec.stackGrowsNegative() ? stack.getMaxAddress().getOffset() + 1
|
||||||
|
: stack.getMinAddress().getOffset();
|
||||||
|
state.setVar(sp, arithmetic.fromConst(stackOffset, sp.getMinimumByteSize()));
|
||||||
|
|
||||||
|
thread.overrideCounter(entry);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,12 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.pcode.exec;
|
package ghidra.pcode.exec;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.lang.Register;
|
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
|
@ -277,4 +277,175 @@ public interface PcodeExecutorStatePiece<A, T> {
|
||||||
* dark.
|
* dark.
|
||||||
*/
|
*/
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to set a variable to a concrete value
|
||||||
|
*
|
||||||
|
* @param address the address in memory
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
default void setConcrete(Address address, byte[] value) {
|
||||||
|
setVar(address, value.length, false, getArithmetic().fromConst(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to inspect the concrete value of a variable
|
||||||
|
*
|
||||||
|
* @param address the address in memory
|
||||||
|
* @param size the number of bytes to inspect
|
||||||
|
* @return the value
|
||||||
|
* @throws ConcretionError if the value cannot be made concrete
|
||||||
|
*/
|
||||||
|
default byte[] inspectConcrete(Address address, int size) {
|
||||||
|
return getArithmetic().toConcrete(getVar(address, size, false, Reason.INSPECT),
|
||||||
|
Purpose.INSPECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to set a variable to a concrete value as a {@link BigInteger}
|
||||||
|
*
|
||||||
|
* @param address the address is memory
|
||||||
|
* @param size the size of the variable (in bytes)
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
default void setBigInteger(Address address, int size, BigInteger value) {
|
||||||
|
setVar(address, size, false, getArithmetic().fromConst(value, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to inspect the concrete value of a variable as a {@link BigInteger}
|
||||||
|
*
|
||||||
|
* @param address the address in memory
|
||||||
|
* @param size the number of bytes to inspect
|
||||||
|
* @return the value
|
||||||
|
* @throws ConcretionError if the value cannot be made concrete
|
||||||
|
*/
|
||||||
|
default BigInteger inspectBigInteger(Address address, int size) {
|
||||||
|
return getArithmetic().toBigInteger(getVar(address, size, false, Reason.INSPECT),
|
||||||
|
Purpose.INSPECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to set a variable to a concrete value as a {@code long}
|
||||||
|
*
|
||||||
|
* @param address the address is memory
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
default void setLong(Address address, long value) {
|
||||||
|
setVar(address, Long.BYTES, false, getArithmetic().fromConst(value, Long.BYTES));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to inspect the concrete value of a variable as a {@code long}
|
||||||
|
*
|
||||||
|
* @param address the address in memory
|
||||||
|
* @return the value
|
||||||
|
* @throws ConcretionError if the value cannot be made concrete
|
||||||
|
*/
|
||||||
|
default long inspectLong(Address address) {
|
||||||
|
return getArithmetic().toLong(getVar(address, Long.BYTES, false, Reason.INSPECT),
|
||||||
|
Purpose.INSPECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to set a variable to a concrete value as an {@code int}
|
||||||
|
*
|
||||||
|
* @param address the address is memory
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
default void setInt(Address address, int value) {
|
||||||
|
setVar(address, Integer.BYTES, false, getArithmetic().fromConst(value, Integer.BYTES));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to inspect the concrete value of a variable as an {@code int}
|
||||||
|
*
|
||||||
|
* @param address the address in memory
|
||||||
|
* @return the value
|
||||||
|
* @throws ConcretionError if the value cannot be made concrete
|
||||||
|
*/
|
||||||
|
default int inspectInt(Address address) {
|
||||||
|
return (int) getArithmetic().toLong(getVar(address, Integer.BYTES, false, Reason.INSPECT),
|
||||||
|
Purpose.INSPECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to set a variable to a concrete value as a {@code short}
|
||||||
|
*
|
||||||
|
* @param address the address is memory
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
default void setShort(Address address, short value) {
|
||||||
|
setVar(address, Short.BYTES, false, getArithmetic().fromConst(value, Short.BYTES));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to inspect the concrete value of a variable as a {@code short}
|
||||||
|
*
|
||||||
|
* @param address the address in memory
|
||||||
|
* @return the value
|
||||||
|
* @throws ConcretionError if the value cannot be made concrete
|
||||||
|
*/
|
||||||
|
default short inspectShort(Address address) {
|
||||||
|
return (short) getArithmetic().toLong(getVar(address, Short.BYTES, false, Reason.INSPECT),
|
||||||
|
Purpose.INSPECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to set a variable to a concrete value as a {@code byte}
|
||||||
|
*
|
||||||
|
* @param address the address is memory
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
default void setByte(Address address, byte value) {
|
||||||
|
setVar(address, Byte.BYTES, false, getArithmetic().fromConst(value, Byte.BYTES));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to inspect the concrete value of a variable as a {@code byte}
|
||||||
|
*
|
||||||
|
* @param address the address in memory
|
||||||
|
* @return the value
|
||||||
|
* @throws ConcretionError if the value cannot be made concrete
|
||||||
|
*/
|
||||||
|
default byte inspectByte(Address address) {
|
||||||
|
return (byte) getArithmetic().toLong(getVar(address, Byte.BYTES, false, Reason.INSPECT),
|
||||||
|
Purpose.INSPECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to set a register variable to a concrete value as a {@link RegisterValue}
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>NOTE:</b> The register from the given value does not have to match the given register, but
|
||||||
|
* their <em>sizes</em> should at least match. This permits simpler moving of values from one
|
||||||
|
* register to another. If the sizes do not match, the behavior is undefined.
|
||||||
|
*
|
||||||
|
* @param register the register
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
default void setRegisterValue(Register register, RegisterValue value) {
|
||||||
|
setVar(register, getArithmetic().fromConst(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to set a register variable to a concrete value as a {@link RegisterValue}
|
||||||
|
*
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
default void setRegisterValue(RegisterValue value) {
|
||||||
|
setRegisterValue(value.getRegister(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience to inspect the concrete value of a register variable as a {@link RegisterValue}
|
||||||
|
*
|
||||||
|
* @param register the register
|
||||||
|
* @return the value
|
||||||
|
* @throws ConcretionError if the value cannot be made concrete
|
||||||
|
*/
|
||||||
|
default RegisterValue inspectRegisterValue(Register register) {
|
||||||
|
return getArithmetic().toRegisterValue(register, getVar(register, Reason.INSPECT),
|
||||||
|
Purpose.INSPECT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,16 +13,28 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.util;
|
package generic.util;
|
||||||
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
import generic.util.PeekableIterator;
|
/**
|
||||||
|
* An implementation of {@link PeekableIterator} that only requires a way to seek out the next
|
||||||
|
* element. This will keep that element in hand until the next element is actually requested by the
|
||||||
|
* client. This does not invoke the search until the next element is required, either because the
|
||||||
|
* client called next or else wants to peek at it.
|
||||||
|
*
|
||||||
|
* @param <T> the type of elements
|
||||||
|
*/
|
||||||
public abstract class AbstractPeekableIterator<T> implements PeekableIterator<T> {
|
public abstract class AbstractPeekableIterator<T> implements PeekableIterator<T> {
|
||||||
protected T next = null;
|
protected T next = null;
|
||||||
protected boolean soughtNext = false;
|
protected boolean soughtNext = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the next element in this iterator, because the client called either {@link #next} or
|
||||||
|
* {@link #peek()}.
|
||||||
|
*
|
||||||
|
* @return the next element
|
||||||
|
*/
|
||||||
protected abstract T seekNext();
|
protected abstract T seekNext();
|
||||||
|
|
||||||
private void checkSeekNext() {
|
private void checkSeekNext() {
|
||||||
|
@ -52,5 +64,4 @@ public abstract class AbstractPeekableIterator<T> implements PeekableIterator<T>
|
||||||
}
|
}
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -13,25 +13,36 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package generic;
|
package generic.util;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO Document me
|
* Given an "outer" iterator and a mapping from its elements to "inner" iterators, this is a
|
||||||
|
* flattened iterator over elements from the inner iterators.
|
||||||
*
|
*
|
||||||
* Note the innerFactory may return null to skip an outer element.
|
* @param <O> the type of elements in the outer iterator
|
||||||
*
|
* @param <I> the type of elements in the inner and flattened iterators
|
||||||
* TODO: Test innerFactory returning null.
|
*/
|
||||||
*
|
public class FlattenedIterator<O, I> implements Iterator<I> {
|
||||||
* @param <O>
|
/**
|
||||||
* @param <I>
|
* Create a flattened iterator
|
||||||
|
* <p>
|
||||||
|
* This iterates over each element of {@code outer} and applies the given {@code innerFactory}
|
||||||
|
* to generate an "inner" iterator. The returned iterator will produce elements from the inner
|
||||||
|
* iterators as if concatentated. This is essentially a flat-map operation on iterators. Note
|
||||||
|
* the {@code innerFactory} may return null to skip an outer element.
|
||||||
|
*
|
||||||
|
* @param <O> the type of elements in the outer iterator
|
||||||
|
* @param <I> the type of elements in the inner and flattened iterators
|
||||||
|
* @param outer the outer iterator
|
||||||
|
* @param innerFactory a mapping from outer elements to inner iterators
|
||||||
|
* @return the flattened iterator
|
||||||
*/
|
*/
|
||||||
public class NestedIterator<O, I> implements Iterator<I> {
|
|
||||||
public static <O, I> Iterator<I> start(Iterator<O> outer,
|
public static <O, I> Iterator<I> start(Iterator<O> outer,
|
||||||
Function<O, Iterator<? extends I>> innerFactory) {
|
Function<O, Iterator<? extends I>> innerFactory) {
|
||||||
return new NestedIterator<>(outer, innerFactory);
|
return new FlattenedIterator<>(outer, innerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final Iterator<O> outer;
|
protected final Iterator<O> outer;
|
||||||
|
@ -40,7 +51,8 @@ public class NestedIterator<O, I> implements Iterator<I> {
|
||||||
protected Iterator<? extends I> inner;
|
protected Iterator<? extends I> inner;
|
||||||
protected Iterator<? extends I> preppedInner;
|
protected Iterator<? extends I> preppedInner;
|
||||||
|
|
||||||
protected NestedIterator(Iterator<O> outer, Function<O, Iterator<? extends I>> innerFactory) {
|
protected FlattenedIterator(Iterator<O> outer,
|
||||||
|
Function<O, Iterator<? extends I>> innerFactory) {
|
||||||
this.outer = outer;
|
this.outer = outer;
|
||||||
this.innerFactory = innerFactory;
|
this.innerFactory = innerFactory;
|
||||||
}
|
}
|
|
@ -13,12 +13,15 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.util;
|
package generic.util;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import generic.util.PeekableIterator;
|
import java.util.PriorityQueue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An iterator which merges sorted iterators according to a comparator
|
* An iterator which merges sorted iterators according to a comparator
|
||||||
|
@ -87,13 +90,16 @@ public class MergeSortingIterator<T> implements PeekableIterator<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a merge-sorting iterator which generates labeled values
|
* Construct a merge-sorting iterator which generates labeled values
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The map of iterators is a map of entries, each giving a label and an iterator to be merged.
|
* The map of iterators is a map of entries, each giving a label and an iterator to be merged.
|
||||||
* Each iterator must return values as sorted by the given comparator. The entries returned by
|
* Each iterator must return values as sorted by the given comparator. The entries returned by
|
||||||
* the combined iterator give the values in sorted order, but each has a the key indicating
|
* the combined iterator give the values in sorted order, but each has a the key indicating
|
||||||
* which given iterator returned that value. Note that the returned entry may be re-used by the
|
* which given iterator returned that value. Note that the returned entry may be re-used by the
|
||||||
* underlying implementation, so users needing to keep the entry should create a copy.
|
* underlying implementation, so users needing to keep the entry should create a copy.
|
||||||
|
* <p>
|
||||||
|
* The purpose of the iterator is to know which iterator provided a given entry in the merged
|
||||||
|
* result. While this has general utility, at the moment, it is only used in our tests to verify
|
||||||
|
* proper operation of the merge-sorting implementation.
|
||||||
*
|
*
|
||||||
* @param iterMap a map of labeled iterators
|
* @param iterMap a map of labeled iterators
|
||||||
* @param comparator the comparator of values
|
* @param comparator the comparator of values
|
||||||
|
@ -110,6 +116,12 @@ public class MergeSortingIterator<T> implements PeekableIterator<T> {
|
||||||
protected final Comparator<? super T> comparator;
|
protected final Comparator<? super T> comparator;
|
||||||
protected final PriorityQueue<PeekableIterator<? extends T>> queue;
|
protected final PriorityQueue<PeekableIterator<? extends T>> queue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a merge sorting iterator
|
||||||
|
*
|
||||||
|
* @param iterators a collection of iterators to merge
|
||||||
|
* @param comparator the comparator defining how the input and output iterators are sorted
|
||||||
|
*/
|
||||||
public MergeSortingIterator(Iterable<? extends Iterator<? extends T>> iterators,
|
public MergeSortingIterator(Iterable<? extends Iterator<? extends T>> iterators,
|
||||||
Comparator<? super T> comparator) {
|
Comparator<? super T> comparator) {
|
||||||
this.comparator = comparator;
|
this.comparator = comparator;
|
|
@ -26,10 +26,11 @@ import java.util.NoSuchElementException;
|
||||||
public interface PeekableIterator<T> extends Iterator<T> {
|
public interface PeekableIterator<T> extends Iterator<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the item that would be returned by calling {@link #next()}, but does not
|
* Returns the item that would be returned by calling {@link #next()}, but without incrementing
|
||||||
* increment the iterator as <code>next</code> would.
|
* the iterator.
|
||||||
*
|
*
|
||||||
* @return the item that would be returned by calling {@link #next()}
|
* @return the item that would be returned by calling {@link #next()}
|
||||||
|
* @throws NoSuchElementException if there is no element to peek at
|
||||||
*/
|
*/
|
||||||
public T peek() throws NoSuchElementException;
|
public T peek() throws NoSuchElementException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,19 +13,29 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.util;
|
package generic.util;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import generic.util.PeekableIterator;
|
/**
|
||||||
import generic.util.WrappingPeekableIterator;
|
* Utilities for working with a {@link PeekableIterator}
|
||||||
|
*/
|
||||||
public enum PeekableIterators {
|
public enum PeekableIterators {
|
||||||
;
|
;
|
||||||
public static <T> PeekableIterator<T> castOrWrap(Iterator<T> it) {
|
/**
|
||||||
if (it instanceof PeekableIterator) {
|
* Ensure that the given iterator is peekable
|
||||||
return (PeekableIterator<T>) it;
|
*
|
||||||
|
* <p>
|
||||||
|
* If it is already peekable, this casts it as such. If not, it wraps it in a peekable iterator.
|
||||||
|
*
|
||||||
|
* @param <E> the type of elements
|
||||||
|
* @param it the iterator
|
||||||
|
* @return the peekable iterator
|
||||||
|
*/
|
||||||
|
public static <E> PeekableIterator<E> castOrWrap(Iterator<E> it) {
|
||||||
|
if (it instanceof PeekableIterator<E> pi) {
|
||||||
|
return pi;
|
||||||
}
|
}
|
||||||
return new WrappingPeekableIterator<T>(it);
|
return new WrappingPeekableIterator<E>(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,59 +16,37 @@
|
||||||
package generic.util;
|
package generic.util;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of {@link PeekableIterator} that can take a Java {@link Iterator} and
|
* An implementation of {@link PeekableIterator} that can take a Java {@link Iterator} and wrap it
|
||||||
* wrap it to implement the {@link PeekableIterator} interface.
|
* to implement the {@link PeekableIterator} interface.
|
||||||
*
|
*
|
||||||
* @param <T> the type of the iterator
|
* @param <T> the type of the iterator
|
||||||
*/
|
*/
|
||||||
public class WrappingPeekableIterator<T> implements PeekableIterator<T> {
|
public class WrappingPeekableIterator<T> extends AbstractPeekableIterator<T> {
|
||||||
|
|
||||||
private Iterator<T> iterator;
|
private final Iterator<T> iterator;
|
||||||
private T peek;
|
|
||||||
private boolean peeked;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap the given iterator
|
||||||
|
*
|
||||||
|
* @see PeekableIterators#castOrWrap(Iterator)
|
||||||
|
* @param iterator the iterator
|
||||||
|
*/
|
||||||
public WrappingPeekableIterator(Iterator<T> iterator) {
|
public WrappingPeekableIterator(Iterator<T> iterator) {
|
||||||
this.iterator = iterator;
|
this.iterator = iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
if (peeked) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return iterator.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T next() {
|
|
||||||
if (peeked) {
|
|
||||||
peeked = false;
|
|
||||||
return peek;
|
|
||||||
}
|
|
||||||
return iterator.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
public void remove() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T peek() throws NoSuchElementException {
|
protected T seekNext() {
|
||||||
if (peeked) {
|
if (!iterator.hasNext()) {
|
||||||
return peek;
|
return null;
|
||||||
}
|
}
|
||||||
|
return iterator.next();
|
||||||
if (!hasNext()) {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
peek = next();
|
|
||||||
peeked = true;
|
|
||||||
return peek;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,20 +13,26 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package generic;
|
package generic.util;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.collections4.IteratorUtils;
|
import org.apache.commons.collections4.IteratorUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class NestedIteratorTest {
|
public class FlattenedIteratorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyOuter() {
|
public void testEmptyOuter() {
|
||||||
List<Object> result =
|
List<Object> result =
|
||||||
IteratorUtils.toList(NestedIterator.start(Collections.emptyIterator(), o -> {
|
IteratorUtils.toList(FlattenedIterator.start(Collections.emptyIterator(), o -> {
|
||||||
fail();
|
fail();
|
||||||
return null;
|
return null;
|
||||||
}));
|
}));
|
||||||
|
@ -36,27 +42,27 @@ public class NestedIteratorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSingleOuterEmptyInner() {
|
public void testSingleOuterEmptyInner() {
|
||||||
List<Object> result = IteratorUtils.toList(
|
List<Object> result = IteratorUtils.toList(
|
||||||
NestedIterator.start(List.of("Test").iterator(), s -> Collections.emptyIterator()));
|
FlattenedIterator.start(List.of("Test").iterator(), s -> Collections.emptyIterator()));
|
||||||
assertTrue(result.isEmpty());
|
assertTrue(result.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDoubleOuterEmptyInner() {
|
public void testDoubleOuterEmptyInner() {
|
||||||
List<Object> result = IteratorUtils.toList(
|
List<Object> result = IteratorUtils.toList(
|
||||||
NestedIterator.start(List.of("T1", "T2").iterator(), s -> Collections.emptyIterator()));
|
FlattenedIterator.start(List.of("T1", "T2").iterator(), s -> Collections.emptyIterator()));
|
||||||
assertTrue(result.isEmpty());
|
assertTrue(result.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSingleOuterSingleInner() {
|
public void testSingleOuterSingleInner() {
|
||||||
List<String> result = IteratorUtils
|
List<String> result = IteratorUtils
|
||||||
.toList(NestedIterator.start(List.of(0).iterator(), n -> List.of("Test").iterator()));
|
.toList(FlattenedIterator.start(List.of(0).iterator(), n -> List.of("Test").iterator()));
|
||||||
assertEquals(List.of("Test"), result);
|
assertEquals(List.of("Test"), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFirstEmptySecondSingleton() {
|
public void testFirstEmptySecondSingleton() {
|
||||||
List<String> result = IteratorUtils.toList(NestedIterator.start(List.of(0, 1).iterator(),
|
List<String> result = IteratorUtils.toList(FlattenedIterator.start(List.of(0, 1).iterator(),
|
||||||
n -> n == 0 ? Collections.emptyIterator() : List.of("Test").iterator()));
|
n -> n == 0 ? Collections.emptyIterator() : List.of("Test").iterator()));
|
||||||
assertEquals(List.of("Test"), result);
|
assertEquals(List.of("Test"), result);
|
||||||
}
|
}
|
||||||
|
@ -64,20 +70,20 @@ public class NestedIteratorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSingleOuterDoubleInner() {
|
public void testSingleOuterDoubleInner() {
|
||||||
List<String> result = IteratorUtils.toList(
|
List<String> result = IteratorUtils.toList(
|
||||||
NestedIterator.start(List.of(0).iterator(), n -> List.of("T1", "T2").iterator()));
|
FlattenedIterator.start(List.of(0).iterator(), n -> List.of("T1", "T2").iterator()));
|
||||||
assertEquals(List.of("T1", "T2"), result);
|
assertEquals(List.of("T1", "T2"), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDoubleOuterDoubleInner() {
|
public void testDoubleOuterDoubleInner() {
|
||||||
List<String> result = IteratorUtils.toList(NestedIterator.start(List.of(0, 1).iterator(),
|
List<String> result = IteratorUtils.toList(FlattenedIterator.start(List.of(0, 1).iterator(),
|
||||||
n -> (n == 0 ? List.of("T1", "T2") : List.of("T3", "T4")).iterator()));
|
n -> (n == 0 ? List.of("T1", "T2") : List.of("T3", "T4")).iterator()));
|
||||||
assertEquals(List.of("T1", "T2", "T3", "T4"), result);
|
assertEquals(List.of("T1", "T2", "T3", "T4"), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultipleHasNextCalls() {
|
public void testMultipleHasNextCalls() {
|
||||||
Iterator<String> it = NestedIterator.start(List.of(0, 1).iterator(),
|
Iterator<String> it = FlattenedIterator.start(List.of(0, 1).iterator(),
|
||||||
n -> (n == 0 ? List.of("T1", "T2") : List.of("T3", "T4")).iterator());
|
n -> (n == 0 ? List.of("T1", "T2") : List.of("T3", "T4")).iterator());
|
||||||
assertTrue(it.hasNext());
|
assertTrue(it.hasNext());
|
||||||
assertTrue(it.hasNext());
|
assertTrue(it.hasNext());
|
||||||
|
@ -100,7 +106,7 @@ public class NestedIteratorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoHasNextCalls() {
|
public void testNoHasNextCalls() {
|
||||||
Iterator<String> it = NestedIterator.start(List.of(0, 1).iterator(),
|
Iterator<String> it = FlattenedIterator.start(List.of(0, 1).iterator(),
|
||||||
n -> (n == 0 ? List.of("T1", "T2") : List.of("T3", "T4")).iterator());
|
n -> (n == 0 ? List.of("T1", "T2") : List.of("T3", "T4")).iterator());
|
||||||
assertEquals("T1", it.next());
|
assertEquals("T1", it.next());
|
||||||
assertEquals("T2", it.next());
|
assertEquals("T2", it.next());
|
||||||
|
@ -116,7 +122,7 @@ public class NestedIteratorTest {
|
||||||
List<String> b = new ArrayList<>(List.of("T3", "T4"));
|
List<String> b = new ArrayList<>(List.of("T3", "T4"));
|
||||||
List<List<String>> listList = new ArrayList<>(List.of(a, b));
|
List<List<String>> listList = new ArrayList<>(List.of(a, b));
|
||||||
|
|
||||||
Iterator<String> it = NestedIterator.start(listList.iterator(), l -> l.iterator());
|
Iterator<String> it = FlattenedIterator.start(listList.iterator(), l -> l.iterator());
|
||||||
assertEquals("T1", it.next());
|
assertEquals("T1", it.next());
|
||||||
assertEquals("T2", it.next());
|
assertEquals("T2", it.next());
|
||||||
assertTrue(it.hasNext()); // Odd to do this right before a remove, but....
|
assertTrue(it.hasNext()); // Odd to do this right before a remove, but....
|
|
@ -20,8 +20,23 @@ import java.util.List;
|
||||||
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract implementation of {@link AddressSetView} that provides suitable defaults for as many
|
||||||
|
* of the required methods as reasonable.
|
||||||
|
*/
|
||||||
public abstract class AbstractAddressSetView implements AddressSetView {
|
public abstract class AbstractAddressSetView implements AddressSetView {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust the given start address so that if it lands in the given iterator's next range, that
|
||||||
|
* range would be entirely included.
|
||||||
|
*
|
||||||
|
* @param rev the iterator. Note only the first element, if present, of this iterator is
|
||||||
|
* considered. The client must ensure it's properly positioned.
|
||||||
|
* @param start the proposed start address
|
||||||
|
* @param forward true for forward iteration, i.e., the adjustment will be to the range's
|
||||||
|
* minimum address, if applicable.
|
||||||
|
* @return the adjusted start
|
||||||
|
*/
|
||||||
protected static Address fixStart(AddressRangeIterator rev, Address start, boolean forward) {
|
protected static Address fixStart(AddressRangeIterator rev, Address start, boolean forward) {
|
||||||
if (!rev.hasNext()) {
|
if (!rev.hasNext()) {
|
||||||
return start;
|
return start;
|
|
@ -18,11 +18,14 @@ package ghidra.util;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
import generic.NestedIterator;
|
import generic.util.FlattenedIterator;
|
||||||
import generic.util.PeekableIterator;
|
import generic.util.PeekableIterator;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
|
||||||
public class AddressIteratorAdapter extends NestedIterator<AddressRange, Address>
|
/**
|
||||||
|
* Convert an {@link AddressRange} iterator to an {@link AddressIterator}.
|
||||||
|
*/
|
||||||
|
public class AddressIteratorAdapter extends FlattenedIterator<AddressRange, Address>
|
||||||
implements AddressIterator {
|
implements AddressIterator {
|
||||||
|
|
||||||
protected static class ForwardAddressIterator implements PeekableIterator<Address> {
|
protected static class ForwardAddressIterator implements PeekableIterator<Address> {
|
||||||
|
@ -89,15 +92,39 @@ public class AddressIteratorAdapter extends NestedIterator<AddressRange, Address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over the addresses in the given range
|
||||||
|
*
|
||||||
|
* @param range the range
|
||||||
|
* @param forward true to iterate forward, false for backward
|
||||||
|
* @return the iterable
|
||||||
|
*/
|
||||||
public static Iterable<Address> forRange(AddressRange range, boolean forward) {
|
public static Iterable<Address> forRange(AddressRange range, boolean forward) {
|
||||||
return () -> forward ? new ForwardAddressIterator(range)
|
return () -> forward ? new ForwardAddressIterator(range)
|
||||||
: new BackwardAddressIterator(range);
|
: new BackwardAddressIterator(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an {@link AddressIterator} over the given address ranges
|
||||||
|
*
|
||||||
|
* @param outer an iterator of address ranges
|
||||||
|
* @param forward true for forward iteration. Otherwise backward iteration. This flag must be
|
||||||
|
* consistent with the order of the given outer iterator.
|
||||||
|
*/
|
||||||
public AddressIteratorAdapter(Iterator<AddressRange> outer, boolean forward) {
|
public AddressIteratorAdapter(Iterator<AddressRange> outer, boolean forward) {
|
||||||
super(outer, forward ? ForwardAddressIterator::new : BackwardAddressIterator::new);
|
super(outer, forward ? ForwardAddressIterator::new : BackwardAddressIterator::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an {@link AddressIterator} over the given address ranges, truncating the initial
|
||||||
|
* range to the given start
|
||||||
|
*
|
||||||
|
* @param outer the iterator of address ranges, the first of which must contain or come after
|
||||||
|
* the given start.
|
||||||
|
* @param start the starting address
|
||||||
|
* @param forward true for forward iteration. Otherwise backward iteration. This flag must be
|
||||||
|
* consistent with the order of the given outer iterator.o
|
||||||
|
*/
|
||||||
public AddressIteratorAdapter(Iterator<AddressRange> outer, Address start, boolean forward) {
|
public AddressIteratorAdapter(Iterator<AddressRange> outer, Address start, boolean forward) {
|
||||||
super(outer, forward ? ar -> {
|
super(outer, forward ? ar -> {
|
||||||
if (!ar.contains(start)) {
|
if (!ar.contains(start)) {
|
|
@ -18,14 +18,29 @@ package ghidra.util;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
import ghidra.program.model.address.AddressRange;
|
import ghidra.program.model.address.AddressRange;
|
||||||
|
import ghidra.program.model.address.AddressSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparators used for sorting address ranges
|
||||||
|
*/
|
||||||
public enum AddressRangeComparators implements Comparator<AddressRange> {
|
public enum AddressRangeComparators implements Comparator<AddressRange> {
|
||||||
|
/**
|
||||||
|
* Compare ranges by their minimum address and order them smallest first.
|
||||||
|
*/
|
||||||
FORWARD {
|
FORWARD {
|
||||||
@Override
|
@Override
|
||||||
public int compare(AddressRange a, AddressRange b) {
|
public int compare(AddressRange a, AddressRange b) {
|
||||||
return a.getMinAddress().compareTo(b.getMinAddress());
|
return a.getMinAddress().compareTo(b.getMinAddress());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Compare ranges by their maximum address and order them largest first.
|
||||||
|
*
|
||||||
|
* @implNote Which address is compared might not ordinarily matter, since {@link AddressSet}
|
||||||
|
* requires a disjoint union of ranges. However, these comparators often compare
|
||||||
|
* ranges from different sets, e.g., in order to merge two or more iterators. Thus, in
|
||||||
|
* reverse, we want to ensure ranges are ordered by their <em>maximum</em> address.
|
||||||
|
*/
|
||||||
BACKWARD {
|
BACKWARD {
|
||||||
@Override
|
@Override
|
||||||
public int compare(AddressRange a, AddressRange b) {
|
public int compare(AddressRange a, AddressRange b) {
|
|
@ -24,6 +24,10 @@ import org.apache.commons.collections4.IteratorUtils;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.util.TwoWayBreakdownAddressRangeIterator.Which;
|
import ghidra.util.TwoWayBreakdownAddressRangeIterator.Which;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for manipulating iterators over {@link AddressRange}s. Notably, this allows the
|
||||||
|
* creation of lazily computed set operations on {@link AddressSetView}s.
|
||||||
|
*/
|
||||||
public enum AddressRangeIterators {
|
public enum AddressRangeIterators {
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -50,13 +54,30 @@ public enum AddressRangeIterators {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility for satisfying the type checker. This just forwards the method calls so that an
|
||||||
|
* {@link Iterator} over {@link AddressRange} can be used where an {@link AddressRangeIterator}
|
||||||
|
* is required. If only Java had type aliasing....
|
||||||
|
*
|
||||||
|
* @param it the iterator
|
||||||
|
* @return the wrapper, or the same iterator if it is already an {@link AddressRangeIterator}
|
||||||
|
*/
|
||||||
public static AddressRangeIterator castOrWrap(Iterator<AddressRange> it) {
|
public static AddressRangeIterator castOrWrap(Iterator<AddressRange> it) {
|
||||||
if (it instanceof AddressRangeIterator) {
|
if (it instanceof AddressRangeIterator ari) {
|
||||||
return (AddressRangeIterator) it;
|
return ari;
|
||||||
}
|
}
|
||||||
return new WrappingAddressRangeIterator(it);
|
return new WrappingAddressRangeIterator(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an iterator over the union of address ranges in the given iterators
|
||||||
|
*
|
||||||
|
* @see UnionAddressSetView
|
||||||
|
* @param iterators the iterators to union
|
||||||
|
* @param forward true for forward iteration. The given iterators must all return ranges in the
|
||||||
|
* order indicated by this flag.
|
||||||
|
* @return the iterator over the union
|
||||||
|
*/
|
||||||
public static AddressRangeIterator union(Collection<Iterator<AddressRange>> iterators,
|
public static AddressRangeIterator union(Collection<Iterator<AddressRange>> iterators,
|
||||||
boolean forward) {
|
boolean forward) {
|
||||||
return new UnionAddressRangeIterator(iterators, forward);
|
return new UnionAddressRangeIterator(iterators, forward);
|
||||||
|
@ -70,6 +91,17 @@ public enum AddressRangeIterators {
|
||||||
: range.getMinAddress().compareTo(start) <= 0;
|
: range.getMinAddress().compareTo(start) <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an iterator over the difference between two address range iterators
|
||||||
|
*
|
||||||
|
* @see DifferenceAddressSetView
|
||||||
|
* @param a the minuend
|
||||||
|
* @param b the subtrahend
|
||||||
|
* @param start the starting address, or null
|
||||||
|
* @param forward true for forward iteration. The given iterators must all return ranges in the
|
||||||
|
* order indicated by this flag.
|
||||||
|
* @return the iterator over the difference
|
||||||
|
*/
|
||||||
public static AddressRangeIterator subtract(Iterator<AddressRange> a, Iterator<AddressRange> b,
|
public static AddressRangeIterator subtract(Iterator<AddressRange> a, Iterator<AddressRange> b,
|
||||||
Address start, boolean forward) {
|
Address start, boolean forward) {
|
||||||
return new WrappingAddressRangeIterator(IteratorUtils.transformedIterator(
|
return new WrappingAddressRangeIterator(IteratorUtils.transformedIterator(
|
||||||
|
@ -78,6 +110,17 @@ public enum AddressRangeIterators {
|
||||||
e -> e.getKey()));
|
e -> e.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an iterator over the symmetric difference between two address range iterators
|
||||||
|
*
|
||||||
|
* @see SymmetricDifferenceAddressSetView
|
||||||
|
* @param a the first iterator
|
||||||
|
* @param b the second iterator
|
||||||
|
* @param start the starting address, or null
|
||||||
|
* @param forward true for forward iteration. The given iterators must all return ranges in the
|
||||||
|
* order indicated by this flag.
|
||||||
|
* @return the iterator over the symmetric difference
|
||||||
|
*/
|
||||||
public static AddressRangeIterator xor(Iterator<AddressRange> a, Iterator<AddressRange> b,
|
public static AddressRangeIterator xor(Iterator<AddressRange> a, Iterator<AddressRange> b,
|
||||||
Address start, boolean forward) {
|
Address start, boolean forward) {
|
||||||
Iterator<Entry<AddressRange, Which>> eit =
|
Iterator<Entry<AddressRange, Which>> eit =
|
||||||
|
@ -93,6 +136,16 @@ public enum AddressRangeIterators {
|
||||||
return new WrappingAddressRangeIterator(result);
|
return new WrappingAddressRangeIterator(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an iterator over the intersection between two address range iterators
|
||||||
|
*
|
||||||
|
* @see IntersectionAddressSetView
|
||||||
|
* @param a the first iterator
|
||||||
|
* @param b the second iterator
|
||||||
|
* @param forward true for forward iteration. The given iterators must all return ranges in the
|
||||||
|
* order indicated by this flag.
|
||||||
|
* @return the iterator over the symmetric difference
|
||||||
|
*/
|
||||||
public static AddressRangeIterator intersect(Iterator<AddressRange> a, Iterator<AddressRange> b,
|
public static AddressRangeIterator intersect(Iterator<AddressRange> a, Iterator<AddressRange> b,
|
||||||
boolean forward) {
|
boolean forward) {
|
||||||
return new WrappingAddressRangeIterator(IteratorUtils.transformedIterator(
|
return new WrappingAddressRangeIterator(IteratorUtils.transformedIterator(
|
|
@ -17,12 +17,33 @@ package ghidra.util;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressRange;
|
||||||
|
import ghidra.program.model.address.AddressRangeImpl;
|
||||||
|
import ghidra.program.model.address.AddressRangeIterator;
|
||||||
|
import ghidra.program.model.address.AddressSetView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lazily computed {@link AddressSetView} defined as the difference between two given
|
||||||
|
* {@link AddressSetView}s.
|
||||||
|
* <p>
|
||||||
|
* This is equivalent to using {@link AddressSetView#subtract(AddressSetView)}, but does not
|
||||||
|
* materialize the difference. The choice of one over the other depends on the number of ranges in
|
||||||
|
* the inputs and the frequency of use of the result. With few ranges, or in cases where you need to
|
||||||
|
* access the entire result, anyway, just use the normal {@link AddressRange}. In cases with many,
|
||||||
|
* many ranges and where only a small part of the result needs to be computed, use this view. It may
|
||||||
|
* also be advantageous to use this view if the inputs are themselves computed lazily.
|
||||||
|
*/
|
||||||
public class DifferenceAddressSetView extends AbstractAddressSetView {
|
public class DifferenceAddressSetView extends AbstractAddressSetView {
|
||||||
private final AddressSetView a;
|
private final AddressSetView a;
|
||||||
private final AddressSetView b;
|
private final AddressSetView b;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the difference between two address sets
|
||||||
|
*
|
||||||
|
* @param a the minuend
|
||||||
|
* @param b the subtrahend
|
||||||
|
*/
|
||||||
public DifferenceAddressSetView(AddressSetView a, AddressSetView b) {
|
public DifferenceAddressSetView(AddressSetView a, AddressSetView b) {
|
||||||
this.a = a;
|
this.a = a;
|
||||||
this.b = b;
|
this.b = b;
|
|
@ -18,12 +18,33 @@ package ghidra.util;
|
||||||
import static ghidra.util.MathUtilities.cmax;
|
import static ghidra.util.MathUtilities.cmax;
|
||||||
import static ghidra.util.MathUtilities.cmin;
|
import static ghidra.util.MathUtilities.cmin;
|
||||||
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressRange;
|
||||||
|
import ghidra.program.model.address.AddressRangeIterator;
|
||||||
|
import ghidra.program.model.address.AddressSetView;
|
||||||
|
import ghidra.program.model.address.EmptyAddressRangeIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lazily computed {@link AddressSetView} defined as the intersection of two given
|
||||||
|
* {@link AddressSetView}s.
|
||||||
|
* <p>
|
||||||
|
* This is equivalent to using {@link AddressSetView#intersect(AddressSetView)}, but does not
|
||||||
|
* materialize the intersection. The choice of one over the other depends on the number of ranges in
|
||||||
|
* the inputs and the frequency of use of the result. With few ranges, or in cases where you need to
|
||||||
|
* access the entire result, anyway, just use the normal {@link AddressRange}. In cases with many,
|
||||||
|
* many ranges and where only a small part of the result needs to be computed, use this view. It may
|
||||||
|
* also be advantageous to use this view if the inputs are themselves computed lazily.
|
||||||
|
*/
|
||||||
public class IntersectionAddressSetView extends AbstractAddressSetView {
|
public class IntersectionAddressSetView extends AbstractAddressSetView {
|
||||||
private final AddressSetView a;
|
private final AddressSetView a;
|
||||||
private final AddressSetView b;
|
private final AddressSetView b;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the intersection of two address sets
|
||||||
|
*
|
||||||
|
* @param a the first set
|
||||||
|
* @param b the second set
|
||||||
|
*/
|
||||||
public IntersectionAddressSetView(AddressSetView a, AddressSetView b) {
|
public IntersectionAddressSetView(AddressSetView a, AddressSetView b) {
|
||||||
this.a = a;
|
this.a = a;
|
||||||
this.b = b;
|
this.b = b;
|
|
@ -15,12 +15,34 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.util;
|
package ghidra.util;
|
||||||
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressRange;
|
||||||
|
import ghidra.program.model.address.AddressRangeIterator;
|
||||||
|
import ghidra.program.model.address.AddressSetView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lazily computed {@link AddressSetView} defined as the symmetric difference between two given
|
||||||
|
* {@link AddressSetView}s.
|
||||||
|
* <p>
|
||||||
|
* There is no equivalent method in {@link AddressSetView}, but it could be computed using a
|
||||||
|
* combination of {@link AddressSetView#subtract(AddressSetView)} and
|
||||||
|
* {@link AddressSetView#union(AddressSetView)}. However, this class does not materialize the
|
||||||
|
* result. The choice of one over the other depends on the number of ranges in the inputs and the
|
||||||
|
* frequency of use of the result. With few ranges, or in cases where you need to access the entire
|
||||||
|
* result, anyway, just use the normal {@link AddressRange}. In cases with many, many ranges and
|
||||||
|
* where only a small part of the result needs to be computed, use this view. It may also be
|
||||||
|
* advantageous to use this if the inputs are themselves computed lazily.
|
||||||
|
*/
|
||||||
public class SymmetricDifferenceAddressSetView extends AbstractAddressSetView {
|
public class SymmetricDifferenceAddressSetView extends AbstractAddressSetView {
|
||||||
private final AddressSetView a;
|
private final AddressSetView a;
|
||||||
private final AddressSetView b;
|
private final AddressSetView b;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the symmetric difference between two address sets
|
||||||
|
*
|
||||||
|
* @param a the first set
|
||||||
|
* @param b the second set
|
||||||
|
*/
|
||||||
public SymmetricDifferenceAddressSetView(AddressSetView a, AddressSetView b) {
|
public SymmetricDifferenceAddressSetView(AddressSetView a, AddressSetView b) {
|
||||||
this.a = a;
|
this.a = a;
|
||||||
this.b = b;
|
this.b = b;
|
|
@ -21,37 +21,91 @@ import static ghidra.util.MathUtilities.cmin;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import generic.util.PeekableIterator;
|
import generic.util.*;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.util.TwoWayBreakdownAddressRangeIterator.Which;
|
import ghidra.util.TwoWayBreakdownAddressRangeIterator.Which;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An iterator that takes two iterators over address ranges and "breaks down" where they do and do
|
||||||
|
* not overlap. Consider one iterator L that contains only [1,3], and another R that contains only
|
||||||
|
* [2,4]. The two could be plotted:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1 2 3 4
|
||||||
|
* [---L---]
|
||||||
|
* [---R---]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This will return an iterator over range-which pairs. "Which" indicates which iterators include
|
||||||
|
* the given range, {@link Which#LEFT}, {@link Which#RIGHT}, or {@link Which#BOTH}. There is no
|
||||||
|
* {@code NONE}, so gaps are omitted. For the example above:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1 2 3 4
|
||||||
|
* [L][-B--][R]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This supports the computation of difference, symmetric difference, and intersection. <b>NOTE:</b>
|
||||||
|
* Clients cannot save the entries returned by the iterator. The entry is only valid during
|
||||||
|
* iteration, and it is reused by the iterator for each subsequent entry.
|
||||||
|
*/
|
||||||
public class TwoWayBreakdownAddressRangeIterator
|
public class TwoWayBreakdownAddressRangeIterator
|
||||||
extends AbstractPeekableIterator<Entry<AddressRange, Which>> {
|
extends AbstractPeekableIterator<Entry<AddressRange, Which>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates which of the input iterators contain a range
|
||||||
|
*/
|
||||||
public enum Which {
|
public enum Which {
|
||||||
LEFT(true, false), RIGHT(false, true), BOTH(true, true);
|
/** Only the left included the range */
|
||||||
|
LEFT(true, false),
|
||||||
|
/** Only the right included the range */
|
||||||
|
RIGHT(false, true),
|
||||||
|
/** Both included the range */
|
||||||
|
BOTH(true, true);
|
||||||
|
|
||||||
private Which(boolean includesLeft, boolean includesRight) {
|
private Which(boolean includesLeft, boolean includesRight) {
|
||||||
this.includesLeft = includesLeft;
|
this.includesLeft = includesLeft;
|
||||||
this.includesRight = includesRight;
|
this.includesRight = includesRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Indicates the the left iterator includes this range */
|
||||||
public final boolean includesLeft;
|
public final boolean includesLeft;
|
||||||
|
/** Indicates that the right iterator includes this range */
|
||||||
public final boolean includesRight;
|
public final boolean includesRight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this range is included in the difference: {@code left - right}
|
||||||
|
*
|
||||||
|
* @return true if included
|
||||||
|
*/
|
||||||
public boolean inSubtract() {
|
public boolean inSubtract() {
|
||||||
return this == LEFT;
|
return this == LEFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this range is included in the symmetric difference: {@code left Δ right}
|
||||||
|
*
|
||||||
|
* @return true if included
|
||||||
|
*/
|
||||||
public boolean inXor() {
|
public boolean inXor() {
|
||||||
return this == LEFT || this == RIGHT;
|
return this == LEFT || this == RIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this range is included in the intersection: {@code left ∩ right}
|
||||||
|
*
|
||||||
|
* @return true if included
|
||||||
|
*/
|
||||||
public boolean inIntersect() {
|
public boolean inIntersect() {
|
||||||
return this == BOTH;
|
return this == BOTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mutable map entry
|
||||||
|
*/
|
||||||
public static class MyEntry implements Entry<AddressRange, Which> {
|
public static class MyEntry implements Entry<AddressRange, Which> {
|
||||||
private AddressRange key;
|
private AddressRange key;
|
||||||
private Which val;
|
private Which val;
|
||||||
|
@ -81,6 +135,14 @@ public class TwoWayBreakdownAddressRangeIterator
|
||||||
|
|
||||||
private final MyEntry entry = new MyEntry();
|
private final MyEntry entry = new MyEntry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an iterator that "breaks down" the two address range iterators.
|
||||||
|
*
|
||||||
|
* @param lit the iterator of ranges on the left
|
||||||
|
* @param rit the iterator of ranges on the right
|
||||||
|
* @param forward true for forward iteration, false for reverse. The input iterators must be
|
||||||
|
* ordered according to this flag.
|
||||||
|
*/
|
||||||
public TwoWayBreakdownAddressRangeIterator(Iterator<AddressRange> lit,
|
public TwoWayBreakdownAddressRangeIterator(Iterator<AddressRange> lit,
|
||||||
Iterator<AddressRange> rit, boolean forward) {
|
Iterator<AddressRange> rit, boolean forward) {
|
||||||
this.lit = PeekableIterators.castOrWrap(lit);
|
this.lit = PeekableIterators.castOrWrap(lit);
|
|
@ -21,17 +21,26 @@ import static ghidra.util.MathUtilities.cmin;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import generic.util.AbstractPeekableIterator;
|
||||||
|
import generic.util.MergeSortingIterator;
|
||||||
import generic.util.PeekableIterator;
|
import generic.util.PeekableIterator;
|
||||||
import ghidra.program.model.address.*;
|
import generic.util.PeekableIterators;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressRange;
|
||||||
|
import ghidra.program.model.address.AddressRangeImpl;
|
||||||
|
import ghidra.program.model.address.AddressRangeIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The iterator implementation backing several methods in {@link UnionAddressSetView}
|
||||||
|
*/
|
||||||
public class UnionAddressRangeIterator extends AbstractPeekableIterator<AddressRange>
|
public class UnionAddressRangeIterator extends AbstractPeekableIterator<AddressRange>
|
||||||
implements AddressRangeIterator {
|
implements AddressRangeIterator {
|
||||||
private final PeekableIterator<AddressRange> mit;
|
private final PeekableIterator<AddressRange> it;
|
||||||
private final boolean forward;
|
private final boolean forward;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Coalesce (by union) ranges from a single iterator
|
* Coalesce (by union) ranges from a single iterator
|
||||||
*
|
* <p>
|
||||||
* The ranges must be returned in order: in the forward direction, by increasing min address; in
|
* The ranges must be returned in order: in the forward direction, by increasing min address; in
|
||||||
* the reverse direction, by decreasing max address.
|
* the reverse direction, by decreasing max address.
|
||||||
*
|
*
|
||||||
|
@ -39,13 +48,13 @@ public class UnionAddressRangeIterator extends AbstractPeekableIterator<AddressR
|
||||||
* @param forward true to coalesce in the forward direction, false for reverse
|
* @param forward true to coalesce in the forward direction, false for reverse
|
||||||
*/
|
*/
|
||||||
public UnionAddressRangeIterator(Iterator<AddressRange> it, boolean forward) {
|
public UnionAddressRangeIterator(Iterator<AddressRange> it, boolean forward) {
|
||||||
this.mit = PeekableIterators.castOrWrap(it);
|
this.it = PeekableIterators.castOrWrap(it);
|
||||||
this.forward = forward;
|
this.forward = forward;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Union into a single range iterator, several range iterators
|
* Union into a single range iterator, several range iterators
|
||||||
*
|
* <p>
|
||||||
* The ranges will be coalesced so that each returned range is disconnected from any other. The
|
* The ranges will be coalesced so that each returned range is disconnected from any other. The
|
||||||
* ranges of each iterator must be returned in order by direction. While not recommended, the
|
* ranges of each iterator must be returned in order by direction. While not recommended, the
|
||||||
* ranges of each iterator may overlap, so long as they are sorted as in
|
* ranges of each iterator may overlap, so long as they are sorted as in
|
||||||
|
@ -56,7 +65,7 @@ public class UnionAddressRangeIterator extends AbstractPeekableIterator<AddressR
|
||||||
*/
|
*/
|
||||||
public UnionAddressRangeIterator(Collection<Iterator<AddressRange>> iterators,
|
public UnionAddressRangeIterator(Collection<Iterator<AddressRange>> iterators,
|
||||||
boolean forward) {
|
boolean forward) {
|
||||||
this.mit = new MergeSortingIterator<AddressRange>(iterators,
|
this.it = new MergeSortingIterator<AddressRange>(iterators,
|
||||||
forward ? AddressRangeComparators.FORWARD : AddressRangeComparators.BACKWARD);
|
forward ? AddressRangeComparators.FORWARD : AddressRangeComparators.BACKWARD);
|
||||||
this.forward = forward;
|
this.forward = forward;
|
||||||
}
|
}
|
||||||
|
@ -68,18 +77,18 @@ public class UnionAddressRangeIterator extends AbstractPeekableIterator<AddressR
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AddressRange seekNext() {
|
protected AddressRange seekNext() {
|
||||||
if (!mit.hasNext()) {
|
if (!it.hasNext()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
AddressRange peek = mit.peek();
|
AddressRange peek = it.peek();
|
||||||
Address min = peek.getMinAddress();
|
Address min = peek.getMinAddress();
|
||||||
Address max = peek.getMaxAddress();
|
Address max = peek.getMaxAddress();
|
||||||
while (true) {
|
while (true) {
|
||||||
mit.next();
|
it.next();
|
||||||
if (!mit.hasNext()) {
|
if (!it.hasNext()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
peek = mit.peek();
|
peek = it.peek();
|
||||||
if (peek.getAddressSpace() != min.getAddressSpace()) {
|
if (peek.getAddressSpace() != min.getAddressSpace()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
|
@ -21,15 +21,43 @@ import static ghidra.util.MathUtilities.cmin;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressRange;
|
||||||
|
import ghidra.program.model.address.AddressRangeIterator;
|
||||||
|
import ghidra.program.model.address.AddressSetView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lazily computed {@link AddressSetView} defined as the union of many given
|
||||||
|
* {@link AddressSetView}s.
|
||||||
|
* <p>
|
||||||
|
* This is equivalent to using {@link AddressSetView#union(AddressSetView)}, but does not
|
||||||
|
* materialize the difference. The choice of one over the other depends on the number of ranges in
|
||||||
|
* the inputs and the frequency of use of the result. With few ranges, or in cases where you need to
|
||||||
|
* access the entire result, anyway, just use the normal {@link AddressRange}. In cases with many,
|
||||||
|
* many ranges and where only a small part of the result needs to be computed, use this view. It may
|
||||||
|
* also be advantageous to use this if the inputs are themselves computed lazily.
|
||||||
|
* <p>
|
||||||
|
* This follows the conventions expected of an {@link AddressSetView} in that the returned ranges
|
||||||
|
* are disjoint. Thus, it will combine intersecting and abutting ranges from among the inputs. For
|
||||||
|
* example, the union of [[1,2]] and [[3,4]] is [[1,4]].
|
||||||
|
*/
|
||||||
public class UnionAddressSetView extends AbstractAddressSetView {
|
public class UnionAddressSetView extends AbstractAddressSetView {
|
||||||
private final Collection<AddressSetView> views;
|
private final Collection<AddressSetView> views;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the union of the given address set views
|
||||||
|
*
|
||||||
|
* @param views the input sets
|
||||||
|
*/
|
||||||
public UnionAddressSetView(AddressSetView... views) {
|
public UnionAddressSetView(AddressSetView... views) {
|
||||||
this(Arrays.asList(views));
|
this(Arrays.asList(views));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the union of the given address set views
|
||||||
|
*
|
||||||
|
* @param views the input sets
|
||||||
|
*/
|
||||||
public UnionAddressSetView(Collection<AddressSetView> views) {
|
public UnionAddressSetView(Collection<AddressSetView> views) {
|
||||||
this.views = views;
|
this.views = views;
|
||||||
}
|
}
|
|
@ -19,19 +19,16 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import generic.test.AbstractGTest;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
|
||||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
|
||||||
|
|
||||||
public class DifferenceAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTest {
|
public class DifferenceAddressSetViewTest extends AbstractGTest {
|
||||||
protected Language toy;
|
protected AddressSpace space = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 1);
|
||||||
|
|
||||||
protected Address addr(long offset) {
|
protected Address addr(long offset) {
|
||||||
return toy.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
return space.getAddress(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AddressRange rng(long min, long max) {
|
protected AddressRange rng(long min, long max) {
|
||||||
|
@ -54,12 +51,6 @@ public class DifferenceAddressSetViewTest extends AbstractGhidraHeadlessIntegrat
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUpIteratorTest() throws LanguageNotFoundException {
|
|
||||||
toy = DefaultLanguageService.getLanguageService().getLanguage(
|
|
||||||
new LanguageID("Toy:BE:64:default"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCounts() {
|
public void testCounts() {
|
||||||
AddressSetView difference;
|
AddressSetView difference;
|
|
@ -19,19 +19,16 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import generic.test.AbstractGTest;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
|
||||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
|
||||||
|
|
||||||
public class IntersectionAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTest {
|
public class IntersectionAddressSetViewTest extends AbstractGTest {
|
||||||
protected Language toy;
|
protected AddressSpace space = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 1);
|
||||||
|
|
||||||
protected Address addr(long offset) {
|
protected Address addr(long offset) {
|
||||||
return toy.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
return space.getAddress(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AddressRange rng(long min, long max) {
|
protected AddressRange rng(long min, long max) {
|
||||||
|
@ -54,12 +51,6 @@ public class IntersectionAddressSetViewTest extends AbstractGhidraHeadlessIntegr
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUpIteratorTest() throws LanguageNotFoundException {
|
|
||||||
toy = DefaultLanguageService.getLanguageService().getLanguage(
|
|
||||||
new LanguageID("Toy:BE:64:default"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCounts() {
|
public void testCounts() {
|
||||||
AddressSetView intersection;
|
AddressSetView intersection;
|
|
@ -22,6 +22,8 @@ import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import generic.util.MergeSortingIterator;
|
||||||
|
|
||||||
public class MergeSortingIteratorTest {
|
public class MergeSortingIteratorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyMap() {
|
public void testEmptyMap() {
|
|
@ -19,19 +19,16 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import generic.test.AbstractGTest;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
|
||||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
|
||||||
|
|
||||||
public class SymmetricDifferenceAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTest {
|
public class SymmetricDifferenceAddressSetViewTest extends AbstractGTest {
|
||||||
protected Language toy;
|
protected AddressSpace space = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 1);
|
||||||
|
|
||||||
protected Address addr(long offset) {
|
protected Address addr(long offset) {
|
||||||
return toy.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
return space.getAddress(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AddressRange rng(long min, long max) {
|
protected AddressRange rng(long min, long max) {
|
||||||
|
@ -54,12 +51,6 @@ public class SymmetricDifferenceAddressSetViewTest extends AbstractGhidraHeadles
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUpIteratorTest() throws LanguageNotFoundException {
|
|
||||||
toy = DefaultLanguageService.getLanguageService().getLanguage(
|
|
||||||
new LanguageID("Toy:BE:64:default"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCounts() {
|
public void testCounts() {
|
||||||
AddressSetView xor;
|
AddressSetView xor;
|
|
@ -22,17 +22,15 @@ import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import generic.test.AbstractGTest;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
|
||||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
|
||||||
import ghidra.util.TwoWayBreakdownAddressRangeIterator.Which;
|
import ghidra.util.TwoWayBreakdownAddressRangeIterator.Which;
|
||||||
|
|
||||||
public class TwoWayBreakdownAddressRangeIteratorTest extends AbstractGhidraHeadlessIntegrationTest {
|
public class TwoWayBreakdownAddressRangeIteratorTest extends AbstractGTest {
|
||||||
protected Language toy;
|
protected AddressSpace rom = new GenericAddressSpace("rom", 64, AddressSpace.TYPE_RAM, 1);
|
||||||
|
protected AddressSpace ram = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 2);
|
||||||
|
|
||||||
protected TwoWayBreakdownAddressRangeIterator makeIterator(AddressSet a, AddressSet b,
|
protected TwoWayBreakdownAddressRangeIterator makeIterator(AddressSet a, AddressSet b,
|
||||||
boolean forward) {
|
boolean forward) {
|
||||||
|
@ -41,11 +39,11 @@ public class TwoWayBreakdownAddressRangeIteratorTest extends AbstractGhidraHeadl
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Address addr(long offset) {
|
protected Address addr(long offset) {
|
||||||
return toy.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
return rom.getAddress(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Address dAddr(long offset) {
|
protected Address dAddr(long offset) {
|
||||||
return toy.getAddressFactory().getAddressSpace("data").getAddress(offset);
|
return ram.getAddress(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AddressRange rng(long min, long max) {
|
protected AddressRange rng(long min, long max) {
|
||||||
|
@ -87,12 +85,6 @@ public class TwoWayBreakdownAddressRangeIteratorTest extends AbstractGhidraHeadl
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUpIteratorTest() throws LanguageNotFoundException {
|
|
||||||
toy = DefaultLanguageService.getLanguageService()
|
|
||||||
.getLanguage(new LanguageID("Toy:BE:64:harvard"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBothEmpty() {
|
public void testBothEmpty() {
|
||||||
AddressSet a = new AddressSet();
|
AddressSet a = new AddressSet();
|
|
@ -19,23 +19,21 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import generic.test.AbstractGTest;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
|
||||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
|
||||||
|
|
||||||
public class UnionAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTest {
|
public class UnionAddressSetViewTest extends AbstractGTest {
|
||||||
protected Language toy;
|
protected AddressSpace rom = new GenericAddressSpace("rom", 64, AddressSpace.TYPE_RAM, 1);
|
||||||
|
protected AddressSpace ram = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 2);
|
||||||
|
|
||||||
protected Address addr(long offset) {
|
protected Address addr(long offset) {
|
||||||
return toy.getDefaultSpace().getAddress(offset);
|
return rom.getAddress(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Address daddr(long offset) {
|
protected Address dAddr(long offset) {
|
||||||
return toy.getDefaultDataSpace().getAddress(offset);
|
return ram.getAddress(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AddressRange rng(long min, long max) {
|
protected AddressRange rng(long min, long max) {
|
||||||
|
@ -43,7 +41,7 @@ public class UnionAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTe
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AddressRange drng(long min, long max) {
|
protected AddressRange drng(long min, long max) {
|
||||||
return new AddressRangeImpl(daddr(min), daddr(max));
|
return new AddressRangeImpl(dAddr(min), dAddr(max));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AddressSet set(AddressRange... ranges) {
|
protected AddressSet set(AddressRange... ranges) {
|
||||||
|
@ -62,12 +60,6 @@ public class UnionAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTe
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUpIteratorTest() throws LanguageNotFoundException {
|
|
||||||
toy = DefaultLanguageService.getLanguageService()
|
|
||||||
.getLanguage(new LanguageID("Toy:BE:64:harvard"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCounts() {
|
public void testCounts() {
|
||||||
AddressSetView union;
|
AddressSetView union;
|
Loading…
Add table
Add a link
Reference in a new issue