Merge remote-tracking branch 'origin/GP-5864_Dan_addEmulatorUtilities--SQUASHED'

This commit is contained in:
Ryan Kurtz 2025-08-25 05:41:08 -04:00
commit 12ac4660a1
43 changed files with 986 additions and 269 deletions

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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());
}
};
}

View file

@ -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()));
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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))));
}
}

View file

@ -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))));
}
}

View file

@ -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());
}
}

View file

@ -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))));
}
}

View file

@ -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());
}
}

View file

@ -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))));
}
}