mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
GP-5864: Add EmulatorUtilities
This commit is contained in:
parent
39c6a6db59
commit
d577925d75
43 changed files with 990 additions and 273 deletions
|
@ -0,0 +1,214 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (!rev.hasNext()) {
|
||||
return start;
|
||||
}
|
||||
AddressRange rng = rev.next();
|
||||
if (!rng.contains(start)) {
|
||||
return start;
|
||||
}
|
||||
return forward ? rng.getMinAddress() : rng.getMaxAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return !iterator().hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address start, Address end) {
|
||||
AddressRangeIterator dit = AddressRangeIterators.subtract(
|
||||
new AddressSet(start, end).iterator(), iterator(start, true), null, true);
|
||||
return !dit.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(AddressSetView rangeSet) {
|
||||
AddressRangeIterator dit = AddressRangeIterators.subtract(rangeSet.iterator(),
|
||||
iterator(rangeSet.getMinAddress(), true), null, true);
|
||||
return !dit.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMinAddress() {
|
||||
AddressRangeIterator it = getAddressRanges(true);
|
||||
return it.hasNext() ? it.next().getMinAddress() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMaxAddress() {
|
||||
AddressRangeIterator it = getAddressRanges(false);
|
||||
return it.hasNext() ? it.next().getMaxAddress() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumAddressRanges() {
|
||||
int count = 0;
|
||||
for (@SuppressWarnings("unused")
|
||||
AddressRange r : this) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator() {
|
||||
return getAddressRanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator(boolean forward) {
|
||||
return getAddressRanges(forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator(Address start, boolean forward) {
|
||||
return getAddressRanges(start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNumAddresses() {
|
||||
long count = 0;
|
||||
for (AddressRange r : this) {
|
||||
count += r.getLength();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressIterator getAddresses(boolean forward) {
|
||||
return new AddressIteratorAdapter(iterator(forward), forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressIterator getAddresses(Address start, boolean forward) {
|
||||
return new AddressIteratorAdapter(iterator(start, forward), start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSameAddresses(AddressSetView view) {
|
||||
AddressRangeIterator ait = this.getAddressRanges();
|
||||
AddressRangeIterator bit = view.getAddressRanges();
|
||||
while (ait.hasNext() && bit.hasNext()) {
|
||||
AddressRange ar = ait.next();
|
||||
AddressRange br = bit.next();
|
||||
if (!ar.equals(br)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (ait.hasNext() || bit.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getFirstRange() {
|
||||
Iterator<AddressRange> it = iterator(true);
|
||||
return it.hasNext() ? it.next() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getLastRange() {
|
||||
Iterator<AddressRange> it = iterator(false);
|
||||
return it.hasNext() ? it.next() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(AddressSetView addrSet) {
|
||||
AddressRangeIterator iit =
|
||||
AddressRangeIterators.intersect(this.iterator(addrSet.getMinAddress(), true),
|
||||
addrSet.iterator(this.getMinAddress(), true), true);
|
||||
return iit.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Address start, Address end) {
|
||||
AddressRangeIterator iit = AddressRangeIterators.intersect(this.iterator(start, true),
|
||||
List.of((AddressRange) new AddressRangeImpl(start, end)).iterator(), true);
|
||||
return iit.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet intersect(AddressSetView view) {
|
||||
return new AddressSet(new IntersectionAddressSetView(this, view));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet intersectRange(Address start, Address end) {
|
||||
return intersect(new AddressSet(start, end));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet union(AddressSetView addrSet) {
|
||||
return new AddressSet(new UnionAddressSetView(this, addrSet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet subtract(AddressSetView addrSet) {
|
||||
return new AddressSet(new DifferenceAddressSetView(this, addrSet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet xor(AddressSetView addrSet) {
|
||||
return new AddressSet(new SymmetricDifferenceAddressSetView(this, addrSet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address findFirstAddressInCommon(AddressSetView set) {
|
||||
AddressRangeIterator iit =
|
||||
AddressRangeIterators.intersect(iterator(), set.iterator(), true);
|
||||
return iit.hasNext() ? iit.next().getMinAddress() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRangeContaining(Address address) {
|
||||
AddressRangeIterator it = getAddressRanges(address, true);
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
AddressRange rng = it.next();
|
||||
if (!rng.contains(address)) {
|
||||
return null;
|
||||
}
|
||||
return rng;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import generic.util.FlattenedIterator;
|
||||
import generic.util.PeekableIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
/**
|
||||
* Convert an {@link AddressRange} iterator to an {@link AddressIterator}.
|
||||
*/
|
||||
public class AddressIteratorAdapter extends FlattenedIterator<AddressRange, Address>
|
||||
implements AddressIterator {
|
||||
|
||||
protected static class ForwardAddressIterator implements PeekableIterator<Address> {
|
||||
private final Address end;
|
||||
|
||||
private Address cur;
|
||||
|
||||
public ForwardAddressIterator(AddressRange range) {
|
||||
this(range.getMinAddress(), range.getMaxAddress());
|
||||
}
|
||||
|
||||
public ForwardAddressIterator(Address min, Address max) {
|
||||
this.end = max;
|
||||
this.cur = min;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return cur != null && cur.compareTo(end) <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address next() {
|
||||
Address next = cur;
|
||||
cur = cur.next();
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address peek() throws NoSuchElementException {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class BackwardAddressIterator implements PeekableIterator<Address> {
|
||||
private final Address end;
|
||||
|
||||
private Address cur;
|
||||
|
||||
public BackwardAddressIterator(AddressRange range) {
|
||||
this(range.getMinAddress(), range.getMaxAddress());
|
||||
}
|
||||
|
||||
public BackwardAddressIterator(Address min, Address max) {
|
||||
this.end = min;
|
||||
this.cur = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return cur != null && cur.compareTo(end) >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address next() {
|
||||
Address next = cur;
|
||||
cur = cur.previous();
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address peek() throws NoSuchElementException {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return () -> forward ? new ForwardAddressIterator(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) {
|
||||
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) {
|
||||
super(outer, forward ? ar -> {
|
||||
if (!ar.contains(start)) {
|
||||
return new ForwardAddressIterator(ar);
|
||||
}
|
||||
return new ForwardAddressIterator(start, ar.getMaxAddress());
|
||||
} : ar -> {
|
||||
if (!ar.contains(start)) {
|
||||
return new BackwardAddressIterator(ar);
|
||||
}
|
||||
return new BackwardAddressIterator(ar.getMinAddress(), start);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Address> iterator() {
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
|
||||
/**
|
||||
* Comparators used for sorting address ranges
|
||||
*/
|
||||
public enum AddressRangeComparators implements Comparator<AddressRange> {
|
||||
/**
|
||||
* Compare ranges by their minimum address and order them smallest first.
|
||||
*/
|
||||
FORWARD {
|
||||
@Override
|
||||
public int compare(AddressRange a, AddressRange b) {
|
||||
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 {
|
||||
@Override
|
||||
public int compare(AddressRange a, AddressRange b) {
|
||||
return b.getMaxAddress().compareTo(a.getMaxAddress());
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
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 {
|
||||
;
|
||||
|
||||
private static class WrappingAddressRangeIterator implements AddressRangeIterator {
|
||||
private final Iterator<AddressRange> it;
|
||||
|
||||
public WrappingAddressRangeIterator(Iterator<AddressRange> it) {
|
||||
this.it = it;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange next() {
|
||||
return it.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (it instanceof AddressRangeIterator ari) {
|
||||
return ari;
|
||||
}
|
||||
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,
|
||||
boolean forward) {
|
||||
return new UnionAddressRangeIterator(iterators, forward);
|
||||
}
|
||||
|
||||
protected static boolean doCheckStart(AddressRange range, Address start, boolean forward) {
|
||||
if (start == null) {
|
||||
return true;
|
||||
}
|
||||
return forward ? range.getMaxAddress().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,
|
||||
Address start, boolean forward) {
|
||||
return new WrappingAddressRangeIterator(IteratorUtils.transformedIterator(
|
||||
IteratorUtils.filteredIterator(new TwoWayBreakdownAddressRangeIterator(a, b, forward),
|
||||
e -> doCheckStart(e.getKey(), start, forward) && e.getValue().inSubtract()),
|
||||
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,
|
||||
Address start, boolean forward) {
|
||||
Iterator<Entry<AddressRange, Which>> eit =
|
||||
new TwoWayBreakdownAddressRangeIterator(a, b, forward);
|
||||
Iterator<Entry<AddressRange, Which>> fit =
|
||||
IteratorUtils.filteredIterator(eit, e -> e.getValue().inXor());
|
||||
Iterator<AddressRange> rit = IteratorUtils.transformedIterator(fit, e -> e.getKey());
|
||||
// Use union to coalesce just-connected ranges in opposite iterators.
|
||||
Iterator<AddressRange> uit = new UnionAddressRangeIterator(rit, forward);
|
||||
// Have to do this filter after union, otherwise parts of ranges are omitted.
|
||||
Iterator<AddressRange> result =
|
||||
IteratorUtils.filteredIterator(uit, r -> doCheckStart(r, start, forward));
|
||||
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,
|
||||
boolean forward) {
|
||||
return new WrappingAddressRangeIterator(IteratorUtils.transformedIterator(
|
||||
IteratorUtils.filteredIterator(new TwoWayBreakdownAddressRangeIterator(a, b, forward),
|
||||
e -> e.getValue().inIntersect()),
|
||||
e -> e.getKey()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
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 {
|
||||
private final AddressSetView a;
|
||||
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) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address addr) {
|
||||
return a.contains(addr) && !b.contains(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address start, Address end) {
|
||||
return a.contains(start, end) && !b.intersects(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(AddressSetView rangeSet) {
|
||||
return a.contains(rangeSet) && !b.intersects(rangeSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges() {
|
||||
return AddressRangeIterators.subtract(a.iterator(), b.iterator(), null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(boolean forward) {
|
||||
return AddressRangeIterators.subtract(a.iterator(forward), b.iterator(forward), null,
|
||||
forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(Address start, boolean forward) {
|
||||
// A range preceding the start in the second set may affect the first range
|
||||
Iterator<AddressRange> rev = b.iterator(start, !forward);
|
||||
Address bStart;
|
||||
if (rev.hasNext()) {
|
||||
bStart = forward ? rev.next().getMinAddress() : rev.next().getMaxAddress();
|
||||
}
|
||||
else {
|
||||
bStart = start;
|
||||
}
|
||||
return AddressRangeIterators.subtract(a.iterator(start, forward),
|
||||
b.iterator(bStart, forward), start, forward);
|
||||
}
|
||||
|
||||
static AddressRange truncate(AddressRange rng, Address address, AddressSetView v) {
|
||||
AddressRangeIterator prevIt = v.getAddressRanges(address, false);
|
||||
AddressRange prev = prevIt.hasNext() ? prevIt.next() : null;
|
||||
AddressRangeIterator nextIt = v.getAddressRanges(address, true);
|
||||
AddressRange next = nextIt.hasNext() ? nextIt.next() : null;
|
||||
|
||||
boolean truncPrev = prev != null && prev.intersects(rng);
|
||||
boolean truncNext = next != null && next.intersects(rng);
|
||||
if (!truncPrev && !truncNext) {
|
||||
return rng;
|
||||
}
|
||||
Address min = truncPrev ? prev.getMaxAddress().next() : rng.getMinAddress();
|
||||
Address max = truncNext ? next.getMinAddress().previous() : rng.getMaxAddress();
|
||||
return new AddressRangeImpl(min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRangeContaining(Address address) {
|
||||
AddressRange rng = a.getRangeContaining(address);
|
||||
if (rng == null) {
|
||||
return null;
|
||||
}
|
||||
AddressRange sub = b.getRangeContaining(address);
|
||||
if (sub != null) {
|
||||
return null;
|
||||
}
|
||||
return truncate(rng, address, b);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static ghidra.util.MathUtilities.cmax;
|
||||
import static ghidra.util.MathUtilities.cmin;
|
||||
|
||||
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 {
|
||||
private final AddressSetView a;
|
||||
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) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address addr) {
|
||||
return a.contains(addr) && b.contains(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address start, Address end) {
|
||||
return a.contains(start, end) && b.contains(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(AddressSetView rangeSet) {
|
||||
return a.contains(rangeSet) && b.contains(rangeSet);
|
||||
}
|
||||
|
||||
protected Address findStart(boolean forward) {
|
||||
Address aStart;
|
||||
Address bStart;
|
||||
if (forward) {
|
||||
if ((aStart = a.getMinAddress()) == null || (bStart = b.getMinAddress()) == null) {
|
||||
return null;
|
||||
}
|
||||
return cmax(aStart, bStart);
|
||||
}
|
||||
if ((aStart = a.getMaxAddress()) == null || (bStart = b.getMaxAddress()) == null) {
|
||||
return null;
|
||||
}
|
||||
return cmin(aStart, bStart);
|
||||
}
|
||||
|
||||
protected Address adjustStart(Address start, boolean forward) {
|
||||
Address aStart;
|
||||
Address bStart;
|
||||
AddressRangeIterator it;
|
||||
if (forward) {
|
||||
it = a.getAddressRanges(start, forward);
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
aStart = it.next().getMinAddress();
|
||||
it = b.getAddressRanges(start, forward);
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
bStart = it.next().getMinAddress();
|
||||
return cmax(aStart, bStart);
|
||||
}
|
||||
it = a.getAddressRanges(start, forward);
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
aStart = it.next().getMaxAddress();
|
||||
it = b.getAddressRanges(start, forward);
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
bStart = it.next().getMaxAddress();
|
||||
return cmin(aStart, bStart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges() {
|
||||
return getAddressRanges(true);
|
||||
}
|
||||
|
||||
protected AddressRangeIterator doGetRanges(Address start, boolean forward) {
|
||||
if (start == null) {
|
||||
return new EmptyAddressRangeIterator();
|
||||
}
|
||||
return AddressRangeIterators.intersect(
|
||||
a.iterator(start, forward),
|
||||
b.iterator(start, forward), forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(boolean forward) {
|
||||
return doGetRanges(findStart(forward), forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(Address start, boolean forward) {
|
||||
return doGetRanges(adjustStart(start, forward), forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRangeContaining(Address address) {
|
||||
AddressRange ar = a.getRangeContaining(address);
|
||||
if (ar == null) {
|
||||
return null;
|
||||
}
|
||||
AddressRange br = b.getRangeContaining(address);
|
||||
if (br == null) {
|
||||
return null;
|
||||
}
|
||||
return ar.intersect(br);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
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 {
|
||||
private final AddressSetView a;
|
||||
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) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address addr) {
|
||||
return a.contains(addr) ^ b.contains(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges() {
|
||||
return AddressRangeIterators.xor(a.iterator(), b.iterator(), null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(boolean forward) {
|
||||
return AddressRangeIterators.xor(a.iterator(forward), b.iterator(forward), null, forward);
|
||||
}
|
||||
|
||||
protected static Address fixStart(AddressRangeIterator rev, boolean forward) {
|
||||
if (!rev.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
AddressRange rng = rev.next();
|
||||
return forward ? rng.getMinAddress() : rng.getMaxAddress();
|
||||
}
|
||||
|
||||
protected static Address rewindIfBounding(AddressRangeIterator rev, Address start,
|
||||
boolean forward) {
|
||||
if (!rev.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
AddressRange rng = rev.next();
|
||||
if (forward) {
|
||||
if (rng.getMaxAddress().isSuccessor(start)) {
|
||||
return rng.getMinAddress();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (start.isSuccessor(rng.getMinAddress())) {
|
||||
return rng.getMaxAddress();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(Address start, boolean forward) {
|
||||
// Need to coalesce in reverse to initialize (XOR disjoint,connected will coalesce)
|
||||
AddressRangeIterator rev = AddressRangeIterators.xor(a.iterator(start, !forward),
|
||||
b.iterator(start, !forward), start, !forward);
|
||||
Address fixedStart = fixStart(rev, start, forward);
|
||||
// Also, something farther back than start may affect first range
|
||||
// Reverse each independently to find such a range
|
||||
Address fixA =
|
||||
rewindIfBounding(a.getAddressRanges(fixedStart, !forward), fixedStart, forward);
|
||||
if (fixA != null) {
|
||||
fixedStart = fixA;
|
||||
}
|
||||
else {
|
||||
Address fixB =
|
||||
rewindIfBounding(b.getAddressRanges(fixedStart, !forward), fixedStart, forward);
|
||||
if (fixB != null) {
|
||||
fixedStart = fixB;
|
||||
}
|
||||
}
|
||||
|
||||
return AddressRangeIterators.xor(a.iterator(fixedStart, forward),
|
||||
b.iterator(fixedStart, forward), start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRangeContaining(Address address) {
|
||||
AddressRange ar = a.getRangeContaining(address);
|
||||
AddressRange br = b.getRangeContaining(address);
|
||||
if ((ar != null) == (br != null)) {
|
||||
return null;
|
||||
}
|
||||
AddressRange rng = ar != null ? ar : br;
|
||||
AddressSetView v = ar != null ? b : a;
|
||||
return DifferenceAddressSetView.truncate(rng, address, v);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,301 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static ghidra.util.MathUtilities.cmax;
|
||||
import static ghidra.util.MathUtilities.cmin;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import generic.util.*;
|
||||
import ghidra.program.model.address.*;
|
||||
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
|
||||
extends AbstractPeekableIterator<Entry<AddressRange, Which>> {
|
||||
|
||||
/**
|
||||
* Indicates which of the input iterators contain a range
|
||||
*/
|
||||
public enum Which {
|
||||
/** 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) {
|
||||
this.includesLeft = includesLeft;
|
||||
this.includesRight = includesRight;
|
||||
}
|
||||
|
||||
/** Indicates the the left iterator includes this range */
|
||||
public final boolean includesLeft;
|
||||
/** Indicates that the right iterator includes this range */
|
||||
public final boolean includesRight;
|
||||
|
||||
/**
|
||||
* Check if this range is included in the difference: {@code left - right}
|
||||
*
|
||||
* @return true if included
|
||||
*/
|
||||
public boolean inSubtract() {
|
||||
return this == LEFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this range is included in the symmetric difference: {@code left Δ right}
|
||||
*
|
||||
* @return true if included
|
||||
*/
|
||||
public boolean inXor() {
|
||||
return this == LEFT || this == RIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this range is included in the intersection: {@code left ∩ right}
|
||||
*
|
||||
* @return true if included
|
||||
*/
|
||||
public boolean inIntersect() {
|
||||
return this == BOTH;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A mutable map entry
|
||||
*/
|
||||
public static class MyEntry implements Entry<AddressRange, Which> {
|
||||
private AddressRange key;
|
||||
private Which val;
|
||||
|
||||
@Override
|
||||
public AddressRange getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Which getValue() {
|
||||
return val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Which setValue(Which value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private final PeekableIterator<AddressRange> lit;
|
||||
private final PeekableIterator<AddressRange> rit;
|
||||
private final boolean forward;
|
||||
|
||||
private AddressSpace curSpace = null;
|
||||
private Address cur = null; // max/min address of next range expected
|
||||
|
||||
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,
|
||||
Iterator<AddressRange> rit, boolean forward) {
|
||||
this.lit = PeekableIterators.castOrWrap(lit);
|
||||
this.rit = PeekableIterators.castOrWrap(rit);
|
||||
this.forward = forward;
|
||||
|
||||
initCur();
|
||||
}
|
||||
|
||||
private void initCur() {
|
||||
if (lit.hasNext()) {
|
||||
cur = getStart(this.lit.peek());
|
||||
}
|
||||
if (rit.hasNext()) {
|
||||
Address a = getStart(rit.peek());
|
||||
cur = cur == null ? a : first(cur, a);
|
||||
}
|
||||
curSpace = cur == null ? null : cur.getAddressSpace();
|
||||
}
|
||||
|
||||
private Address getBefore(AddressRange r, AddressSpace beforeSpace) {
|
||||
if (forward) {
|
||||
Address prev = r.getMinAddress().previous();
|
||||
if (prev != null) {
|
||||
return prev;
|
||||
}
|
||||
return beforeSpace.getMaxAddress();
|
||||
}
|
||||
Address next = r.getMaxAddress().next();
|
||||
if (next != null) {
|
||||
return next;
|
||||
}
|
||||
return beforeSpace.getMinAddress();
|
||||
}
|
||||
|
||||
private Address getStart(AddressRange r) {
|
||||
return forward ? r.getMinAddress() : r.getMaxAddress();
|
||||
}
|
||||
|
||||
private Address getEnd(AddressRange r) {
|
||||
return forward ? r.getMaxAddress() : r.getMinAddress();
|
||||
}
|
||||
|
||||
private Address getAfter(AddressRange r) {
|
||||
return forward ? r.getMaxAddress().next() : r.getMinAddress().previous();
|
||||
}
|
||||
|
||||
private Address first(Address a, Address b) {
|
||||
return forward ? cmin(a, b) : cmax(a, b);
|
||||
}
|
||||
|
||||
private Address last(Address a, Address b) {
|
||||
return forward ? cmax(a, b) : cmin(a, b);
|
||||
}
|
||||
|
||||
private int cmp(Address a, Address b) {
|
||||
return forward ? a.compareTo(b) : b.compareTo(a);
|
||||
}
|
||||
|
||||
private AddressRange truncateRange(Address beg, Address end) {
|
||||
return forward ? new AddressRangeImpl(cmax(cur, beg), end)
|
||||
: new AddressRangeImpl(end, cmin(cur, beg));
|
||||
}
|
||||
|
||||
private AddressRange truncateRange(AddressRange rng) {
|
||||
if (!rng.contains(cur)) {
|
||||
return rng;
|
||||
}
|
||||
return forward ? truncateRange(rng.getMinAddress(), rng.getMaxAddress())
|
||||
: truncateRange(rng.getMaxAddress(), rng.getMinAddress());
|
||||
}
|
||||
|
||||
private void findSuitable(PeekableIterator<AddressRange> it) {
|
||||
while (it.hasNext() && cmp(getEnd(it.peek()), cur) < 0) {
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
|
||||
private void advanceSpace(PeekableIterator<AddressRange> it) {
|
||||
while (it.hasNext() && it.peek().getAddressSpace() == curSpace) {
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
|
||||
private void advanceSpace() {
|
||||
advanceSpace(lit);
|
||||
advanceSpace(rit);
|
||||
}
|
||||
|
||||
private void advance() {
|
||||
cur = getAfter(entry.key);
|
||||
if (cur == null) { // ended at an extreme
|
||||
advanceSpace();
|
||||
initCur();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Entry<AddressRange, Which> seekNext() {
|
||||
if (cur == null) {
|
||||
return null;
|
||||
}
|
||||
findSuitable(lit);
|
||||
findSuitable(rit);
|
||||
|
||||
boolean ln = lit.hasNext();
|
||||
boolean rn = rit.hasNext();
|
||||
if (!ln && !rn) {
|
||||
return null;
|
||||
}
|
||||
if (ln && !rn) {
|
||||
entry.key = truncateRange(lit.next());
|
||||
entry.val = Which.LEFT;
|
||||
advance();
|
||||
return entry;
|
||||
}
|
||||
if (!ln && rn) {
|
||||
entry.key = truncateRange(rit.next());
|
||||
entry.val = Which.RIGHT;
|
||||
advance();
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Advance past empty space
|
||||
Address adv = first(getStart(lit.peek()), getStart(rit.peek()));
|
||||
cur = cur == null ? adv : last(cur, adv);
|
||||
|
||||
boolean lc = cmp(getStart(lit.peek()), cur) <= 0;
|
||||
boolean rc = cmp(getStart(rit.peek()), cur) <= 0;
|
||||
if (lc && rc) {
|
||||
Address beg = last(getStart(lit.peek()), getStart(rit.peek()));
|
||||
Address end = first(getEnd(lit.peek()), getEnd(rit.peek()));
|
||||
entry.key = truncateRange(beg, end);
|
||||
entry.val = Which.BOTH;
|
||||
advance();
|
||||
return entry;
|
||||
}
|
||||
if (lc && !rc) {
|
||||
Address beg = getStart(lit.peek());
|
||||
Address end = first(getEnd(lit.peek()), getBefore(rit.peek(), beg.getAddressSpace()));
|
||||
entry.key = truncateRange(beg, end);
|
||||
entry.val = Which.LEFT;
|
||||
advance();
|
||||
return entry;
|
||||
}
|
||||
if (!lc && rc) {
|
||||
Address beg = getStart(rit.peek());
|
||||
Address end = first(getEnd(rit.peek()), getBefore(lit.peek(), beg.getAddressSpace()));
|
||||
entry.key = truncateRange(beg, end);
|
||||
entry.val = Which.RIGHT;
|
||||
advance();
|
||||
return entry;
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static ghidra.util.MathUtilities.cmax;
|
||||
import static ghidra.util.MathUtilities.cmin;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import generic.util.AbstractPeekableIterator;
|
||||
import generic.util.MergeSortingIterator;
|
||||
import generic.util.PeekableIterator;
|
||||
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>
|
||||
implements AddressRangeIterator {
|
||||
private final PeekableIterator<AddressRange> it;
|
||||
private final boolean forward;
|
||||
|
||||
/**
|
||||
* 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 reverse direction, by decreasing max address.
|
||||
*
|
||||
* @param it the iterator
|
||||
* @param forward true to coalesce in the forward direction, false for reverse
|
||||
*/
|
||||
public UnionAddressRangeIterator(Iterator<AddressRange> it, boolean forward) {
|
||||
this.it = PeekableIterators.castOrWrap(it);
|
||||
this.forward = forward;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* {@link #UnionAddressRangeIterator(Iterator, boolean)}
|
||||
*
|
||||
* @param iterators the iterators to union
|
||||
* @param forward true to union in the forward direction, false for reverse
|
||||
*/
|
||||
public UnionAddressRangeIterator(Collection<Iterator<AddressRange>> iterators,
|
||||
boolean forward) {
|
||||
this.it = new MergeSortingIterator<AddressRange>(iterators,
|
||||
forward ? AddressRangeComparators.FORWARD : AddressRangeComparators.BACKWARD);
|
||||
this.forward = forward;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressRange seekNext() {
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
AddressRange peek = it.peek();
|
||||
Address min = peek.getMinAddress();
|
||||
Address max = peek.getMaxAddress();
|
||||
while (true) {
|
||||
it.next();
|
||||
if (!it.hasNext()) {
|
||||
break;
|
||||
}
|
||||
peek = it.peek();
|
||||
if (peek.getAddressSpace() != min.getAddressSpace()) {
|
||||
break;
|
||||
}
|
||||
if (forward) {
|
||||
Address n = max.next();
|
||||
if (n != null && peek.getMinAddress().compareTo(n) > 0) {
|
||||
break;
|
||||
}
|
||||
max = cmax(max, peek.getMaxAddress());
|
||||
}
|
||||
else {
|
||||
Address p = min.previous();
|
||||
if (p != null && peek.getMaxAddress().compareTo(p) < 0) {
|
||||
break;
|
||||
}
|
||||
min = cmin(min, peek.getMinAddress());
|
||||
}
|
||||
}
|
||||
return new AddressRangeImpl(min, max);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static ghidra.util.MathUtilities.cmax;
|
||||
import static ghidra.util.MathUtilities.cmin;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
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 {
|
||||
private final Collection<AddressSetView> views;
|
||||
|
||||
/**
|
||||
* Construct the union of the given address set views
|
||||
*
|
||||
* @param views the input sets
|
||||
*/
|
||||
public UnionAddressSetView(AddressSetView... views) {
|
||||
this(Arrays.asList(views));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the union of the given address set views
|
||||
*
|
||||
* @param views the input sets
|
||||
*/
|
||||
public UnionAddressSetView(Collection<AddressSetView> views) {
|
||||
this.views = views;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address addr) {
|
||||
for (AddressSetView v : views) {
|
||||
if (v.contains(addr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
for (AddressSetView v : views) {
|
||||
if (!v.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMinAddress() {
|
||||
Address result = null;
|
||||
for (AddressSetView v : views) {
|
||||
Address candMin = v.getMinAddress();
|
||||
if (candMin == null) {
|
||||
continue;
|
||||
}
|
||||
if (result == null) {
|
||||
result = candMin;
|
||||
continue;
|
||||
}
|
||||
result = cmin(result, candMin);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMaxAddress() {
|
||||
Address result = null;
|
||||
for (AddressSetView v : views) {
|
||||
Address candMin = v.getMaxAddress();
|
||||
if (candMin == null) {
|
||||
continue;
|
||||
}
|
||||
if (result == null) {
|
||||
result = candMin;
|
||||
continue;
|
||||
}
|
||||
result = cmax(result, candMin);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges() {
|
||||
return AddressRangeIterators.union(views.stream().map(v -> v.iterator()).toList(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(boolean forward) {
|
||||
return AddressRangeIterators.union(views.stream().map(v -> v.iterator(forward)).toList(),
|
||||
forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(Address start, boolean forward) {
|
||||
// Need to coalesce in reverse to initialize
|
||||
AddressRangeIterator rev = AddressRangeIterators.union(
|
||||
views.stream().map(v -> v.iterator(start, !forward)).toList(), !forward);
|
||||
Address fixedStart = fixStart(rev, start, forward);
|
||||
return AddressRangeIterators
|
||||
.union(views.stream().map(v -> v.iterator(fixedStart, forward)).toList(), forward);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class DifferenceAddressSetViewTest extends AbstractGTest {
|
||||
protected AddressSpace space = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 1);
|
||||
|
||||
protected Address addr(long offset) {
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
protected AddressRange rng(long min, long max) {
|
||||
return new AddressRangeImpl(addr(min), addr(max));
|
||||
}
|
||||
|
||||
protected AddressSet set(AddressRange... ranges) {
|
||||
AddressSet set = new AddressSet();
|
||||
for (AddressRange rng : ranges) {
|
||||
set.add(rng);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
protected List<Address> addrs(long... offsets) {
|
||||
List<Address> result = new ArrayList<>(offsets.length);
|
||||
for (long off : offsets) {
|
||||
result.add(addr(off));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCounts() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new DifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertTrue(difference.isEmpty());
|
||||
assertEquals(0, difference.getNumAddresses());
|
||||
assertEquals(0, difference.getNumAddressRanges());
|
||||
|
||||
// Disjoint, connected
|
||||
difference =
|
||||
new DifferenceAddressSetView(set(rng(0x0000, 0x0fff)), set(rng(0x1000, 0x1fff)));
|
||||
assertFalse(difference.isEmpty());
|
||||
assertEquals(0x1000, difference.getNumAddresses());
|
||||
assertEquals(1, difference.getNumAddressRanges());
|
||||
|
||||
// Subtract from middle
|
||||
difference =
|
||||
new DifferenceAddressSetView(set(rng(0x0000, 0x2fff)), set(rng(0x1000, 0x1fff)));
|
||||
assertFalse(difference.isEmpty());
|
||||
assertEquals(0x2000, difference.getNumAddresses());
|
||||
assertEquals(2, difference.getNumAddressRanges());
|
||||
|
||||
// Subtract everything
|
||||
difference =
|
||||
new DifferenceAddressSetView(set(rng(0x1000, 0x1fff)), set(rng(0x0000, 0x2fff)));
|
||||
assertTrue(difference.isEmpty());
|
||||
assertEquals(0, difference.getNumAddresses());
|
||||
assertEquals(0, difference.getNumAddressRanges());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContains() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new DifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertFalse(difference.contains(addr(0x0800), addr(0x1800)));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new DifferenceAddressSetView(a, b);
|
||||
assertTrue(difference.contains(addr(0x0800)));
|
||||
assertFalse(difference.contains(addr(0x1800)));
|
||||
assertTrue(difference.contains(addr(0x2800)));
|
||||
assertFalse(difference.contains(addr(0x3000)));
|
||||
assertTrue(difference.contains(addr(0x0800), addr(0x0fff)));
|
||||
assertFalse(difference.contains(addr(0x0800), addr(0x1000)));
|
||||
assertTrue(difference.contains(addr(0x2000), addr(0x2fff)));
|
||||
assertFalse(difference.contains(addr(0x1fff), addr(0x2fff)));
|
||||
assertFalse(difference.contains(addr(0x2000), addr(0x3000)));
|
||||
assertTrue(difference.contains(set(rng(0x0400, 0x0c00), rng(0x2400, 0x2c00))));
|
||||
assertFalse(difference.contains(set(rng(0x0400, 0x0c00), rng(0x1c00, 0x2fff))));
|
||||
assertFalse(difference.contains(set(rng(0x0400, 0x0c00), rng(0x2400, 0x3c00))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndpoints() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new DifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(difference.getMinAddress());
|
||||
assertNull(difference.getMaxAddress());
|
||||
|
||||
difference =
|
||||
new DifferenceAddressSetView(set(rng(0x0000, 0x2fff)), set(rng(0x1000, 0x1fff)));
|
||||
assertEquals(addr(0x0000), difference.getMinAddress());
|
||||
assertEquals(addr(0x2fff), difference.getMaxAddress());
|
||||
|
||||
difference =
|
||||
new DifferenceAddressSetView(set(rng(0x0000, 0x1fff)), set(rng(0x1000, 0x2fff)));
|
||||
assertEquals(addr(0x0000), difference.getMinAddress());
|
||||
assertEquals(addr(0x0fff), difference.getMaxAddress());
|
||||
|
||||
difference =
|
||||
new DifferenceAddressSetView(set(rng(0x1000, 0x2fff)), set(rng(0x0000, 0x1fff)));
|
||||
assertEquals(addr(0x2000), difference.getMinAddress());
|
||||
assertEquals(addr(0x2fff), difference.getMaxAddress());
|
||||
}
|
||||
|
||||
protected <T> List<T> collect(Iterator<T> it) {
|
||||
List<T> result = new ArrayList<>();
|
||||
while (it.hasNext()) {
|
||||
result.add(it.next());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected AddressSet randomSet() {
|
||||
Random r = new Random();
|
||||
AddressSet result = new AddressSet();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int len = r.nextInt(0x7ff) + 1;
|
||||
int off = r.nextInt(0x10000 - len);
|
||||
result.add(rng(off, off + len - 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterator() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference =
|
||||
new DifferenceAddressSetView(set(rng(0x1000, 0x1fff)), set(rng(0x0000, 0x0fff)));
|
||||
assertEquals(List.of(rng(0x1000, 0x1fff)), collect(difference.iterator(true)));
|
||||
assertEquals(List.of(rng(0x1000, 0x1fff)), collect(difference.iterator(false)));
|
||||
assertEquals(List.of(rng(0x1000, 0x1fff)),
|
||||
collect(difference.iterator(addr(0x0800), true)));
|
||||
assertEquals(List.of(), collect(difference.iterator(addr(0x0800), false)));
|
||||
assertEquals(List.of(rng(0x1000, 0x1fff)),
|
||||
collect(difference.iterator(addr(0x1800), true)));
|
||||
assertEquals(List.of(rng(0x1000, 0x1fff)),
|
||||
collect(difference.iterator(addr(0x1800), false)));
|
||||
assertEquals(List.of(), collect(difference.iterator(addr(0x2fff), true)));
|
||||
assertEquals(List.of(rng(0x1000, 0x1fff)),
|
||||
collect(difference.iterator(addr(0x2fff), false)));
|
||||
|
||||
difference =
|
||||
new DifferenceAddressSetView(set(rng(0x0000, 0x2fff)), set(rng(0x1000, 0x1fff)));
|
||||
assertEquals(List.of(rng(0x0000, 0x0fff), rng(0x2000, 0x2fff)),
|
||||
collect(difference.iterator(true)));
|
||||
assertEquals(List.of(rng(0x2000, 0x2fff), rng(0x0000, 0x0fff)),
|
||||
collect(difference.iterator(false)));
|
||||
|
||||
AddressSet a = randomSet();
|
||||
AddressSet b = randomSet();
|
||||
try {
|
||||
difference = new DifferenceAddressSetView(a, b);
|
||||
AddressSetView expected = a.subtract(b);
|
||||
assertEquals(collect(expected.getAddressRanges(true)),
|
||||
collect(difference.iterator(true)));
|
||||
assertEquals(collect(expected.getAddressRanges(false)),
|
||||
collect(difference.iterator(false)));
|
||||
assertEquals(collect(expected.getAddressRanges(addr(0x8000), true)),
|
||||
collect(difference.iterator(addr(0x8000), true)));
|
||||
assertEquals(collect(expected.getAddressRanges(addr(0x8000), false)),
|
||||
collect(difference.iterator(addr(0x8000), false)));
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
System.out.println("Failing sets: ");
|
||||
System.out.println(" A: " + a);
|
||||
System.out.println(" B: " + b);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAddresses() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new DifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertFalse(difference.getAddresses(true).hasNext());
|
||||
assertFalse(difference.getAddresses(false).hasNext());
|
||||
|
||||
difference =
|
||||
new DifferenceAddressSetView(set(rng(1, 3), rng(8, 9)), set(rng(2, 4), rng(6, 7)));
|
||||
assertEquals(addrs(1, 8, 9), collect(difference.getAddresses(true)));
|
||||
assertEquals(addrs(9, 8, 1), collect(difference.getAddresses(false)));
|
||||
assertEquals(addrs(8, 9), collect(difference.getAddresses(addr(3), true)));
|
||||
assertEquals(addrs(8, 9), collect(difference.getAddresses(addr(5), true)));
|
||||
assertEquals(addrs(8, 1), collect(difference.getAddresses(addr(8), false)));
|
||||
assertEquals(addrs(1), collect(difference.getAddresses(addr(5), false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRangeContaining() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new DifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(difference.getRangeContaining(addr(0x0800)));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff), rng(0x4000, 0x4fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new DifferenceAddressSetView(a, b);
|
||||
assertEquals(rng(0x0000, 0x0fff), difference.getRangeContaining(addr(0x0000)));
|
||||
assertEquals(rng(0x0000, 0x0fff), difference.getRangeContaining(addr(0x0800)));
|
||||
assertEquals(rng(0x0000, 0x0fff), difference.getRangeContaining(addr(0x0fff)));
|
||||
assertNull(difference.getRangeContaining(addr(0x1000)));
|
||||
assertNull(difference.getRangeContaining(addr(0x1800)));
|
||||
assertNull(difference.getRangeContaining(addr(0x1fff)));
|
||||
assertEquals(rng(0x2000, 0x2fff), difference.getRangeContaining(addr(0x2000)));
|
||||
assertEquals(rng(0x2000, 0x2fff), difference.getRangeContaining(addr(0x2800)));
|
||||
assertEquals(rng(0x2000, 0x2fff), difference.getRangeContaining(addr(0x2fff)));
|
||||
assertNull(difference.getRangeContaining(addr(0x3000)));
|
||||
assertEquals(rng(0x4000, 0x4fff), difference.getRangeContaining(addr(0x4800)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasSameAddresses() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new DifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertTrue(difference.hasSameAddresses(new AddressSet()));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new DifferenceAddressSetView(a, b);
|
||||
assertFalse(difference.hasSameAddresses(new AddressSet()));
|
||||
assertTrue(difference.hasSameAddresses(set(rng(0x0000, 0x0fff), rng(0x2000, 0x2fff))));
|
||||
assertFalse(difference.hasSameAddresses(set(rng(0x0000, 0x0fff))));
|
||||
assertFalse(difference.hasSameAddresses(set(rng(0x2000, 0x2fff))));
|
||||
assertFalse(difference.hasSameAddresses(set(rng(0x0000, 0x0fff), rng(0x2000, 0x3000))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFirstLastRanges() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new DifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(difference.getFirstRange());
|
||||
assertNull(difference.getLastRange());
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new DifferenceAddressSetView(a, b);
|
||||
assertEquals(rng(0x0000, 0x0fff), difference.getFirstRange());
|
||||
assertEquals(rng(0x2000, 0x2fff), difference.getLastRange());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersect() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new DifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertFalse(difference.intersects(addr(0x1000), addr(0x1fff)));
|
||||
assertEquals(new AddressSet(), difference.intersectRange(addr(0x1000), addr(0x1fff)));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new DifferenceAddressSetView(a, b);
|
||||
assertTrue(difference.intersects(addr(0x0000), addr(0x00ff)));
|
||||
assertEquals(set(rng(0x000, 0x00ff)),
|
||||
difference.intersectRange(addr(0x0000), addr(0x00ff)));
|
||||
assertFalse(difference.intersects(addr(0x5000), addr(0x5fff)));
|
||||
assertEquals(new AddressSet(), difference.intersectRange(addr(0x5000), addr(0x5fff)));
|
||||
assertTrue(difference.intersects(addr(0x0000), addr(0x1fff)));
|
||||
assertEquals(set(rng(0x0000, 0x0fff)),
|
||||
difference.intersectRange(addr(0x0000), addr(0x1fff)));
|
||||
assertTrue(difference.intersects(addr(0x0000), addr(0x3fff)));
|
||||
assertEquals(set(rng(0x0000, 0x0fff), rng(0x2000, 0x2fff)),
|
||||
difference.intersectRange(addr(0x0000), addr(0x3fff)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnion() {
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
AddressSetView difference = new DifferenceAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0000, 0x2fff)), difference.union(set(rng(0x1000, 0x1fff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtract() {
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
AddressSetView difference = new DifferenceAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0000, 0x03ff), rng(0x0c00, 0x0fff), rng(0x2000, 0x2fff)),
|
||||
difference.subtract(set(rng(0x0400, 0x0bff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXor() {
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
AddressSetView difference = new DifferenceAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0000, 0x07ff), rng(0x1000, 0x1fff), rng(0x2800, 0x2fff)),
|
||||
difference.xor(set(rng(0x0800, 0x27ff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindFirstAddressInCommon() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new DifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(difference.findFirstAddressInCommon(set(rng(0x1000, 0x1fff))));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new DifferenceAddressSetView(a, b);
|
||||
assertNull(difference.findFirstAddressInCommon(set(rng(0x1000, 0x1fff))));
|
||||
assertEquals(addr(0x0800), difference.findFirstAddressInCommon(set(rng(0x0800, 0x1fff))));
|
||||
assertEquals(addr(0x2000), difference.findFirstAddressInCommon(set(rng(0x1800, 0x3fff))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,333 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class IntersectionAddressSetViewTest extends AbstractGTest {
|
||||
protected AddressSpace space = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 1);
|
||||
|
||||
protected Address addr(long offset) {
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
protected AddressRange rng(long min, long max) {
|
||||
return new AddressRangeImpl(addr(min), addr(max));
|
||||
}
|
||||
|
||||
protected AddressSet set(AddressRange... ranges) {
|
||||
AddressSet set = new AddressSet();
|
||||
for (AddressRange rng : ranges) {
|
||||
set.add(rng);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
protected List<Address> addrs(long... offsets) {
|
||||
List<Address> result = new ArrayList<>(offsets.length);
|
||||
for (long off : offsets) {
|
||||
result.add(addr(off));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCounts() {
|
||||
AddressSetView intersection;
|
||||
|
||||
intersection = new IntersectionAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertTrue(intersection.isEmpty());
|
||||
assertEquals(0, intersection.getNumAddresses());
|
||||
assertEquals(0, intersection.getNumAddressRanges());
|
||||
|
||||
// Disjoint, connected
|
||||
intersection =
|
||||
new IntersectionAddressSetView(set(rng(0x0000, 0x0fff)), set(rng(0x1000, 0x1fff)));
|
||||
assertTrue(intersection.isEmpty());
|
||||
assertEquals(0, intersection.getNumAddresses());
|
||||
assertEquals(0, intersection.getNumAddressRanges());
|
||||
|
||||
// One enclosed
|
||||
intersection =
|
||||
new IntersectionAddressSetView(set(rng(0x0000, 0x2fff)), set(rng(0x1000, 0x1fff)));
|
||||
assertFalse(intersection.isEmpty());
|
||||
assertEquals(0x1000, intersection.getNumAddresses());
|
||||
assertEquals(1, intersection.getNumAddressRanges());
|
||||
|
||||
// Overlapping
|
||||
intersection =
|
||||
new IntersectionAddressSetView(set(rng(0x0000, 0x1fff)), set(rng(0x1000, 0x2fff)));
|
||||
assertFalse(intersection.isEmpty());
|
||||
assertEquals(0x1000, intersection.getNumAddresses());
|
||||
assertEquals(1, intersection.getNumAddressRanges());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContains() {
|
||||
AddressSetView intersection;
|
||||
|
||||
intersection = new IntersectionAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertFalse(intersection.contains(addr(0x0800), addr(0x1800)));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x1fff));
|
||||
AddressSet b = set(rng(0x1000, 0x2fff));
|
||||
intersection = new IntersectionAddressSetView(a, b);
|
||||
assertFalse(intersection.contains(addr(0x0800)));
|
||||
assertTrue(intersection.contains(addr(0x1800)));
|
||||
assertFalse(intersection.contains(addr(0x2800)));
|
||||
assertFalse(intersection.contains(addr(0x3000)));
|
||||
assertTrue(intersection.contains(addr(0x1800), addr(0x1bff)));
|
||||
assertFalse(intersection.contains(addr(0x1800), addr(0x2bff)));
|
||||
assertFalse(intersection.contains(addr(0x0800), addr(0x1bff)));
|
||||
assertFalse(intersection.contains(addr(0x1fff), addr(0x2fff)));
|
||||
assertFalse(intersection.contains(addr(0x2000), addr(0x3000)));
|
||||
assertTrue(intersection.contains(set(rng(0x1200, 0x15ff), rng(0x1a00, 0x1dff))));
|
||||
assertFalse(intersection.contains(set(rng(0x0800, 0x15ff), rng(0x1a00, 0x1dff))));
|
||||
assertFalse(intersection.contains(set(rng(0x1200, 0x15ff), rng(0x1a00, 0x2000))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndpoints() {
|
||||
AddressSetView intersection;
|
||||
|
||||
intersection = new IntersectionAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(intersection.getMinAddress());
|
||||
assertNull(intersection.getMaxAddress());
|
||||
|
||||
intersection =
|
||||
new IntersectionAddressSetView(set(rng(0x0000, 0x0fff)), set(rng(0x1000, 0x1fff)));
|
||||
assertNull(intersection.getMinAddress());
|
||||
assertNull(intersection.getMaxAddress());
|
||||
|
||||
intersection =
|
||||
new IntersectionAddressSetView(set(rng(0x1000, 0x2fff)), set(rng(0x0000, 0x1fff)));
|
||||
assertEquals(addr(0x1000), intersection.getMinAddress());
|
||||
assertEquals(addr(0x1fff), intersection.getMaxAddress());
|
||||
|
||||
intersection =
|
||||
new IntersectionAddressSetView(set(rng(0x1000, 0x1fff)), set(rng(0x0000, 0x2fff)));
|
||||
assertEquals(addr(0x1000), intersection.getMinAddress());
|
||||
assertEquals(addr(0x1fff), intersection.getMaxAddress());
|
||||
}
|
||||
|
||||
protected <T> List<T> collect(Iterator<T> it) {
|
||||
List<T> result = new ArrayList<>();
|
||||
while (it.hasNext()) {
|
||||
result.add(it.next());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected AddressSet randomSet() {
|
||||
Random r = new Random();
|
||||
AddressSet result = new AddressSet();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int len = r.nextInt(0x7ff) + 1;
|
||||
int off = r.nextInt(0x10000 - len);
|
||||
result.add(rng(off, off + len - 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterator() {
|
||||
AddressSetView intersection;
|
||||
|
||||
intersection = new IntersectionAddressSetView(set(rng(0x1000, 0x3fff)),
|
||||
set(rng(0x0000, 0x1fff), rng(0x3000, 0x4fff)));
|
||||
assertEquals(List.of(rng(0x1000, 0x1fff), rng(0x3000, 0x3fff)),
|
||||
collect(intersection.iterator(true)));
|
||||
assertEquals(List.of(rng(0x3fff, 0x3000), rng(0x1000, 0x1fff)),
|
||||
collect(intersection.iterator(false)));
|
||||
assertEquals(List.of(rng(0x1000, 0x1fff), rng(0x3000, 0x3fff)),
|
||||
collect(intersection.iterator(addr(0x0800), true)));
|
||||
assertEquals(List.of(), collect(intersection.iterator(addr(0x0800), false)));
|
||||
assertEquals(List.of(rng(0x1000, 0x1fff), rng(0x3000, 0x3fff)),
|
||||
collect(intersection.iterator(addr(0x1800), true)));
|
||||
assertEquals(List.of(rng(0x1000, 0x1fff)),
|
||||
collect(intersection.iterator(addr(0x1800), false)));
|
||||
assertEquals(List.of(), collect(intersection.iterator(addr(0x4fff), true)));
|
||||
assertEquals(List.of(rng(0x3000, 0x3fff), rng(0x1000, 0x1fff)),
|
||||
collect(intersection.iterator(addr(0x4fff), false)));
|
||||
|
||||
AddressSet a = randomSet();
|
||||
AddressSet b = randomSet();
|
||||
try {
|
||||
intersection = new IntersectionAddressSetView(a, b);
|
||||
AddressSetView expected = a.intersect(b);
|
||||
assertEquals(collect(expected.getAddressRanges(true)),
|
||||
collect(intersection.iterator(true)));
|
||||
assertEquals(collect(expected.getAddressRanges(false)),
|
||||
collect(intersection.iterator(false)));
|
||||
assertEquals(collect(expected.getAddressRanges(addr(0x8000), true)),
|
||||
collect(intersection.iterator(addr(0x8000), true)));
|
||||
assertEquals(collect(expected.getAddressRanges(addr(0x8000), false)),
|
||||
collect(intersection.iterator(addr(0x8000), false)));
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
System.out.println("Failing sets: ");
|
||||
System.out.println(" A: " + a);
|
||||
System.out.println(" B: " + b);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAddresses() {
|
||||
AddressSetView intersection;
|
||||
|
||||
intersection = new IntersectionAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertFalse(intersection.getAddresses(true).hasNext());
|
||||
assertFalse(intersection.getAddresses(false).hasNext());
|
||||
|
||||
intersection =
|
||||
new IntersectionAddressSetView(set(rng(1, 3), rng(5, 9)), set(rng(2, 4), rng(6, 7)));
|
||||
assertEquals(addrs(2, 3, 6, 7), collect(intersection.getAddresses(true)));
|
||||
assertEquals(addrs(7, 6, 3, 2), collect(intersection.getAddresses(false)));
|
||||
assertEquals(addrs(3, 6, 7), collect(intersection.getAddresses(addr(3), true)));
|
||||
assertEquals(addrs(6, 7), collect(intersection.getAddresses(addr(5), true)));
|
||||
assertEquals(addrs(7, 6, 3, 2), collect(intersection.getAddresses(addr(8), false)));
|
||||
assertEquals(addrs(3, 2), collect(intersection.getAddresses(addr(5), false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRangeContaining() {
|
||||
AddressSetView intersection;
|
||||
|
||||
intersection = new IntersectionAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(intersection.getRangeContaining(addr(0x0800)));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x1fff), rng(0x4000, 0x4fff));
|
||||
AddressSet b = set(rng(0x1000, 0x2fff));
|
||||
intersection = new IntersectionAddressSetView(a, b);
|
||||
assertNull(intersection.getRangeContaining(addr(0x0000)));
|
||||
assertNull(intersection.getRangeContaining(addr(0x0800)));
|
||||
assertNull(intersection.getRangeContaining(addr(0x0fff)));
|
||||
assertEquals(rng(0x1000, 0x1fff), intersection.getRangeContaining(addr(0x1000)));
|
||||
assertEquals(rng(0x1000, 0x1fff), intersection.getRangeContaining(addr(0x1800)));
|
||||
assertEquals(rng(0x1000, 0x1fff), intersection.getRangeContaining(addr(0x1fff)));
|
||||
assertNull(intersection.getRangeContaining(addr(0x2000)));
|
||||
assertNull(intersection.getRangeContaining(addr(0x2800)));
|
||||
assertNull(intersection.getRangeContaining(addr(0x2fff)));
|
||||
assertNull(intersection.getRangeContaining(addr(0x3000)));
|
||||
assertNull(intersection.getRangeContaining(addr(0x4800)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasSameAddresses() {
|
||||
AddressSetView intersection;
|
||||
|
||||
intersection = new IntersectionAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertTrue(intersection.hasSameAddresses(new AddressSet()));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x1fff));
|
||||
AddressSet b = set(rng(0x1000, 0x2fff));
|
||||
intersection = new IntersectionAddressSetView(a, b);
|
||||
assertFalse(intersection.hasSameAddresses(new AddressSet()));
|
||||
assertTrue(intersection.hasSameAddresses(set(rng(0x1000, 0x1fff))));
|
||||
assertFalse(intersection.hasSameAddresses(set(rng(0x0000, 0x1fff))));
|
||||
assertFalse(intersection.hasSameAddresses(set(rng(0x1000, 0x2fff))));
|
||||
assertFalse(intersection.hasSameAddresses(set(rng(0x1000, 0x1fff), rng(0x3000, 0x3fff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFirstLastRanges() {
|
||||
AddressSetView intersection;
|
||||
|
||||
intersection = new IntersectionAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(intersection.getFirstRange());
|
||||
assertNull(intersection.getLastRange());
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x1fff), rng(0x3000, 0x4fff));
|
||||
AddressSet b = set(rng(0x1000, 0x3fff));
|
||||
intersection = new IntersectionAddressSetView(a, b);
|
||||
assertEquals(rng(0x1000, 0x1fff), intersection.getFirstRange());
|
||||
assertEquals(rng(0x3000, 0x3fff), intersection.getLastRange());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersect() {
|
||||
AddressSetView intersection;
|
||||
|
||||
intersection = new IntersectionAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertFalse(intersection.intersects(addr(0x1000), addr(0x1fff)));
|
||||
assertEquals(new AddressSet(), intersection.intersectRange(addr(0x1000), addr(0x1fff)));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x1fff), rng(0x3000, 0x4fff));
|
||||
AddressSet b = set(rng(0x1000, 0x3fff));
|
||||
intersection = new IntersectionAddressSetView(a, b);
|
||||
assertFalse(intersection.intersects(addr(0x0000), addr(0x00ff)));
|
||||
assertEquals(new AddressSet(), intersection.intersectRange(addr(0x0000), addr(0x00ff)));
|
||||
|
||||
assertFalse(intersection.intersects(addr(0x5000), addr(0x5fff)));
|
||||
assertEquals(new AddressSet(), intersection.intersectRange(addr(0x5000), addr(0x5fff)));
|
||||
|
||||
assertTrue(intersection.intersects(addr(0x0000), addr(0x1fff)));
|
||||
assertEquals(set(rng(0x1000, 0x1fff)),
|
||||
intersection.intersectRange(addr(0x0000), addr(0x1fff)));
|
||||
|
||||
assertTrue(intersection.intersects(addr(0x1800), addr(0x37ff)));
|
||||
assertEquals(set(rng(0x1800, 0x1fff), rng(0x3000, 0x37ff)),
|
||||
intersection.intersectRange(addr(0x1800), addr(0x37ff)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnion() {
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
AddressSetView intersection = new IntersectionAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0000, 0x1fff)), intersection.union(set(rng(0x0000, 0x1fff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtract() {
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
AddressSetView intersection = new IntersectionAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x1000, 0x13ff), rng(0x1c00, 0x1fff)),
|
||||
intersection.subtract(set(rng(0x1400, 0x1bff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXor() {
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
AddressSetView intersection = new IntersectionAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0000, 0x0fff)), intersection.xor(set(rng(0x0000, 0x1fff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindFirstAddressInCommon() {
|
||||
AddressSetView intersection;
|
||||
|
||||
intersection = new IntersectionAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(intersection.findFirstAddressInCommon(set(rng(0x1000, 0x1fff))));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x1fff));
|
||||
AddressSet b = set(rng(0x1000, 0x2fff));
|
||||
intersection = new IntersectionAddressSetView(a, b);
|
||||
assertNull(intersection.findFirstAddressInCommon(set(rng(0x0000, 0x0fff))));
|
||||
assertEquals(addr(0x1000), intersection.findFirstAddressInCommon(set(rng(0x0800, 0x1fff))));
|
||||
assertEquals(addr(0x1800), intersection.findFirstAddressInCommon(set(rng(0x1800, 0x3fff))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.util.MergeSortingIterator;
|
||||
|
||||
public class MergeSortingIteratorTest {
|
||||
@Test
|
||||
public void testEmptyMap() {
|
||||
Map<String, Iterator<Integer>> iterMap = new HashMap<>();
|
||||
Iterator<Entry<String, Integer>> iter =
|
||||
MergeSortingIterator.withLabels(iterMap, Comparator.naturalOrder());
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapOfEmpties() {
|
||||
Map<String, Iterator<Integer>> iterMap = new HashMap<>();
|
||||
iterMap.put("A", Collections.emptyIterator());
|
||||
iterMap.put("B", Collections.emptyIterator());
|
||||
Iterator<Entry<String, Integer>> iter =
|
||||
MergeSortingIterator.withLabels(iterMap, Comparator.naturalOrder());
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2By2Seq() {
|
||||
Map<String, Iterator<Integer>> iterMap = new HashMap<>();
|
||||
iterMap.put("A", List.of(1, 2).iterator());
|
||||
iterMap.put("B", List.of(3, 4).iterator());
|
||||
Iterator<Entry<String, Integer>> iter =
|
||||
MergeSortingIterator.withLabels(iterMap, Comparator.naturalOrder());
|
||||
Entry<String, Integer> entry;
|
||||
|
||||
assertTrue(iter.hasNext());
|
||||
entry = iter.next();
|
||||
assertEquals("A", entry.getKey());
|
||||
assertEquals(1, entry.getValue().intValue());
|
||||
|
||||
assertTrue(iter.hasNext());
|
||||
entry = iter.next();
|
||||
assertEquals("A", entry.getKey());
|
||||
assertEquals(2, entry.getValue().intValue());
|
||||
|
||||
assertTrue(iter.hasNext());
|
||||
entry = iter.next();
|
||||
assertEquals("B", entry.getKey());
|
||||
assertEquals(3, entry.getValue().intValue());
|
||||
|
||||
assertTrue(iter.hasNext());
|
||||
entry = iter.next();
|
||||
assertEquals("B", entry.getKey());
|
||||
assertEquals(4, entry.getValue().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2By2Alt() {
|
||||
Map<String, Iterator<Integer>> iterMap = new HashMap<>();
|
||||
iterMap.put("A", List.of(1, 3).iterator());
|
||||
iterMap.put("B", List.of(2, 4).iterator());
|
||||
Iterator<Entry<String, Integer>> iter =
|
||||
MergeSortingIterator.withLabels(iterMap, Comparator.naturalOrder());
|
||||
Entry<String, Integer> entry;
|
||||
|
||||
assertTrue(iter.hasNext());
|
||||
entry = iter.next();
|
||||
assertEquals("A", entry.getKey());
|
||||
assertEquals(1, entry.getValue().intValue());
|
||||
|
||||
assertTrue(iter.hasNext());
|
||||
entry = iter.next();
|
||||
assertEquals("B", entry.getKey());
|
||||
assertEquals(2, entry.getValue().intValue());
|
||||
|
||||
assertTrue(iter.hasNext());
|
||||
entry = iter.next();
|
||||
assertEquals("A", entry.getKey());
|
||||
assertEquals(3, entry.getValue().intValue());
|
||||
|
||||
assertTrue(iter.hasNext());
|
||||
entry = iter.next();
|
||||
assertEquals("B", entry.getKey());
|
||||
assertEquals(4, entry.getValue().intValue());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class SymmetricDifferenceAddressSetViewTest extends AbstractGTest {
|
||||
protected AddressSpace space = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 1);
|
||||
|
||||
protected Address addr(long offset) {
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
protected AddressRange rng(long min, long max) {
|
||||
return new AddressRangeImpl(addr(min), addr(max));
|
||||
}
|
||||
|
||||
protected AddressSet set(AddressRange... ranges) {
|
||||
AddressSet set = new AddressSet();
|
||||
for (AddressRange rng : ranges) {
|
||||
set.add(rng);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
protected List<Address> addrs(long... offsets) {
|
||||
List<Address> result = new ArrayList<>(offsets.length);
|
||||
for (long off : offsets) {
|
||||
result.add(addr(off));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCounts() {
|
||||
AddressSetView xor;
|
||||
|
||||
xor = new SymmetricDifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertTrue(xor.isEmpty());
|
||||
assertEquals(0, xor.getNumAddresses());
|
||||
assertEquals(0, xor.getNumAddressRanges());
|
||||
|
||||
// Disjoint, connected
|
||||
xor = new SymmetricDifferenceAddressSetView(set(rng(0x0000, 0x0fff)),
|
||||
set(rng(0x1000, 0x1fff)));
|
||||
assertFalse(xor.isEmpty());
|
||||
assertEquals(0x2000, xor.getNumAddresses());
|
||||
assertEquals(1, xor.getNumAddressRanges());
|
||||
|
||||
// Subtract from middle
|
||||
xor = new SymmetricDifferenceAddressSetView(set(rng(0x0000, 0x2fff)),
|
||||
set(rng(0x1000, 0x1fff)));
|
||||
assertFalse(xor.isEmpty());
|
||||
assertEquals(0x2000, xor.getNumAddresses());
|
||||
assertEquals(2, xor.getNumAddressRanges());
|
||||
|
||||
// Reverse of above
|
||||
xor = new SymmetricDifferenceAddressSetView(set(rng(0x1000, 0x1fff)),
|
||||
set(rng(0x0000, 0x2fff)));
|
||||
assertFalse(xor.isEmpty());
|
||||
assertEquals(0x2000, xor.getNumAddresses());
|
||||
assertEquals(2, xor.getNumAddressRanges());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContains() {
|
||||
AddressSetView xor;
|
||||
|
||||
xor = new SymmetricDifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertFalse(xor.contains(addr(0x0800), addr(0x1800)));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
xor = new SymmetricDifferenceAddressSetView(a, b);
|
||||
assertTrue(xor.contains(addr(0x0800)));
|
||||
assertFalse(xor.contains(addr(0x1800)));
|
||||
assertTrue(xor.contains(addr(0x2800)));
|
||||
assertFalse(xor.contains(addr(0x3000)));
|
||||
assertTrue(xor.contains(addr(0x0800), addr(0x0fff)));
|
||||
assertFalse(xor.contains(addr(0x0800), addr(0x1000)));
|
||||
assertTrue(xor.contains(addr(0x2000), addr(0x2fff)));
|
||||
assertFalse(xor.contains(addr(0x1fff), addr(0x2fff)));
|
||||
assertFalse(xor.contains(addr(0x2000), addr(0x3000)));
|
||||
assertTrue(xor.contains(set(rng(0x0400, 0x0c00), rng(0x2400, 0x2c00))));
|
||||
assertFalse(xor.contains(set(rng(0x0400, 0x0c00), rng(0x1c00, 0x2fff))));
|
||||
assertFalse(xor.contains(set(rng(0x0400, 0x0c00), rng(0x2400, 0x3c00))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndpoints() {
|
||||
AddressSetView xor;
|
||||
|
||||
xor = new SymmetricDifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(xor.getMinAddress());
|
||||
assertNull(xor.getMaxAddress());
|
||||
|
||||
xor = new SymmetricDifferenceAddressSetView(set(rng(0x0000, 0x2fff)),
|
||||
set(rng(0x1000, 0x1fff)));
|
||||
assertEquals(addr(0x0000), xor.getMinAddress());
|
||||
assertEquals(addr(0x2fff), xor.getMaxAddress());
|
||||
|
||||
xor = new SymmetricDifferenceAddressSetView(set(rng(0x0000, 0x2fff)),
|
||||
set(rng(0x1000, 0x2fff)));
|
||||
assertEquals(addr(0x0000), xor.getMinAddress());
|
||||
assertEquals(addr(0x0fff), xor.getMaxAddress());
|
||||
|
||||
xor = new SymmetricDifferenceAddressSetView(set(rng(0x0000, 0x2fff)),
|
||||
set(rng(0x0000, 0x1fff)));
|
||||
assertEquals(addr(0x2000), xor.getMinAddress());
|
||||
assertEquals(addr(0x2fff), xor.getMaxAddress());
|
||||
}
|
||||
|
||||
protected <T> List<T> collect(Iterator<T> it) {
|
||||
List<T> result = new ArrayList<>();
|
||||
while (it.hasNext()) {
|
||||
result.add(it.next());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected AddressSet randomSet() {
|
||||
Random r = new Random();
|
||||
AddressSet result = new AddressSet();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int len = r.nextInt(0x7ff) + 1;
|
||||
int off = r.nextInt(0x10000 - len);
|
||||
result.add(rng(off, off + len - 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterator() {
|
||||
AddressSetView xor;
|
||||
|
||||
xor = new SymmetricDifferenceAddressSetView(set(rng(0x1000, 0x1fff)),
|
||||
set(rng(0x0000, 0x0fff)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(xor.iterator(true)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(xor.iterator(false)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(xor.iterator(addr(0x0800), true)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(xor.iterator(addr(0x0800), false)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(xor.iterator(addr(0x1800), true)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(xor.iterator(addr(0x1800), false)));
|
||||
assertEquals(List.of(), collect(xor.iterator(addr(0x2fff), true)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(xor.iterator(addr(0x2fff), false)));
|
||||
|
||||
xor = new SymmetricDifferenceAddressSetView(set(rng(0x0000, 0x2fff)),
|
||||
set(rng(0x1000, 0x1fff)));
|
||||
assertEquals(List.of(rng(0x0000, 0x0fff), rng(0x2000, 0x2fff)),
|
||||
collect(xor.iterator(true)));
|
||||
assertEquals(List.of(rng(0x2000, 0x2fff), rng(0x0000, 0x0fff)),
|
||||
collect(xor.iterator(false)));
|
||||
|
||||
// +---+---+---+---+---+
|
||||
// 0 . 1 . 2 . 3 . 4 . 5
|
||||
// A: ---- ----
|
||||
// B: ---- ----
|
||||
// X: ---- -- -- ----
|
||||
// <- ^
|
||||
xor = new SymmetricDifferenceAddressSetView(set(rng(0x0000, 0x0fff), rng(0x2000, 0x2fff)),
|
||||
set(rng(0x1800, 0x27ff), rng(0x4000, 0x4fff)));
|
||||
assertEquals(List.of(rng(0x1800, 0x1fff), rng(0x2800, 0x2fff), rng(0x4000, 0x4fff)),
|
||||
collect(xor.iterator(addr(0x1c00), true)));
|
||||
assertEquals(List.of(rng(0x1800, 0x1fff), rng(0x0000, 0x0fff)),
|
||||
collect(xor.iterator(addr(0x1c00), false)));
|
||||
|
||||
AddressSet a = null;
|
||||
AddressSet b = null;
|
||||
try {
|
||||
// This one has been particularly elusive.
|
||||
// Increase the likelihood of failure, if there is indeed an error.
|
||||
a = randomSet();
|
||||
b = randomSet();
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
xor = new SymmetricDifferenceAddressSetView(a, b);
|
||||
AddressSetView expected = a.xor(b);
|
||||
assertEquals(collect(expected.getAddressRanges(true)), collect(xor.iterator(true)));
|
||||
assertEquals(collect(expected.getAddressRanges(false)),
|
||||
collect(xor.iterator(false)));
|
||||
assertEquals(collect(expected.getAddressRanges(addr(0x8000), true)),
|
||||
collect(xor.iterator(addr(0x8000), true)));
|
||||
assertEquals(collect(expected.getAddressRanges(addr(0x8000), false)),
|
||||
collect(xor.iterator(addr(0x8000), false)));
|
||||
}
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
System.out.println("Failing sets: ");
|
||||
System.out.println(" A: " + a);
|
||||
System.out.println(" B: " + b);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAddresses() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new SymmetricDifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertFalse(difference.getAddresses(true).hasNext());
|
||||
assertFalse(difference.getAddresses(false).hasNext());
|
||||
|
||||
difference = new SymmetricDifferenceAddressSetView(set(rng(1, 3), rng(8, 9)),
|
||||
set(rng(2, 4), rng(6, 7)));
|
||||
assertEquals(addrs(1, 4, 6, 7, 8, 9), collect(difference.getAddresses(true)));
|
||||
assertEquals(addrs(9, 8, 7, 6, 4, 1), collect(difference.getAddresses(false)));
|
||||
assertEquals(addrs(4, 6, 7, 8, 9), collect(difference.getAddresses(addr(3), true)));
|
||||
assertEquals(addrs(6, 7, 8, 9), collect(difference.getAddresses(addr(5), true)));
|
||||
assertEquals(addrs(8, 7, 6, 4, 1), collect(difference.getAddresses(addr(8), false)));
|
||||
assertEquals(addrs(4, 1), collect(difference.getAddresses(addr(5), false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRangeContaining() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new SymmetricDifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(difference.getRangeContaining(addr(0x0800)));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff), rng(0x4000, 0x4fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new SymmetricDifferenceAddressSetView(a, b);
|
||||
assertEquals(rng(0x0000, 0x0fff), difference.getRangeContaining(addr(0x0000)));
|
||||
assertEquals(rng(0x0000, 0x0fff), difference.getRangeContaining(addr(0x0800)));
|
||||
assertEquals(rng(0x0000, 0x0fff), difference.getRangeContaining(addr(0x0fff)));
|
||||
assertNull(difference.getRangeContaining(addr(0x1000)));
|
||||
assertNull(difference.getRangeContaining(addr(0x1800)));
|
||||
assertNull(difference.getRangeContaining(addr(0x1fff)));
|
||||
assertEquals(rng(0x2000, 0x2fff), difference.getRangeContaining(addr(0x2000)));
|
||||
assertEquals(rng(0x2000, 0x2fff), difference.getRangeContaining(addr(0x2800)));
|
||||
assertEquals(rng(0x2000, 0x2fff), difference.getRangeContaining(addr(0x2fff)));
|
||||
assertNull(difference.getRangeContaining(addr(0x3000)));
|
||||
assertEquals(rng(0x4000, 0x4fff), difference.getRangeContaining(addr(0x4800)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasSameAddresses() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new SymmetricDifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertTrue(difference.hasSameAddresses(new AddressSet()));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new SymmetricDifferenceAddressSetView(a, b);
|
||||
assertFalse(difference.hasSameAddresses(new AddressSet()));
|
||||
assertTrue(difference.hasSameAddresses(set(rng(0x0000, 0x0fff), rng(0x2000, 0x2fff))));
|
||||
assertFalse(difference.hasSameAddresses(set(rng(0x0000, 0x0fff))));
|
||||
assertFalse(difference.hasSameAddresses(set(rng(0x2000, 0x2fff))));
|
||||
assertFalse(difference.hasSameAddresses(set(rng(0x0000, 0x0fff), rng(0x2000, 0x3000))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFirstLastRanges() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new SymmetricDifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(difference.getFirstRange());
|
||||
assertNull(difference.getLastRange());
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new SymmetricDifferenceAddressSetView(a, b);
|
||||
assertEquals(rng(0x0000, 0x0fff), difference.getFirstRange());
|
||||
assertEquals(rng(0x2000, 0x2fff), difference.getLastRange());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersect() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new SymmetricDifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertFalse(difference.intersects(addr(0x1000), addr(0x1fff)));
|
||||
assertEquals(new AddressSet(), difference.intersectRange(addr(0x1000), addr(0x1fff)));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new SymmetricDifferenceAddressSetView(a, b);
|
||||
assertTrue(difference.intersects(addr(0x0000), addr(0x00ff)));
|
||||
assertEquals(set(rng(0x000, 0x00ff)),
|
||||
difference.intersectRange(addr(0x0000), addr(0x00ff)));
|
||||
assertFalse(difference.intersects(addr(0x5000), addr(0x5fff)));
|
||||
assertEquals(new AddressSet(), difference.intersectRange(addr(0x5000), addr(0x5fff)));
|
||||
assertTrue(difference.intersects(addr(0x0000), addr(0x1fff)));
|
||||
assertEquals(set(rng(0x0000, 0x0fff)),
|
||||
difference.intersectRange(addr(0x0000), addr(0x1fff)));
|
||||
assertTrue(difference.intersects(addr(0x0000), addr(0x3fff)));
|
||||
assertEquals(set(rng(0x0000, 0x0fff), rng(0x2000, 0x2fff)),
|
||||
difference.intersectRange(addr(0x0000), addr(0x3fff)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnion() {
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
AddressSetView difference = new SymmetricDifferenceAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0000, 0x2fff)), difference.union(set(rng(0x1000, 0x1fff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtract() {
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
AddressSetView difference = new SymmetricDifferenceAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0000, 0x03ff), rng(0x0c00, 0x0fff), rng(0x2000, 0x2fff)),
|
||||
difference.subtract(set(rng(0x0400, 0x0bff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXor() {
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
AddressSetView difference = new SymmetricDifferenceAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0000, 0x07ff), rng(0x1000, 0x1fff), rng(0x2800, 0x2fff)),
|
||||
difference.xor(set(rng(0x0800, 0x27ff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindFirstAddressInCommon() {
|
||||
AddressSetView difference;
|
||||
|
||||
difference = new SymmetricDifferenceAddressSetView(new AddressSet(), new AddressSet());
|
||||
assertNull(difference.findFirstAddressInCommon(set(rng(0x1000, 0x1fff))));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x2fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
difference = new SymmetricDifferenceAddressSetView(a, b);
|
||||
assertNull(difference.findFirstAddressInCommon(set(rng(0x1000, 0x1fff))));
|
||||
assertEquals(addr(0x0800), difference.findFirstAddressInCommon(set(rng(0x0800, 0x1fff))));
|
||||
assertEquals(addr(0x2000), difference.findFirstAddressInCommon(set(rng(0x1800, 0x3fff))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,400 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.util.TwoWayBreakdownAddressRangeIterator.Which;
|
||||
|
||||
public class TwoWayBreakdownAddressRangeIteratorTest extends AbstractGTest {
|
||||
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,
|
||||
boolean forward) {
|
||||
return new TwoWayBreakdownAddressRangeIterator(a.iterator(forward), b.iterator(forward),
|
||||
forward);
|
||||
}
|
||||
|
||||
protected Address addr(long offset) {
|
||||
return rom.getAddress(offset);
|
||||
}
|
||||
|
||||
protected Address dAddr(long offset) {
|
||||
return ram.getAddress(offset);
|
||||
}
|
||||
|
||||
protected AddressRange rng(long min, long max) {
|
||||
return new AddressRangeImpl(addr(min), addr(max));
|
||||
}
|
||||
|
||||
protected AddressRange dRng(long min, long max) {
|
||||
return new AddressRangeImpl(dAddr(min), dAddr(max));
|
||||
}
|
||||
|
||||
protected AddressSet set(AddressRange... ranges) {
|
||||
AddressSet set = new AddressSet();
|
||||
for (AddressRange rng : ranges) {
|
||||
set.add(rng);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
protected Pair<AddressRange, Which> pair(long min, long max, Which which) {
|
||||
return Pair.of(rng(min, max), which);
|
||||
}
|
||||
|
||||
protected Pair<AddressRange, Which> dPair(long min, long max, Which which) {
|
||||
return Pair.of(dRng(min, max), which);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies each entry into a new list, since the iterator may modify the entry in place
|
||||
*
|
||||
* @param it the source iterator
|
||||
* @return the destination (new) list
|
||||
*/
|
||||
protected static <K, V> List<Entry<K, V>> toList(Iterator<Entry<K, V>> it) {
|
||||
List<Entry<K, V>> result = new ArrayList<>();
|
||||
while (it.hasNext()) {
|
||||
Entry<K, V> ent = it.next();
|
||||
result.add(new ImmutablePair<>(ent.getKey(), ent.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBothEmpty() {
|
||||
AddressSet a = new AddressSet();
|
||||
AddressSet b = new AddressSet();
|
||||
|
||||
assertFalse(makeIterator(a, b, true).hasNext());
|
||||
assertFalse(makeIterator(a, b, false).hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneEmpty() {
|
||||
AddressSet a = new AddressSet();
|
||||
AddressSet b = set(rng(0x4000, 0x4fff), rng(0x6000, 0x6fff));
|
||||
|
||||
List<Entry<AddressRange, Which>> expected;
|
||||
List<Entry<AddressRange, Which>> actual;
|
||||
|
||||
expected = List.of(pair(0x4000, 0x4fff, Which.RIGHT), pair(0x6000, 0x6fff, Which.RIGHT));
|
||||
actual = toList(makeIterator(a, b, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x4000, 0x4fff, Which.LEFT), pair(0x6000, 0x6fff, Which.LEFT));
|
||||
actual = toList(makeIterator(b, a, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x6000, 0x6fff, Which.RIGHT), pair(0x4000, 0x4fff, Which.RIGHT));
|
||||
actual = toList(makeIterator(a, b, false));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x6000, 0x6fff, Which.LEFT), pair(0x4000, 0x4fff, Which.LEFT));
|
||||
actual = toList(makeIterator(b, a, false));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnected() {
|
||||
AddressSet a = set(rng(0x3000, 0x3fff), rng(0x5000, 0x5fff));
|
||||
AddressSet b = set(rng(0x4000, 0x4fff), rng(0x6000, 0x6fff));
|
||||
|
||||
List<Entry<AddressRange, Which>> expected;
|
||||
List<Entry<AddressRange, Which>> actual;
|
||||
|
||||
expected = List.of(pair(0x3000, 0x3fff, Which.LEFT), pair(0x4000, 0x4fff, Which.RIGHT),
|
||||
pair(0x5000, 0x5fff, Which.LEFT), pair(0x6000, 0x6fff, Which.RIGHT));
|
||||
actual = toList(makeIterator(a, b, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x3000, 0x3fff, Which.RIGHT), pair(0x4000, 0x4fff, Which.LEFT),
|
||||
pair(0x5000, 0x5fff, Which.RIGHT), pair(0x6000, 0x6fff, Which.LEFT));
|
||||
actual = toList(makeIterator(b, a, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x6000, 0x6fff, Which.RIGHT), pair(0x5000, 0x5fff, Which.LEFT),
|
||||
pair(0x4000, 0x4fff, Which.RIGHT), pair(0x3000, 0x3fff, Which.LEFT));
|
||||
actual = toList(makeIterator(a, b, false));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x6000, 0x6fff, Which.LEFT), pair(0x5000, 0x5fff, Which.RIGHT),
|
||||
pair(0x4000, 0x4fff, Which.LEFT), pair(0x3000, 0x3fff, Which.RIGHT));
|
||||
actual = toList(makeIterator(b, a, false));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisjoint() {
|
||||
AddressSet a = set(rng(0x3000, 0x37ff), rng(0x5000, 0x57ff));
|
||||
AddressSet b = set(rng(0x4000, 0x47ff), rng(0x6000, 0x67ff));
|
||||
|
||||
List<Entry<AddressRange, Which>> expected;
|
||||
List<Entry<AddressRange, Which>> actual;
|
||||
|
||||
expected = List.of(pair(0x3000, 0x37ff, Which.LEFT), pair(0x4000, 0x47ff, Which.RIGHT),
|
||||
pair(0x5000, 0x57ff, Which.LEFT), pair(0x6000, 0x67ff, Which.RIGHT));
|
||||
actual = toList(makeIterator(a, b, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x3000, 0x37ff, Which.RIGHT), pair(0x4000, 0x47ff, Which.LEFT),
|
||||
pair(0x5000, 0x57ff, Which.RIGHT), pair(0x6000, 0x67ff, Which.LEFT));
|
||||
actual = toList(makeIterator(b, a, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x6000, 0x67ff, Which.RIGHT), pair(0x5000, 0x57ff, Which.LEFT),
|
||||
pair(0x4000, 0x47ff, Which.RIGHT), pair(0x3000, 0x37ff, Which.LEFT));
|
||||
actual = toList(makeIterator(a, b, false));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x6000, 0x67ff, Which.LEFT), pair(0x5000, 0x57ff, Which.RIGHT),
|
||||
pair(0x4000, 0x47ff, Which.LEFT), pair(0x3000, 0x37ff, Which.RIGHT));
|
||||
actual = toList(makeIterator(b, a, false));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlapEmptyBetween() {
|
||||
AddressSet a = set(rng(0x3000, 0x4fff), rng(0x7000, 0x8fff));
|
||||
AddressSet b = set(rng(0x4000, 0x5fff), rng(0x8000, 0x9fff));
|
||||
|
||||
List<Entry<AddressRange, Which>> expected;
|
||||
List<Entry<AddressRange, Which>> actual;
|
||||
|
||||
expected = List.of(pair(0x3000, 0x3fff, Which.LEFT), pair(0x4000, 0x4fff, Which.BOTH),
|
||||
pair(0x5000, 0x5fff, Which.RIGHT), pair(0x7000, 0x7fff, Which.LEFT),
|
||||
pair(0x8000, 0x8fff, Which.BOTH), pair(0x9000, 0x9fff, Which.RIGHT));
|
||||
actual = toList(makeIterator(a, b, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x3000, 0x3fff, Which.RIGHT), pair(0x4000, 0x4fff, Which.BOTH),
|
||||
pair(0x5000, 0x5fff, Which.LEFT), pair(0x7000, 0x7fff, Which.RIGHT),
|
||||
pair(0x8000, 0x8fff, Which.BOTH), pair(0x9000, 0x9fff, Which.LEFT));
|
||||
actual = toList(makeIterator(b, a, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x9000, 0x9fff, Which.RIGHT), pair(0x8000, 0x8fff, Which.BOTH),
|
||||
pair(0x7000, 0x7fff, Which.LEFT), pair(0x5000, 0x5fff, Which.RIGHT),
|
||||
pair(0x4000, 0x4fff, Which.BOTH), pair(0x3000, 0x3fff, Which.LEFT));
|
||||
actual = toList(makeIterator(a, b, false));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x9000, 0x9fff, Which.LEFT), pair(0x8000, 0x8fff, Which.BOTH),
|
||||
pair(0x7000, 0x7fff, Which.RIGHT), pair(0x5000, 0x5fff, Which.LEFT),
|
||||
pair(0x4000, 0x4fff, Which.BOTH), pair(0x3000, 0x3fff, Which.RIGHT));
|
||||
actual = toList(makeIterator(b, a, false));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlapMiddle() {
|
||||
AddressSet a = set(rng(0x3000, 0x5fff));
|
||||
AddressSet b = set(rng(0x4000, 0x4fff));
|
||||
|
||||
List<Entry<AddressRange, Which>> expected;
|
||||
List<Entry<AddressRange, Which>> actual;
|
||||
|
||||
expected = List.of(pair(0x3000, 0x3fff, Which.LEFT), pair(0x4000, 0x4fff, Which.BOTH),
|
||||
pair(0x5000, 0x5fff, Which.LEFT));
|
||||
actual = toList(makeIterator(a, b, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x3000, 0x3fff, Which.RIGHT), pair(0x4000, 0x4fff, Which.BOTH),
|
||||
pair(0x5000, 0x5fff, Which.RIGHT));
|
||||
actual = toList(makeIterator(b, a, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x5000, 0x5fff, Which.LEFT), pair(0x4000, 0x4fff, Which.BOTH),
|
||||
pair(0x3000, 0x3fff, Which.LEFT));
|
||||
actual = toList(makeIterator(a, b, false));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x5000, 0x5fff, Which.RIGHT), pair(0x4000, 0x4fff, Which.BOTH),
|
||||
pair(0x3000, 0x3fff, Which.RIGHT));
|
||||
actual = toList(makeIterator(b, a, false));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSame() {
|
||||
AddressSet a = set(rng(0x4000, 0x4fff), rng(0x6000, 0x6fff));
|
||||
AddressSet b = set(rng(0x4000, 0x4fff), rng(0x6000, 0x6fff));
|
||||
|
||||
List<Entry<AddressRange, Which>> expected;
|
||||
List<Entry<AddressRange, Which>> actual;
|
||||
|
||||
expected = List.of(pair(0x4000, 0x4fff, Which.BOTH), pair(0x6000, 0x6fff, Which.BOTH));
|
||||
actual = toList(makeIterator(a, b, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x6000, 0x6fff, Which.BOTH), pair(0x4000, 0x4fff, Which.BOTH));
|
||||
actual = toList(makeIterator(a, b, false));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlapAtExtremes() {
|
||||
AddressSet a = set(rng(0x0000, 0x0fff), rng(-0x2000, -0x0001));
|
||||
AddressSet b = set(rng(0x0000, 0x1fff), rng(-0x1000, -0x0001));
|
||||
|
||||
List<Entry<AddressRange, Which>> expected;
|
||||
List<Entry<AddressRange, Which>> actual;
|
||||
|
||||
expected = List.of(pair(0x0000, 0x0fff, Which.BOTH), pair(0x1000, 0x1fff, Which.RIGHT),
|
||||
pair(-0x2000, -0x1001, Which.LEFT), pair(-0x1000, -0x0001, Which.BOTH));
|
||||
actual = toList(makeIterator(a, b, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x0000, 0x0fff, Which.BOTH), pair(0x1000, 0x1fff, Which.LEFT),
|
||||
pair(-0x2000, -0x1001, Which.RIGHT), pair(-0x1000, -0x0001, Which.BOTH));
|
||||
actual = toList(makeIterator(b, a, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(-0x1000, -0x0001, Which.BOTH), pair(-0x2000, -0x1001, Which.LEFT),
|
||||
pair(0x1000, 0x1fff, Which.RIGHT), pair(0x0000, 0x0fff, Which.BOTH));
|
||||
actual = toList(makeIterator(a, b, false));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(-0x1000, -0x0001, Which.BOTH), pair(-0x2000, -0x1001, Which.RIGHT),
|
||||
pair(0x1000, 0x1fff, Which.LEFT), pair(0x0000, 0x0fff, Which.BOTH));
|
||||
actual = toList(makeIterator(b, a, false));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcrossSpaces() {
|
||||
AddressSet a = set(rng(0x1000, 0x2000));
|
||||
AddressSet b = set(dRng(0x1000, 0x2000));
|
||||
|
||||
List<Entry<AddressRange, Which>> expected;
|
||||
List<Entry<AddressRange, Which>> actual;
|
||||
|
||||
expected = List.of(pair(0x1000, 0x2000, Which.LEFT), dPair(0x1000, 0x2000, Which.RIGHT));
|
||||
actual = toList(makeIterator(a, b, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x1000, 0x2000, Which.RIGHT), dPair(0x1000, 0x2000, Which.LEFT));
|
||||
actual = toList(makeIterator(b, a, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(dPair(0x1000, 0x2000, Which.RIGHT), pair(0x1000, 0x2000, Which.LEFT));
|
||||
actual = toList(makeIterator(a, b, false));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(dPair(0x1000, 0x2000, Which.LEFT), pair(0x1000, 0x2000, Which.RIGHT));
|
||||
actual = toList(makeIterator(b, a, false));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcrossSpacesExtremes() {
|
||||
AddressSet a = set(rng(0x1000, -1L));
|
||||
AddressSet b = set(dRng(0x0, 0xfff));
|
||||
|
||||
List<Entry<AddressRange, Which>> expected;
|
||||
List<Entry<AddressRange, Which>> actual;
|
||||
|
||||
expected = List.of(pair(0x1000, -1L, Which.LEFT), dPair(0x0, 0xfff, Which.RIGHT));
|
||||
actual = toList(makeIterator(a, b, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(pair(0x1000, -1L, Which.RIGHT), dPair(0x0, 0xfff, Which.LEFT));
|
||||
actual = toList(makeIterator(b, a, true));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(dPair(0x0, 0xfff, Which.RIGHT), pair(0x1000, -1L, Which.LEFT));
|
||||
actual = toList(makeIterator(a, b, false));
|
||||
assertEquals(expected, actual);
|
||||
|
||||
expected = List.of(dPair(0x0, 0xfff, Which.LEFT), pair(0x1000, -1L, Which.RIGHT));
|
||||
actual = toList(makeIterator(b, a, false));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandom() {
|
||||
AddressSet a = randomSet();
|
||||
AddressSet b = randomSet();
|
||||
|
||||
runIteratorTest(a, b, true);
|
||||
runIteratorTest(b, a, true);
|
||||
runIteratorTest(a, b, false);
|
||||
runIteratorTest(b, a, false);
|
||||
}
|
||||
|
||||
protected AddressSet randomSet() {
|
||||
Random r = new Random();
|
||||
AddressSet result = new AddressSet();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int len = r.nextInt(0x7ff) + 1;
|
||||
int off = r.nextInt(0x10000 - len);
|
||||
if (r.nextBoolean()) {
|
||||
result.add(rng(off, off + len - 1));
|
||||
}
|
||||
else {
|
||||
result.add(dRng(off, off + len - 1));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void runIteratorTest(AddressSet a, AddressSet b, boolean forward) {
|
||||
AddressSet both = a.intersect(b);
|
||||
AddressSet left = a.subtract(b);
|
||||
AddressSet right = b.subtract(a);
|
||||
|
||||
TwoWayBreakdownAddressRangeIterator it = makeIterator(a, b, forward);
|
||||
while (it.hasNext()) {
|
||||
Entry<AddressRange, Which> next = it.next();
|
||||
AddressSet which;
|
||||
switch (next.getValue()) {
|
||||
case BOTH:
|
||||
which = both;
|
||||
break;
|
||||
case LEFT:
|
||||
which = left;
|
||||
break;
|
||||
case RIGHT:
|
||||
which = right;
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
if (forward) {
|
||||
assertEquals(which.getMinAddress(), next.getKey().getMinAddress());
|
||||
}
|
||||
else {
|
||||
assertEquals(which.getMaxAddress(), next.getKey().getMaxAddress());
|
||||
}
|
||||
which.delete(next.getKey());
|
||||
}
|
||||
|
||||
assertTrue(both.isEmpty());
|
||||
assertTrue(left.isEmpty());
|
||||
assertTrue(right.isEmpty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,354 @@
|
|||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class UnionAddressSetViewTest extends AbstractGTest {
|
||||
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) {
|
||||
return rom.getAddress(offset);
|
||||
}
|
||||
|
||||
protected Address dAddr(long offset) {
|
||||
return ram.getAddress(offset);
|
||||
}
|
||||
|
||||
protected AddressRange rng(long min, long max) {
|
||||
return new AddressRangeImpl(addr(min), addr(max));
|
||||
}
|
||||
|
||||
protected AddressRange drng(long min, long max) {
|
||||
return new AddressRangeImpl(dAddr(min), dAddr(max));
|
||||
}
|
||||
|
||||
protected AddressSet set(AddressRange... ranges) {
|
||||
AddressSet set = new AddressSet();
|
||||
for (AddressRange rng : ranges) {
|
||||
set.add(rng);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
protected List<Address> addrs(long... offsets) {
|
||||
List<Address> result = new ArrayList<>(offsets.length);
|
||||
for (long off : offsets) {
|
||||
result.add(addr(off));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCounts() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView();
|
||||
assertTrue(union.isEmpty());
|
||||
assertEquals(0, union.getNumAddresses());
|
||||
assertEquals(0, union.getNumAddressRanges());
|
||||
|
||||
union = new UnionAddressSetView(new AddressSet());
|
||||
assertTrue(union.isEmpty());
|
||||
assertEquals(0, union.getNumAddresses());
|
||||
assertEquals(0, union.getNumAddressRanges());
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x0fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
union = new UnionAddressSetView(a, b);
|
||||
assertFalse(union.isEmpty());
|
||||
assertEquals(0x2000, union.getNumAddresses());
|
||||
assertEquals(1, union.getNumAddressRanges());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContains() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView();
|
||||
assertFalse(union.contains(addr(0x0800)));
|
||||
assertFalse(union.contains(addr(0x0800), addr(0x1800)));
|
||||
|
||||
union = new UnionAddressSetView(new AddressSet());
|
||||
assertFalse(union.contains(addr(0x0800), addr(0x1800)));
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x0fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
union = new UnionAddressSetView(a, b);
|
||||
assertTrue(union.contains(addr(0x0800)));
|
||||
assertTrue(union.contains(addr(0x1800)));
|
||||
assertFalse(union.contains(addr(0x2800)));
|
||||
assertTrue(union.contains(addr(0x0800), addr(0x1800)));
|
||||
assertFalse(union.contains(addr(0x0800), addr(0x2800)));
|
||||
assertTrue(union.contains(set(rng(0x0800, 0x1800), rng(0x1900, 0x1fff))));
|
||||
assertFalse(union.contains(set(rng(0x0800, 0x1800), rng(0x1900, 0x2000))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndpoints() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView();
|
||||
assertNull(union.getMinAddress());
|
||||
assertNull(union.getMaxAddress());
|
||||
|
||||
union = new UnionAddressSetView(new AddressSet());
|
||||
assertNull(union.getMinAddress());
|
||||
assertNull(union.getMaxAddress());
|
||||
|
||||
AddressSet a = set(rng(0x0000, 0x0fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
union = new UnionAddressSetView(a, b);
|
||||
assertEquals(addr(0x0000), union.getMinAddress());
|
||||
assertEquals(addr(0x1fff), union.getMaxAddress());
|
||||
}
|
||||
|
||||
protected <T> List<T> collect(Iterator<T> it) {
|
||||
List<T> result = new ArrayList<>();
|
||||
while (it.hasNext()) {
|
||||
result.add(it.next());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected AddressSet randomSet() {
|
||||
Random r = new Random();
|
||||
AddressSet result = new AddressSet();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int len = r.nextInt(0x7ff) + 1;
|
||||
int off = r.nextInt(0x10000 - len);
|
||||
result.add(rng(off, off + len - 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterator() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView(set(rng(0x0000, 0x0fff)), set(rng(0x1000, 0x1fff)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(union.iterator(true)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(union.iterator(false)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(union.iterator(addr(0x0800), true)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(union.iterator(addr(0x0800), false)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(union.iterator(addr(0x1800), true)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(union.iterator(addr(0x1800), false)));
|
||||
assertEquals(List.of(), collect(union.iterator(addr(0x2fff), true)));
|
||||
assertEquals(List.of(rng(0x0000, 0x1fff)), collect(union.iterator(addr(0x2fff), false)));
|
||||
|
||||
union = new UnionAddressSetView(set(rng(0x0000, 0x07ff)), set(rng(0x1000, 0x17ff)));
|
||||
assertEquals(List.of(rng(0x0000, 0x07ff), rng(0x1000, 0x17ff)),
|
||||
collect(union.iterator(true)));
|
||||
assertEquals(List.of(rng(0x1000, 0x17ff), rng(0x0000, 0x07ff)),
|
||||
collect(union.iterator(false)));
|
||||
|
||||
union = new UnionAddressSetView(set(rng(0x80000000, 0xffff0fff)),
|
||||
set(rng(0xffff1000, 0xffffffff)));
|
||||
assertEquals(List.of(rng(0x80000000, 0xffffffff)), collect(union.iterator(true)));
|
||||
|
||||
AddressSet a = randomSet();
|
||||
AddressSet b = randomSet();
|
||||
try {
|
||||
union = new UnionAddressSetView(a, b);
|
||||
AddressSetView expected = a.union(b);
|
||||
assertEquals(collect(expected.getAddressRanges(true)), collect(union.iterator(true)));
|
||||
assertEquals(collect(expected.getAddressRanges(false)), collect(union.iterator(false)));
|
||||
assertEquals(collect(expected.getAddressRanges(addr(0x8000), true)),
|
||||
collect(union.iterator(addr(0x8000), true)));
|
||||
assertEquals(collect(expected.getAddressRanges(addr(0x8000), false)),
|
||||
collect(union.iterator(addr(0x8000), false)));
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
System.out.println("Failing sets: ");
|
||||
System.out.println(" A: " + a);
|
||||
System.out.println(" B: " + b);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIteratorMultipleSpaces() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView(set(rng(0x0000, 0x0fff)), set(drng(0x1000, 0x1fff)));
|
||||
assertEquals(List.of(rng(0x0000, 0x0fff), drng(0x1000, 0x1fff)),
|
||||
collect(union.iterator(true)));
|
||||
|
||||
union =
|
||||
new UnionAddressSetView(set(rng(0x80000000, 0xffffffff)), set(drng(0x0000, 0x0fff)));
|
||||
assertEquals(List.of(rng(0x80000000, 0xffffffff), drng(0x0000, 0x0fff)),
|
||||
collect(union.iterator(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAddresses() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView();
|
||||
assertFalse(union.getAddresses(true).hasNext());
|
||||
assertFalse(union.getAddresses(false).hasNext());
|
||||
|
||||
union = new UnionAddressSetView(new AddressSet());
|
||||
assertFalse(union.getAddresses(true).hasNext());
|
||||
assertFalse(union.getAddresses(false).hasNext());
|
||||
|
||||
union = new UnionAddressSetView(set(rng(1, 3), rng(8, 9)), set(rng(2, 4), rng(6, 7)));
|
||||
assertEquals(addrs(1, 2, 3, 4, 6, 7, 8, 9), collect(union.getAddresses(true)));
|
||||
assertEquals(addrs(9, 8, 7, 6, 4, 3, 2, 1), collect(union.getAddresses(false)));
|
||||
assertEquals(addrs(3, 4, 6, 7, 8, 9), collect(union.getAddresses(addr(3), true)));
|
||||
assertEquals(addrs(6, 7, 8, 9), collect(union.getAddresses(addr(5), true)));
|
||||
assertEquals(addrs(8, 7, 6, 4, 3, 2, 1), collect(union.getAddresses(addr(8), false)));
|
||||
assertEquals(addrs(4, 3, 2, 1), collect(union.getAddresses(addr(5), false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRangeContaining() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView();
|
||||
assertNull(union.getRangeContaining(addr(0x0800)));
|
||||
|
||||
union = new UnionAddressSetView(new AddressSet());
|
||||
assertNull(union.getRangeContaining(addr(0x0800)));
|
||||
|
||||
AddressSet a = set(rng(0x0100, 0x0fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
union = new UnionAddressSetView(a, b);
|
||||
assertNull(union.getRangeContaining(addr(0x0000)));
|
||||
assertNull(union.getRangeContaining(addr(0x0000)));
|
||||
assertNull(union.getRangeContaining(addr(0x00ff)));
|
||||
assertEquals(rng(0x0100, 0x1fff), union.getRangeContaining(addr(0x0100)));
|
||||
assertEquals(rng(0x0100, 0x1fff), union.getRangeContaining(addr(0x0800)));
|
||||
assertEquals(rng(0x0100, 0x1fff), union.getRangeContaining(addr(0x1fff)));
|
||||
assertNull(union.getRangeContaining(addr(0x2000)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasSameAddresses() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView();
|
||||
assertTrue(union.hasSameAddresses(new AddressSet()));
|
||||
|
||||
union = new UnionAddressSetView(new AddressSet());
|
||||
assertTrue(union.hasSameAddresses(new AddressSet()));
|
||||
|
||||
AddressSet a = set(rng(0x0100, 0x0fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff));
|
||||
union = new UnionAddressSetView(a, b);
|
||||
assertFalse(union.hasSameAddresses(new AddressSet()));
|
||||
assertTrue(union.hasSameAddresses(set(rng(0x0100, 0x1fff))));
|
||||
assertFalse(union.hasSameAddresses(set(rng(0x0000, 0x1fff))));
|
||||
assertFalse(union.hasSameAddresses(set(rng(0x0100, 0x2fff))));
|
||||
assertFalse(union.hasSameAddresses(set(rng(0x0100, 0x1fff), rng(0x3000, 0x3fff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFirstLastRanges() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView();
|
||||
assertNull(union.getFirstRange());
|
||||
assertNull(union.getLastRange());
|
||||
|
||||
union = new UnionAddressSetView(new AddressSet());
|
||||
assertNull(union.getFirstRange());
|
||||
assertNull(union.getLastRange());
|
||||
|
||||
AddressSet a = set(rng(0x0100, 0x0fff), rng(0x3000, 0x3fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff), rng(0x4000, 0x4fff));
|
||||
union = new UnionAddressSetView(a, b);
|
||||
assertEquals(rng(0x0100, 0x1fff), union.getFirstRange());
|
||||
assertEquals(rng(0x3000, 0x4fff), union.getLastRange());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersect() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView();
|
||||
assertFalse(union.intersects(addr(0x1000), addr(0x1fff)));
|
||||
assertEquals(new AddressSet(), union.intersectRange(addr(0x1000), addr(0x1fff)));
|
||||
|
||||
union = new UnionAddressSetView(new AddressSet());
|
||||
assertFalse(union.intersects(addr(0x1000), addr(0x1fff)));
|
||||
assertEquals(new AddressSet(), union.intersectRange(addr(0x1000), addr(0x1fff)));
|
||||
|
||||
AddressSet a = set(rng(0x0100, 0x0fff), rng(0x3000, 0x3fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff), rng(0x4000, 0x4fff));
|
||||
union = new UnionAddressSetView(a, b);
|
||||
assertFalse(union.intersects(addr(0x0000), addr(0x00ff)));
|
||||
assertEquals(new AddressSet(), union.intersectRange(addr(0x0000), addr(0x00ff)));
|
||||
assertFalse(union.intersects(addr(0x5000), addr(0x5fff)));
|
||||
assertEquals(new AddressSet(), union.intersectRange(addr(0x5000), addr(0x5fff)));
|
||||
assertTrue(union.intersects(addr(0x0000), addr(0x0fff)));
|
||||
assertEquals(set(rng(0x0100, 0x0fff)), union.intersectRange(addr(0x0000), addr(0x0fff)));
|
||||
assertTrue(union.intersects(addr(0x4000), addr(0x5fff)));
|
||||
assertEquals(set(rng(0x4000, 0x4fff)), union.intersectRange(addr(0x4000), addr(0x4fff)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnion() {
|
||||
AddressSet a = set(rng(0x0100, 0x0fff), rng(0x3000, 0x3fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff), rng(0x4000, 0x4fff));
|
||||
AddressSetView union = new UnionAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0100, 0x4fff)), union.union(set(rng(0x2000, 0x2fff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtract() {
|
||||
AddressSet a = set(rng(0x0100, 0x0fff), rng(0x3000, 0x3fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff), rng(0x4000, 0x4fff));
|
||||
AddressSetView union = new UnionAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0100, 0x17ff), rng(0x3800, 0x4fff)),
|
||||
union.subtract(set(rng(0x1800, 0x37ff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXor() {
|
||||
AddressSet a = set(rng(0x0100, 0x0fff), rng(0x3000, 0x3fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff), rng(0x4000, 0x4fff));
|
||||
AddressSetView union = new UnionAddressSetView(a, b);
|
||||
assertEquals(set(rng(0x0100, 0x17ff), rng(0x2000, 0x2fff), rng(0x3800, 0x4fff)),
|
||||
union.xor(set(rng(0x1800, 0x37ff))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindFirstAddressInCommon() {
|
||||
AddressSetView union;
|
||||
|
||||
union = new UnionAddressSetView();
|
||||
assertNull(union.findFirstAddressInCommon(set(rng(0x1000, 0x1fff))));
|
||||
|
||||
union = new UnionAddressSetView(new AddressSet());
|
||||
assertNull(union.findFirstAddressInCommon(set(rng(0x1000, 0x1fff))));
|
||||
|
||||
AddressSet a = set(rng(0x0100, 0x0fff), rng(0x3000, 0x3fff));
|
||||
AddressSet b = set(rng(0x1000, 0x1fff), rng(0x4000, 0x4fff));
|
||||
union = new UnionAddressSetView(a, b);
|
||||
assertNull(union.findFirstAddressInCommon(set(rng(0x2000, 0x2fff))));
|
||||
assertEquals(addr(0x0800), union.findFirstAddressInCommon(set(rng(0x0800, 0x1fff))));
|
||||
assertEquals(addr(0x3000), union.findFirstAddressInCommon(set(rng(0x2000, 0x37ff))));
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue