mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-134: Mainline changes cherry-picked from Debugger branch
This commit is contained in:
parent
192aba987a
commit
724df5a44c
60 changed files with 3855 additions and 1770 deletions
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,72 @@
|
|||
/* ###
|
||||
* 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.generic.util.datastruct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An interface for sorted lists
|
||||
*
|
||||
* <p>
|
||||
* This might be better described as a NavigableMultiset; however, I wish for the elements to be
|
||||
* retrievable by index, though insertion and mutation is not permitted by index. This implies that
|
||||
* though unordered, the underlying implementation has sorted the elements in some way and wishes to
|
||||
* expose that ordering to its clients.
|
||||
*
|
||||
* @param <E> the type of elements in this list
|
||||
*/
|
||||
public interface SortedList<E> extends List<E> {
|
||||
/**
|
||||
* Returns the greatest index in this list whose element is strictly less than the specified
|
||||
* element
|
||||
*
|
||||
* @param element the element to search for
|
||||
* @return the index of the found element, or -1
|
||||
*/
|
||||
int lowerIndex(E element);
|
||||
|
||||
/**
|
||||
* Returns the greatest index in this list whose element is less than or equal to the specified
|
||||
* element
|
||||
*
|
||||
* <p>
|
||||
* If multiples of the specified element exist, this returns the least index of that element.
|
||||
*
|
||||
* @param element the element to search for
|
||||
* @return the index of the found element, or -1
|
||||
*/
|
||||
int floorIndex(E element);
|
||||
|
||||
/**
|
||||
* Returns the least index in this list whose element is greater than or equal to the specified
|
||||
* element
|
||||
*
|
||||
* <p>
|
||||
* If multiples of the specified element exist, this returns the greatest index of that element.
|
||||
*
|
||||
* @param element the element to search for
|
||||
* @return the index of the found element, or -1
|
||||
*/
|
||||
int ceilingIndex(E element);
|
||||
|
||||
/**
|
||||
* Returns the least index in this list whose element is strictly greater the specified element
|
||||
*
|
||||
* @param element the element to search for
|
||||
* @return the index of the found element, or -1
|
||||
*/
|
||||
int higherIndex(E element);
|
||||
}
|
|
@ -15,14 +15,13 @@
|
|||
*/
|
||||
package ghidra.generic.util.datastruct;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.collections4.multimap.AbstractSetValuedMap;
|
||||
|
||||
/**
|
||||
* A multi-valued dictionary using a tree map of tree sets
|
||||
* A multi-valued map using a tree map of tree sets
|
||||
*
|
||||
* @param <K> the type of key
|
||||
* @param <V> the type of value
|
||||
*/
|
||||
|
|
|
@ -22,36 +22,43 @@ import org.apache.commons.collections4.comparators.ComparableComparator;
|
|||
import ghidra.util.ReversedListIterator;
|
||||
|
||||
/**
|
||||
* A map that is sorted by value.
|
||||
*
|
||||
* This is an implementation of {@link Map} where entries are sorted by value, rather than by key.
|
||||
* Such a tree may be useful as a priority queue where the cost of an entry may change over time.
|
||||
* As such, the collections returned by {@link #entrySet()}, {@link #keySet()}, and
|
||||
* {@link #values()} all implement {@link Deque}. The order of the entries will be updated on any
|
||||
* call to {@link #put(Object, Object)}, or a call to {@link Collection#add(Object)} on the entry
|
||||
* set. Additionally, if the values are mutable objects, whose costs may change, there is an
|
||||
* {@link #update(Object)} method, which notifies the map that the given key may need to be
|
||||
* repositioned. The associated collections also implement the {@link List} interface, providing
|
||||
* fairly efficient implementations of {@link List#get(int)} and {@link List#indexOf(Object)}.
|
||||
* Sequential access is best performed via {@link Collection#iterator()}, since this will use a
|
||||
* linked list.
|
||||
*
|
||||
* A tree-based implementation of a value-sorted map
|
||||
*
|
||||
* The underlying implementation is currently an unbalanced binary tree whose nodes also comprise a
|
||||
* doubly-linked list. Currently, it is not thread safe.
|
||||
* TODO Consider changing to an AVL tree implementation
|
||||
* TODO Consider implementing the {@link NavigableMap} interface
|
||||
* TODO Consider making the implementation thread-safe
|
||||
*
|
||||
* Note this implementation isn't terribly smart, as it makes no efforts to balance the tree. It is
|
||||
* also not thread safe.
|
||||
*
|
||||
* @param <K> the type of the keys
|
||||
* @param <V> the type of the values
|
||||
*/
|
||||
public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
||||
public class TreeValueSortedMap<K, V> extends AbstractMap<K, V> implements ValueSortedMap<K, V> {
|
||||
|
||||
/**
|
||||
* Create a tree using the values' natural ordering
|
||||
*/
|
||||
public static <K, V extends Comparable<V>> TreeValueSortedMap<K, V> createWithNaturalOrder() {
|
||||
Comparator<V> natural = Comparator.naturalOrder();
|
||||
return new TreeValueSortedMap<>(natural);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tree using a custom comparator to order the values
|
||||
*
|
||||
* @param comparator the comparator, providing a total ordering of the values
|
||||
*/
|
||||
public static <K, V> TreeValueSortedMap<K, V> createWithComparator(Comparator<V> comparator) {
|
||||
return new TreeValueSortedMap<>(comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* An iterator of the entries
|
||||
*/
|
||||
protected class EntryListIterator implements ListIterator<Entry<K, V>> {
|
||||
private boolean atEnd = false;
|
||||
private Node next;
|
||||
private Node cur;
|
||||
|
||||
/**
|
||||
* Construct a list iterator over the entries
|
||||
|
@ -88,7 +95,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
Node cur = next;
|
||||
cur = next;
|
||||
next = next.next;
|
||||
atEnd = next == null;
|
||||
return cur;
|
||||
|
@ -109,9 +116,9 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
atEnd = tail == null;
|
||||
}
|
||||
else {
|
||||
next = next.prev;
|
||||
cur = next = next.prev;
|
||||
}
|
||||
return next;
|
||||
return cur;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -124,8 +131,12 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public void remove() {
|
||||
nodeMap.remove(next.key);
|
||||
next.remove();
|
||||
if (cur == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
nodeMap.remove(cur.key);
|
||||
cur.remove();
|
||||
cur = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -204,18 +215,18 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
*/
|
||||
protected class Node implements Entry<K, V> {
|
||||
// Node key and data
|
||||
private final K key;
|
||||
private V val;
|
||||
protected final K key;
|
||||
protected V val;
|
||||
|
||||
// Tree-related fields
|
||||
private Node parent;
|
||||
private Node lChild;
|
||||
private Node rChild;
|
||||
private int sizeLeft;
|
||||
protected Node parent;
|
||||
protected Node lChild;
|
||||
protected Node rChild;
|
||||
protected int sizeLeft;
|
||||
|
||||
// Linked list-related fields
|
||||
private Node next;
|
||||
private Node prev;
|
||||
protected Node next;
|
||||
protected Node prev;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
@ -224,10 +235,11 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
/**
|
||||
* Construct a new node
|
||||
*
|
||||
* @param key the key
|
||||
* @param val the data
|
||||
*/
|
||||
private Node(K key, V val) {
|
||||
protected Node(K key, V val) {
|
||||
this.key = key;
|
||||
this.val = val;
|
||||
}
|
||||
|
@ -237,17 +249,24 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Entry<K, V> that = (Entry<K, V>) obj;
|
||||
return eq(this.key, that.getKey()) && eq(this.val, that.getValue());
|
||||
return Objects.equals(this.key, that.getKey()) &&
|
||||
Objects.equals(this.val, that.getValue());
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.key, this.val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute this node's index.
|
||||
*
|
||||
* This uses the {@link #sizeLeft} field to compute the index in O(log n) on average.
|
||||
*
|
||||
* @return the index
|
||||
*/
|
||||
public int computeIndex() {
|
||||
|
@ -268,7 +287,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
*
|
||||
* This really only makes sense at the root
|
||||
*
|
||||
* @param index the index
|
||||
* @param index the index
|
||||
* @return the node at the given index
|
||||
*/
|
||||
private Node getByIndex(int index) {
|
||||
|
@ -310,6 +329,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
/**
|
||||
* Insert a node into this subtree and the linked list
|
||||
*
|
||||
* @param item the node to insert
|
||||
*/
|
||||
void insert(Node item) {
|
||||
|
@ -340,6 +360,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
/**
|
||||
* Insert a node as a successor to this node in the linked list
|
||||
*
|
||||
* NOTE: Called only after the node is inserted into the tree
|
||||
*/
|
||||
private void insertAfter(Node item) {
|
||||
|
@ -357,6 +378,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
/**
|
||||
* Insert a node as a predecessor to this node in the linked list
|
||||
*
|
||||
* NOTE: Called only after the node is inserted into the tree
|
||||
*/
|
||||
private void insertBefore(Node item) {
|
||||
|
@ -456,34 +478,57 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
/**
|
||||
* Find the given value in this subtree
|
||||
*
|
||||
* @param val the value to find
|
||||
* @param value the value to find
|
||||
* @param mode when the value occurs multiple times, identifies which instance to find
|
||||
* @return the node containing the given value, or null if not found
|
||||
*/
|
||||
private Node searchValue(V val, SearchMode mode) {
|
||||
private Node searchValue(V value, SearchMode mode) {
|
||||
Node cur = this;
|
||||
Node eq = null;
|
||||
int c;
|
||||
while (true) {
|
||||
int c = comparator.compare(val, cur.val);
|
||||
c = comparator.compare(value, cur.val);
|
||||
if (c == 0) {
|
||||
eq = cur;
|
||||
}
|
||||
if (c < 0 || (c == 0 && mode == SearchMode.FIRST)) {
|
||||
if (c < 0 || (c == 0 && mode.inEq == Comp.LT)) {
|
||||
if (cur.lChild == null) {
|
||||
return eq;
|
||||
break;
|
||||
}
|
||||
cur = cur.lChild;
|
||||
}
|
||||
else if (c > 0 || (c == 0 && mode == SearchMode.LAST)) {
|
||||
else if (c > 0 || (c == 0 && mode.inEq == Comp.GT)) {
|
||||
if (cur.rChild == null) {
|
||||
return eq;
|
||||
break;
|
||||
}
|
||||
cur = cur.rChild;
|
||||
}
|
||||
else { // c == 0 && mode == SearchMode.ANY
|
||||
return eq;
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (eq != null) {
|
||||
if (mode.allowEq == BoundType.CLOSED) {
|
||||
return eq;
|
||||
}
|
||||
if (mode.comp == Comp.LT) {
|
||||
return eq.prev;
|
||||
}
|
||||
return eq.next;
|
||||
}
|
||||
if (mode.comp == Comp.LT) {
|
||||
if (c < 0) {
|
||||
return cur.prev; // May be null;
|
||||
}
|
||||
return cur; // c != 0 here, so c > 0
|
||||
}
|
||||
if (mode.comp == Comp.GT) {
|
||||
if (c < 0) {
|
||||
return cur;
|
||||
}
|
||||
return cur.next;
|
||||
}
|
||||
return null; // Other search modes require exact match
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -495,19 +540,42 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
private enum BoundType {
|
||||
CLOSED, OPEN
|
||||
}
|
||||
|
||||
private enum Comp {
|
||||
NONE, LT, GT
|
||||
}
|
||||
|
||||
/**
|
||||
* When searching for values, identifies which instance to find
|
||||
*
|
||||
* TODO When/if implementing {@link NavigableMap}, this seems an appropriate place to put
|
||||
* FLOOR, CEILING, etc.
|
||||
*/
|
||||
private enum SearchMode {
|
||||
/** Find any occurrence */
|
||||
ANY,
|
||||
ANY(BoundType.CLOSED, Comp.NONE, Comp.NONE),
|
||||
/** Find the first occurrence */
|
||||
FIRST,
|
||||
FIRST(BoundType.CLOSED, Comp.LT, Comp.NONE),
|
||||
/** Find the last occurrence */
|
||||
LAST;
|
||||
LAST(BoundType.CLOSED, Comp.GT, Comp.NONE),
|
||||
/** Find the nearest match less than */
|
||||
LOWER(BoundType.OPEN, Comp.NONE, Comp.LT),
|
||||
/** Find the nearest match less than or equal */
|
||||
FLOOR(BoundType.CLOSED, Comp.LT, Comp.LT),
|
||||
/** Find the nearest match greater than or equal */
|
||||
CEILING(BoundType.CLOSED, Comp.GT, Comp.GT),
|
||||
/** Find the nearest match greater than */
|
||||
HIGHER(BoundType.OPEN, Comp.NONE, Comp.GT);
|
||||
|
||||
final BoundType allowEq;
|
||||
final Comp inEq;
|
||||
final Comp comp;
|
||||
|
||||
SearchMode(BoundType allowEq, Comp inEq, Comp comp) {
|
||||
this.allowEq = allowEq;
|
||||
this.inEq = inEq;
|
||||
this.comp = comp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -577,13 +645,13 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
* A public view of the map as a set of entries
|
||||
*
|
||||
* In addition to {@link Set}, this view implements {@link List} and {@link Deque}, since an
|
||||
* ordered set ought to behave like a list, and since this implementation is meant to be used
|
||||
* as a dynamic-cost priority queue.
|
||||
* ordered set ought to behave like a list, and since this implementation is meant to be used as
|
||||
* a dynamic-cost priority queue.
|
||||
*
|
||||
* Generally, all of the mutation methods are supported.
|
||||
*/
|
||||
public class ValueSortedTreeMapEntrySet extends AbstractSet<Entry<K, V>>
|
||||
implements List<Entry<K, V>>, Deque<Entry<K, V>> {
|
||||
protected class ValueSortedTreeMapEntrySet extends AbstractSet<Entry<K, V>>
|
||||
implements ValueSortedMapEntryList<K, V> {
|
||||
private ValueSortedTreeMapEntrySet() {
|
||||
}
|
||||
|
||||
|
@ -633,7 +701,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public void clear() {
|
||||
DynamicValueSortedTreeMap.this.clear();
|
||||
TreeValueSortedMap.this.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -645,7 +713,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
@SuppressWarnings("unchecked")
|
||||
Node n = (Node) o;
|
||||
Node m = nodeMap.get(n.key);
|
||||
return eq(n.val, m.val);
|
||||
return Objects.equals(n.val, m.val);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
return false;
|
||||
|
@ -658,18 +726,18 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Node element() {
|
||||
public Entry<K, V> element() {
|
||||
return getFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node get(int index) {
|
||||
public Entry<K, V> get(int index) {
|
||||
return root.getByIndex(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getFirst() {
|
||||
Node ret = peekFirst();
|
||||
public Entry<K, V> getFirst() {
|
||||
Entry<K, V> ret = peekFirst();
|
||||
if (ret == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
@ -677,8 +745,8 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Node getLast() {
|
||||
Node ret = peekLast();
|
||||
public Entry<K, V> getLast() {
|
||||
Entry<K, V> ret = peekLast();
|
||||
if (ret == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
@ -722,6 +790,9 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public ListIterator<Entry<K, V>> listIterator(int index) {
|
||||
if (root == null) {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
return new EntryListIterator(root.getByIndex(index));
|
||||
}
|
||||
|
||||
|
@ -747,27 +818,27 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Node peek() {
|
||||
public Entry<K, V> peek() {
|
||||
return peekFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node peekFirst() {
|
||||
public Entry<K, V> peekFirst() {
|
||||
return head;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node peekLast() {
|
||||
public Entry<K, V> peekLast() {
|
||||
return tail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node poll() {
|
||||
public Entry<K, V> poll() {
|
||||
return pollFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node pollFirst() {
|
||||
public Entry<K, V> pollFirst() {
|
||||
if (head == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -778,7 +849,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Node pollLast() {
|
||||
public Entry<K, V> pollLast() {
|
||||
if (tail == null) {
|
||||
return tail;
|
||||
}
|
||||
|
@ -789,7 +860,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Node pop() {
|
||||
public Entry<K, V> pop() {
|
||||
return removeFirst();
|
||||
}
|
||||
|
||||
|
@ -799,12 +870,12 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Node remove() {
|
||||
public Entry<K, V> remove() {
|
||||
return removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node remove(int index) {
|
||||
public Entry<K, V> remove(int index) {
|
||||
Node n = root.getByIndex(index);
|
||||
n.remove();
|
||||
nodeMap.remove(n.key);
|
||||
|
@ -822,7 +893,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
nodeMap.remove(n.key);
|
||||
return true;
|
||||
}
|
||||
if (eq(n.val, rm.val)) {
|
||||
if (Objects.equals(n.val, rm.val)) {
|
||||
nodeMap.remove(rm.key);
|
||||
rm.remove();
|
||||
return true;
|
||||
|
@ -835,8 +906,8 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Node removeFirst() {
|
||||
Node ret = pollFirst();
|
||||
public Entry<K, V> removeFirst() {
|
||||
Entry<K, V> ret = pollFirst();
|
||||
if (ret == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
@ -849,8 +920,8 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Node removeLast() {
|
||||
Node ret = pollLast();
|
||||
public Entry<K, V> removeLast() {
|
||||
Entry<K, V> ret = pollLast();
|
||||
if (ret == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
@ -865,13 +936,13 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
/**
|
||||
* Modify the entry (key and value) at index
|
||||
*
|
||||
* Because the map is sorted by value, the index of the given entry may not remain the
|
||||
* same after it is modified. In fact, this is equivalent to removing the entry at the
|
||||
* given index, and then inserting the given entry at its sorted position.
|
||||
* Because the map is sorted by value, the index of the given entry may not remain the same
|
||||
* after it is modified. In fact, this is equivalent to removing the entry at the given
|
||||
* index, and then inserting the given entry at its sorted position.
|
||||
*/
|
||||
@Override
|
||||
public Node set(int index, Entry<K, V> element) {
|
||||
Node result = remove(index);
|
||||
public Entry<K, V> set(int index, Entry<K, V> element) {
|
||||
Entry<K, V> result = remove(index);
|
||||
add(element);
|
||||
return result;
|
||||
}
|
||||
|
@ -881,11 +952,6 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
return nodeMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<Entry<K, V>> spliterator() {
|
||||
return Spliterators.spliterator(this, Spliterator.ORDERED | Spliterator.DISTINCT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This operation is not supported
|
||||
*/
|
||||
|
@ -899,12 +965,13 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
* A public view of the map as a set of keys
|
||||
*
|
||||
* In addition to {@link Set}, this view implements {@link List} and {@link Deque}, since an
|
||||
* ordered set ought to behave like a list, and since this implementation is meant to be used
|
||||
* as a dynamic-cost priority queue.
|
||||
* ordered set ought to behave like a list, and since this implementation is meant to be used as
|
||||
* a dynamic-cost priority queue.
|
||||
*
|
||||
* Generally, only the removal mutation methods are supported, all others are not supported.
|
||||
*/
|
||||
public class ValueSortedTreeMapKeySet extends AbstractSet<K> implements List<K>, Deque<K> {
|
||||
protected class ValueSortedTreeMapKeySet extends AbstractSet<K>
|
||||
implements ValueSortedMapKeyList<K> {
|
||||
private ValueSortedTreeMapKeySet() {
|
||||
}
|
||||
|
||||
|
@ -940,7 +1007,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public void clear() {
|
||||
DynamicValueSortedTreeMap.this.clear();
|
||||
TreeValueSortedMap.this.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -960,17 +1027,17 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public K get(int index) {
|
||||
return entrySet.get(index).key;
|
||||
return entrySet.get(index).getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getFirst() {
|
||||
return entrySet.getFirst().key;
|
||||
return entrySet.getFirst().getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getLast() {
|
||||
return entrySet.getLast().key;
|
||||
return entrySet.getLast().getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1029,20 +1096,20 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public K peekFirst() {
|
||||
Node n = entrySet.peekFirst();
|
||||
Entry<K, V> n = entrySet.peekFirst();
|
||||
if (n == null) {
|
||||
return null;
|
||||
}
|
||||
return n.key;
|
||||
return n.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K peekLast() {
|
||||
Node n = entrySet.peekLast();
|
||||
Entry<K, V> n = entrySet.peekLast();
|
||||
if (n == null) {
|
||||
return null;
|
||||
}
|
||||
return n.key;
|
||||
return n.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1052,20 +1119,20 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public K pollFirst() {
|
||||
Node n = entrySet.pollFirst();
|
||||
Entry<K, V> n = entrySet.pollFirst();
|
||||
if (n == null) {
|
||||
return null;
|
||||
}
|
||||
return n.key;
|
||||
return n.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K pollLast() {
|
||||
Node n = entrySet.pollLast();
|
||||
Entry<K, V> n = entrySet.pollLast();
|
||||
if (n == null) {
|
||||
return null;
|
||||
}
|
||||
return n.key;
|
||||
return n.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1085,32 +1152,32 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public K remove(int index) {
|
||||
return entrySet.remove(index).key;
|
||||
return entrySet.remove(index).getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return DynamicValueSortedTreeMap.this.remove(o) != null;
|
||||
return TreeValueSortedMap.this.remove(o) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K removeFirst() {
|
||||
return entrySet.removeFirst().key;
|
||||
return entrySet.removeFirst().getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFirstOccurrence(Object o) {
|
||||
return DynamicValueSortedTreeMap.this.remove(o) != null;
|
||||
return TreeValueSortedMap.this.remove(o) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K removeLast() {
|
||||
return entrySet.removeLast().key;
|
||||
return entrySet.removeLast().getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeLastOccurrence(Object o) {
|
||||
return DynamicValueSortedTreeMap.this.remove(o) != null;
|
||||
return TreeValueSortedMap.this.remove(o) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1123,11 +1190,6 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
return nodeMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<K> spliterator() {
|
||||
return Spliterators.spliterator(this, Spliterator.ORDERED | Spliterator.DISTINCT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This operation is not supported
|
||||
*/
|
||||
|
@ -1140,14 +1202,14 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
/**
|
||||
* A public view of the map as a list of values
|
||||
*
|
||||
* This view implements {@link List} and {@link Deque}, since an ordered collection ought to
|
||||
* behave like a list, and since this implementation is meant to be used as a dynamic-cost
|
||||
* This view implements {@link SortedList} and {@link Deque}, since an ordered collection ought
|
||||
* to behave like a list, and since this implementation is meant to be used as a dynamic-cost
|
||||
* priority queue.
|
||||
*
|
||||
* Generally, only the removal mutation methods are supported, all others are not supported.
|
||||
*/
|
||||
public class ValueSortedTreeMapValues extends AbstractCollection<V>
|
||||
implements List<V>, Deque<V> {
|
||||
protected class ValueSortedTreeMapValues extends AbstractCollection<V>
|
||||
implements SortedList<V>, Deque<V> {
|
||||
private ValueSortedTreeMapValues() {
|
||||
}
|
||||
|
||||
|
@ -1183,7 +1245,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public void clear() {
|
||||
DynamicValueSortedTreeMap.this.clear();
|
||||
TreeValueSortedMap.this.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1210,17 +1272,17 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public V get(int index) {
|
||||
return entrySet.get(index).val;
|
||||
return entrySet.get(index).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getFirst() {
|
||||
return entrySet.getFirst().val;
|
||||
return entrySet.getFirst().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getLast() {
|
||||
return entrySet.getLast().val;
|
||||
return entrySet.getLast().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1228,7 +1290,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
V val = (V) o;
|
||||
Node n = root.searchValue(val, SearchMode.FIRST);
|
||||
Node n = searchValue(val, SearchMode.FIRST);
|
||||
if (n == null) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -1239,6 +1301,45 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lowerIndex(V element) {
|
||||
if (root == null) {
|
||||
return -1;
|
||||
}
|
||||
Node n = searchValue(element, SearchMode.LOWER);
|
||||
if (n == null) {
|
||||
return -1;
|
||||
}
|
||||
return n.computeIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int floorIndex(V element) {
|
||||
Node n = searchValue(element, SearchMode.FLOOR);
|
||||
if (n == null) {
|
||||
return -1;
|
||||
}
|
||||
return n.computeIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int ceilingIndex(V element) {
|
||||
Node n = searchValue(element, SearchMode.CEILING);
|
||||
if (n == null) {
|
||||
return -1;
|
||||
}
|
||||
return n.computeIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int higherIndex(V element) {
|
||||
Node n = searchValue(element, SearchMode.HIGHER);
|
||||
if (n == null) {
|
||||
return -1;
|
||||
}
|
||||
return n.computeIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return root == null;
|
||||
|
@ -1297,20 +1398,20 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public V peekFirst() {
|
||||
Node n = entrySet.peekFirst();
|
||||
Entry<K, V> n = entrySet.peekFirst();
|
||||
if (n == null) {
|
||||
return null;
|
||||
}
|
||||
return n.val;
|
||||
return n.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V peekLast() {
|
||||
Node n = entrySet.peekLast();
|
||||
Entry<K, V> n = entrySet.peekLast();
|
||||
if (n == null) {
|
||||
return null;
|
||||
}
|
||||
return n.val;
|
||||
return n.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1320,20 +1421,20 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public V pollFirst() {
|
||||
Node n = entrySet.pollFirst();
|
||||
Entry<K, V> n = entrySet.pollFirst();
|
||||
if (n == null) {
|
||||
return null;
|
||||
}
|
||||
return n.val;
|
||||
return n.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V pollLast() {
|
||||
Node n = entrySet.pollLast();
|
||||
Entry<K, V> n = entrySet.pollLast();
|
||||
if (n == null) {
|
||||
return null;
|
||||
}
|
||||
return n.val;
|
||||
return n.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1353,7 +1454,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public V remove(int index) {
|
||||
return entrySet.remove(index).val;
|
||||
return entrySet.remove(index).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1363,7 +1464,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public V removeFirst() {
|
||||
return entrySet.removeFirst().val;
|
||||
return entrySet.removeFirst().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1386,7 +1487,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
@Override
|
||||
public V removeLast() {
|
||||
return entrySet.removeLast().val;
|
||||
return entrySet.removeLast().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1426,53 +1527,61 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience for null-safe comparison
|
||||
*/
|
||||
protected static boolean eq(Object o1, Object o2) {
|
||||
return o1 == null ? o2 == null : o1.equals(o2);
|
||||
}
|
||||
|
||||
// The user-provided comparator
|
||||
private final Comparator<V> comparator;
|
||||
protected final Comparator<V> comparator;
|
||||
// A hash map to locate entries by key
|
||||
private final Map<K, Node> nodeMap = new HashMap<>();
|
||||
/* Remember, the tree is indexed by *value*, not by key, and more specifically, they are
|
||||
* indexed by the comparator, so an entry's cost may change at any time. Thus, this map
|
||||
* provides an index by key. This is especially important during an update, since we need to
|
||||
* locate the affected node, given that it's most likely not in its correct position at the
|
||||
* moment. We also use it to ensure each key occurs at most once. */
|
||||
protected final Map<K, Node> nodeMap = new HashMap<>();
|
||||
/*
|
||||
* Remember, the tree is indexed by *value*, not by key, and more specifically, they are indexed
|
||||
* by the comparator, so an entry's cost may change at any time. Thus, this map provides an
|
||||
* index by key. This is especially important during an update, since we need to locate the
|
||||
* affected node, given that it's most likely not in its correct position at the moment. We also
|
||||
* use it to ensure each key occurs at most once.
|
||||
*/
|
||||
|
||||
// Pre-constructed views. Unlike Java's stock collections, I create these outright
|
||||
// At least one ought to be accessed for this implementation to be useful
|
||||
private transient final ValueSortedTreeMapEntrySet entrySet = new ValueSortedTreeMapEntrySet();
|
||||
private transient final ValueSortedTreeMapKeySet keySet = new ValueSortedTreeMapKeySet();
|
||||
private transient final ValueSortedTreeMapValues values = new ValueSortedTreeMapValues();
|
||||
private transient final ValueSortedTreeMapEntrySet entrySet = createEntrySet();
|
||||
private transient final ValueSortedTreeMapKeySet keySet = createKeySet();
|
||||
private transient final ValueSortedTreeMapValues values = createValues();
|
||||
|
||||
// Pointers into the data structure
|
||||
private Node root; // The root of the binary tree
|
||||
private Node head; // The node with the least value
|
||||
private Node tail; // The node with the greatest value
|
||||
protected Node root; // The root of the binary tree
|
||||
protected Node head; // The node with the least value
|
||||
protected Node tail; // The node with the greatest value
|
||||
|
||||
/**
|
||||
* Construct a dynamic value-sorted tree map using the values' natural ordering
|
||||
*
|
||||
* If the values do not have a natural ordering, you will eventually encounter a
|
||||
* {@link ClassCastException}. This condition is not checked at construction.
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public DynamicValueSortedTreeMap() {
|
||||
protected TreeValueSortedMap() {
|
||||
this(new ComparableComparator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a dynamic value-sorted tree map using a custom comparator to order the values
|
||||
* @param comparator the comparator, providing a total ordering of the values
|
||||
*/
|
||||
public DynamicValueSortedTreeMap(Comparator<V> comparator) {
|
||||
protected TreeValueSortedMap(Comparator<V> comparator) {
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
protected ValueSortedTreeMapEntrySet createEntrySet() {
|
||||
return new ValueSortedTreeMapEntrySet();
|
||||
}
|
||||
|
||||
protected ValueSortedTreeMapKeySet createKeySet() {
|
||||
return new ValueSortedTreeMapKeySet();
|
||||
}
|
||||
|
||||
protected ValueSortedTreeMapValues createValues() {
|
||||
return new ValueSortedTreeMapValues();
|
||||
}
|
||||
|
||||
protected Node createNode(K key, V value) {
|
||||
return new Node(key, value);
|
||||
}
|
||||
|
||||
protected Node searchValue(V value, SearchMode mode) {
|
||||
if (root == null) {
|
||||
return null;
|
||||
}
|
||||
return root.searchValue(value, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
nodeMap.clear();
|
||||
|
@ -1491,7 +1600,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
V val = (V) value;
|
||||
return root.searchValue(val, SearchMode.ANY) != null;
|
||||
return searchValue(val, SearchMode.ANY) != null;
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
return false;
|
||||
|
@ -1517,6 +1626,26 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
return n.val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> lowerEntryByValue(V value) {
|
||||
return searchValue(value, SearchMode.LOWER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> floorEntryByValue(V value) {
|
||||
return searchValue(value, SearchMode.FLOOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> ceilingEntryByValue(V value) {
|
||||
return searchValue(value, SearchMode.CEILING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> higherEntryByValue(V value) {
|
||||
return searchValue(value, SearchMode.HIGHER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return root == null;
|
||||
|
@ -1524,6 +1653,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
|
||||
/**
|
||||
* Check if a node is correctly positioned relative to its immediate neighbors
|
||||
*
|
||||
* @param n the node
|
||||
* @return true if the node need not be moved
|
||||
*/
|
||||
|
@ -1557,7 +1687,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
if (n != null) {
|
||||
return n.setValue(value);
|
||||
}
|
||||
n = new Node(key, value);
|
||||
n = createNode(key, value);
|
||||
nodeMap.put(key, n);
|
||||
if (root == null) {
|
||||
root = n;
|
||||
|
@ -1592,15 +1722,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
return nodeMap.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the map of an external change to the cost of a key's associated value
|
||||
*
|
||||
* This is meant to update the entry's position after a change in cost. The position may not
|
||||
* necessarily change, however, if the cost did not change significantly.
|
||||
*
|
||||
* @param key the key whose associated value has changed in cost
|
||||
* @return true if the entry's position changed
|
||||
*/
|
||||
@Override
|
||||
public boolean update(K key) {
|
||||
Node n = nodeMap.get(key);
|
||||
if (n == null) {
|
||||
|
@ -1615,6 +1737,7 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
* This ought to be called any time the value of a node is modified, whether internall or
|
||||
* externally. The only way we know of external changes is if the user calls
|
||||
* {@link #update(Object)}.
|
||||
*
|
||||
* @param n the node whose position to check and update
|
||||
* @return true if the node's position changed
|
||||
*/
|
||||
|
@ -1636,4 +1759,24 @@ public class DynamicValueSortedTreeMap<K, V> extends AbstractMap<K, V> {
|
|||
public ValueSortedTreeMapValues values() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSortedMap<K, V> subMapByValue(V fromValue, boolean fromInclusive, V toValue,
|
||||
boolean toInclusive) {
|
||||
return new RestrictedValueSortedMap<>(this, comparator, true, fromValue, fromInclusive,
|
||||
true, toValue, toInclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
// TODO: Test this implementation and related others
|
||||
public ValueSortedMap<K, V> headMapByValue(V toValue, boolean inclusive) {
|
||||
return new RestrictedValueSortedMap<>(this, comparator, false, null, false, true, toValue,
|
||||
inclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSortedMap<K, V> tailMapByValue(V fromValue, boolean inclusive) {
|
||||
return new RestrictedValueSortedMap<>(this, comparator, true, fromValue, inclusive, false,
|
||||
null, false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/* ###
|
||||
* 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.generic.util.datastruct;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A map that is sorted by value.
|
||||
*
|
||||
* <p>
|
||||
* This is an extension of {@link Map} where entries are sorted by value, rather than by key. Such a
|
||||
* map may be useful as a priority queue where the cost of an entry may change over time. As such,
|
||||
* the collections returned by {@link #entrySet()}, {@link #keySet()}, and {@link #values()} all
|
||||
* extend {@link Deque}. The order of the entries will be updated on any call to {@link #put(Object,
|
||||
* Object))}, or a call to {@link Collection#add(Object)} on the entry set. Additionally, if the
|
||||
* values are mutable objects, whose order may change, there is an {@link #update(Object)} method,
|
||||
* which notifies the map that the given key may need to be repositioned. The associated collections
|
||||
* also extend the {@link List} interface, providing fairly efficient implementations of
|
||||
* {@link List#get(int)} and {@link List#indexOf(Object)}. Sequential access is best performed via
|
||||
* {@link Collection#iterator()}, since this will use a linked list.
|
||||
*
|
||||
* @param <K> the type of the keys
|
||||
* @param <V> the type of the values
|
||||
*/
|
||||
public interface ValueSortedMap<K, V> extends Map<K, V> {
|
||||
public interface ValueSortedMapEntryList<K, V>
|
||||
extends Set<Entry<K, V>>, List<Entry<K, V>>, Deque<Entry<K, V>> {
|
||||
@Override
|
||||
default Spliterator<Entry<K, V>> spliterator() {
|
||||
return Spliterators.spliterator(this, Spliterator.ORDERED | Spliterator.DISTINCT);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ValueSortedMapKeyList<K> extends Set<K>, List<K>, Deque<K> {
|
||||
@Override
|
||||
default Spliterator<K> spliterator() {
|
||||
return Spliterators.spliterator(this, Spliterator.ORDERED | Spliterator.DISTINCT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
ValueSortedMapEntryList<K, V> entrySet();
|
||||
|
||||
/**
|
||||
* Returns a key-value mapping associated with the greatest value strictly less than the given
|
||||
* value, or {@code null} if there is no such value.
|
||||
*
|
||||
* @param value the value
|
||||
* @return the found entry, or {@code null}
|
||||
*/
|
||||
Entry<K, V> lowerEntryByValue(V value);
|
||||
|
||||
/**
|
||||
* Returns a key-value mapping associated with the greatest value less than or equal to the
|
||||
* given value, or {@code null} if there is no such value.
|
||||
*
|
||||
* @param value the value
|
||||
* @return the found entry, or {@code null}
|
||||
*/
|
||||
Entry<K, V> floorEntryByValue(V value);
|
||||
|
||||
/**
|
||||
* Returns a key-value mapping associated with the least value greater than or equal to the
|
||||
* given value, or {@code null} if there is no such value.
|
||||
*
|
||||
* @param value the value
|
||||
* @return the found entry, or {@code null}
|
||||
*/
|
||||
Entry<K, V> ceilingEntryByValue(V value);
|
||||
|
||||
/**
|
||||
* Returns a key-value mapping associated with the least value strictly greater than the given
|
||||
* value, or {@code null} if there is no such value.
|
||||
*
|
||||
* @param value the value
|
||||
* @return the found entry, or {@code null}
|
||||
*/
|
||||
Entry<K, V> higherEntryByValue(V value);
|
||||
|
||||
/**
|
||||
* Returns a view of the portion of this map whose values range from {@code fromValue} to
|
||||
* {@code toValue}. The returned map is an unmodifiable view.
|
||||
*
|
||||
* @param fromValue low endpoint of the values in the returned map
|
||||
* @param fromInclusive {@code true} if the low endpoint is to be included in the returned view
|
||||
* @param toValue high endpoint of the values in the returned map
|
||||
* @param toInclusive {@code true} if the high endpoint is to be included in the returned view
|
||||
* @return the view
|
||||
*/
|
||||
ValueSortedMap<K, V> subMapByValue(V fromValue, boolean fromInclusive, V toValue,
|
||||
boolean toInclusive);
|
||||
|
||||
/**
|
||||
* Returns a view of the portion of this map whose values are less than (or equal to, if
|
||||
* {@code inclusive} is true) {@code toValue}. The returned map is an unmodifiable view.
|
||||
*
|
||||
* @param toValue high endpoint of the values in the returned map
|
||||
* @param inclusive {@code true} if the high endpoint is to be included in the returned view
|
||||
* @return the view
|
||||
*/
|
||||
ValueSortedMap<K, V> headMapByValue(V toValue, boolean inclusive);
|
||||
|
||||
/**
|
||||
* Returns a view of the portion of this map whose values are greater than (or equal to, if
|
||||
* {@code inclusive} is true) {@code toValue}. The returned map is an unmodifiable view.
|
||||
*
|
||||
* @param fromValue low endpoint of the values in the returned map
|
||||
* @param inclusive {@code true} if the low endpoint is to be included in the returned view
|
||||
* @return the view
|
||||
*/
|
||||
ValueSortedMap<K, V> tailMapByValue(V fromValue, boolean inclusive);
|
||||
|
||||
@Override
|
||||
ValueSortedMapKeyList<K> keySet();
|
||||
|
||||
/**
|
||||
* Notify the map of an external change to the cost of a key's associated value
|
||||
*
|
||||
* <p>
|
||||
* This is meant to update the entry's position after a change in cost. The position may not
|
||||
* necessarily change, however, if the cost did not change significantly.
|
||||
*
|
||||
* @param key the key whose associated value has changed in cost
|
||||
* @return true if the entry's position changed
|
||||
*/
|
||||
boolean update(K key);
|
||||
|
||||
@Override
|
||||
SortedList<V> values();
|
||||
}
|
|
@ -1,320 +0,0 @@
|
|||
/* ###
|
||||
* 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.*;
|
||||
|
||||
import ghidra.generic.util.datastruct.DynamicValueSortedTreeMap;
|
||||
|
||||
/**
|
||||
* A set where the ordering of elements may change over time, based on an alternative comparator
|
||||
*
|
||||
* This is an implementation of {@link Set} where elements may be sorted by an alternative
|
||||
* comparator (usually by "cost"), rather than by the natural ordering. It may seem odd, but the
|
||||
* natural ordering is still used to determine the uniqueness of keys. That is, two elements that
|
||||
* are unequal -- but are considered equal by the alternative comparator -- may co-exist in the
|
||||
* set. (Note: in such cases, the two elements are ordered first-in first-out). Additionally, if
|
||||
* the elements are mutable, then their ordering may change over time. This mode of operation is
|
||||
* enabled by the {@link #update(Object)} method, which must be called to notify the set of any
|
||||
* change to an element that may affect its order. This set also implements the {@link List} and
|
||||
* {@link Deque} interfaces. Since the set is ordered, it makes sense to treat it as a list. It
|
||||
* provides fairly efficient implementations of {@link #get(int)} and {@link #indexOf(Object)}.
|
||||
* Sequential access is best performed via {@link #iterator()}, since this will use a linked list.
|
||||
*
|
||||
* The underlying implementation is backed by {@link DynamicValueSortedTreeMap}. Currently, it is
|
||||
* not thread safe.
|
||||
*
|
||||
* @param <E> the type of the elements
|
||||
*/
|
||||
public class DynamicSortedTreeSet<E> extends AbstractSet<E> implements List<E>, Deque<E> {
|
||||
private final transient DynamicValueSortedTreeMap<E, E>.ValueSortedTreeMapKeySet keys;
|
||||
private final transient DynamicValueSortedTreeMap<E, E> map;
|
||||
|
||||
/**
|
||||
* Construct a dynamic sorted tree set using the elements' natural ordering
|
||||
*
|
||||
* Other than, perhaps, a more convenient interface, this offers few if any benefits over the
|
||||
* stock {@link Set}.
|
||||
*/
|
||||
public DynamicSortedTreeSet() {
|
||||
map = new DynamicValueSortedTreeMap<>();
|
||||
keys = map.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a dynamic sorted tree set using a custom comparator to order the elements
|
||||
* @param comparator the comparator, providing a total ordering of the values
|
||||
*/
|
||||
public DynamicSortedTreeSet(Comparator<E> comparator) {
|
||||
map = new DynamicValueSortedTreeMap<>(comparator);
|
||||
keys = map.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
return map.put(e, e) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the element, ignoring index
|
||||
*
|
||||
* @param index ignore since the set is sorted
|
||||
*/
|
||||
@Override
|
||||
public void add(int index, E element) {
|
||||
add(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts all elements from the given collection, ignoring index
|
||||
*
|
||||
* @param index ignore since the set is sorted
|
||||
*/
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends E> c) {
|
||||
return addAll(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the element, not necessarily first
|
||||
*/
|
||||
@Override
|
||||
public void addFirst(E e) {
|
||||
add(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the element, not necessarily last
|
||||
*/
|
||||
@Override
|
||||
public void addLast(E e) {
|
||||
add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return map.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> descendingIterator() {
|
||||
return keys.descendingIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E element() {
|
||||
return keys.element();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
return keys.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getFirst() {
|
||||
return keys.getFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getLast() {
|
||||
return keys.getLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return keys.indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return keys.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return keys.lastIndexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<E> listIterator() {
|
||||
return keys.listIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<E> listIterator(int index) {
|
||||
return keys.listIterator(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E e) {
|
||||
return add(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the element, not necessarily first
|
||||
*/
|
||||
@Override
|
||||
public boolean offerFirst(E e) {
|
||||
return add(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the element, not necessarily last
|
||||
*/
|
||||
@Override
|
||||
public boolean offerLast(E e) {
|
||||
return add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E peek() {
|
||||
return keys.peek();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E peekFirst() {
|
||||
return keys.peekFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E peekLast() {
|
||||
return keys.peekLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E poll() {
|
||||
return keys.poll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E pollFirst() {
|
||||
return keys.pollFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E pollLast() {
|
||||
return keys.pollLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E pop() {
|
||||
return keys.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(E e) {
|
||||
add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove() {
|
||||
return keys.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove(int index) {
|
||||
return keys.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return keys.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return keys.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E removeFirst() {
|
||||
return keys.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFirstOccurrence(Object o) {
|
||||
return keys.removeFirstOccurrence(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E removeLast() {
|
||||
return keys.removeLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeLastOccurrence(Object o) {
|
||||
return keys.removeLastOccurrence(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return keys.retainAll(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the element at the given index with the given element
|
||||
*
|
||||
* Because the set is sorted, the index of the given element may not be the same as
|
||||
* {@code index}. In fact, this is equivalent to removing the element at the given index, and
|
||||
* then inserting the given element at its sorted position.
|
||||
*/
|
||||
@Override
|
||||
public E set(int index, E element) {
|
||||
E result = remove(index);
|
||||
add(element);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<E> spliterator() {
|
||||
return Spliterators.spliterator(this, Spliterator.ORDERED | Spliterator.DISTINCT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This operation is not supported
|
||||
*/
|
||||
@Override
|
||||
public List<E> subList(int fromIndex, int toIndex) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the queue of a change to an elements cost.
|
||||
*
|
||||
* This may cause the element's index to change.
|
||||
* @param e the element whose cost may have changed
|
||||
* @return true if the index changed
|
||||
*/
|
||||
public boolean update(E e) {
|
||||
return map.update(e);
|
||||
}
|
||||
}
|
|
@ -21,8 +21,8 @@ public class MathUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Perform unsigned division. Provides proper handling of all 64-bit unsigned
|
||||
* values.
|
||||
* Perform unsigned division. Provides proper handling of all 64-bit unsigned values.
|
||||
*
|
||||
* @param numerator unsigned numerator
|
||||
* @param denominator positive divisor
|
||||
* @return result of unsigned division
|
||||
|
@ -47,8 +47,8 @@ public class MathUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Perform unsigned modulo. Provides proper handling of all 64-bit unsigned
|
||||
* values.
|
||||
* Perform unsigned modulo. Provides proper handling of all 64-bit unsigned values.
|
||||
*
|
||||
* @param numerator unsigned numerator
|
||||
* @param denominator positive divisor
|
||||
* @return result of unsigned modulo (i.e., remainder)
|
||||
|
@ -97,4 +97,111 @@ public class MathUtilities {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the minimum, treating the inputs as unsigned
|
||||
*
|
||||
* @param a the first value to consider
|
||||
* @param b the second value to consider
|
||||
* @return the minimum
|
||||
*/
|
||||
public static long unsignedMin(long a, long b) {
|
||||
return (Long.compareUnsigned(a, b) < 0) ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the minimum, treating the inputs as unsigned
|
||||
*
|
||||
* @param a the first value to consider
|
||||
* @param b the second value to consider
|
||||
* @return the minimum
|
||||
*/
|
||||
public static int unsignedMin(int a, int b) {
|
||||
return (Integer.compareUnsigned(a, b) < 0) ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the minimum, treating the inputs as unsigned
|
||||
*
|
||||
* <p>
|
||||
* This method is overloaded to prevent accidental signed-extension on one of the inputs. This
|
||||
* method will correctly zero-extend the {@code int} parameter before performing any comparison.
|
||||
* Also note the return type is {@code int}, since b would never be selected if it overflows an
|
||||
* {@code int}.
|
||||
*
|
||||
* @param a the first value to consider
|
||||
* @param b the second value to consider
|
||||
* @return the minimum
|
||||
*/
|
||||
public static int unsignedMin(int a, long b) {
|
||||
return (Long.compareUnsigned(a & 0x0ffffffffL, b) < 0) ? a : (int) b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the minimum, treating the inputs as unsigned
|
||||
*
|
||||
* <p>
|
||||
* This method is overloaded to prevent accidental signed-extension on one of the inputs. This
|
||||
* method will correctly zero-extend the {@code int} parameter before performing any comparison.
|
||||
* Also note the return type is {@code int}, since b would never be selected if it overflows an
|
||||
* {@code int}.
|
||||
*
|
||||
* @param a the first value to consider
|
||||
* @param b the second value to consider
|
||||
* @return the minimum
|
||||
*/
|
||||
public static int unsignedMin(long a, int b) {
|
||||
return (Long.compareUnsigned(a, b & 0x0ffffffffL) < 0) ? (int) a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the maximum, treating the inputs as unsigned
|
||||
*
|
||||
* @param a the first value to consider
|
||||
* @param b the second value to consider
|
||||
* @return the maximum
|
||||
*/
|
||||
public static long unsignedMax(long a, long b) {
|
||||
return (Long.compareUnsigned(a, b) > 0) ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the maximum, treating the inputs as unsigned
|
||||
*
|
||||
* @param a the first value to consider
|
||||
* @param b the second value to consider
|
||||
* @return the maximum
|
||||
*/
|
||||
public static int unsignedMax(int a, int b) {
|
||||
return (Integer.compareUnsigned(a, b) > 0) ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the maximum, treating the inputs as unsigned
|
||||
*
|
||||
* <p>
|
||||
* This method is overloaded to prevent accidental signed-extension on one of the inputs. This
|
||||
* method will correctly zero-extend the {@code int} parameter before performing any comparison.
|
||||
*
|
||||
* @param a the first value to consider
|
||||
* @param b the second value to consider
|
||||
* @return the maximum
|
||||
*/
|
||||
public static long unsignedMax(int a, long b) {
|
||||
return (Long.compareUnsigned(a & 0x0ffffffffL, b) > 0) ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the maximum, treating the inputs as unsigned
|
||||
*
|
||||
* <p>
|
||||
* This method is overloaded to prevent accidental signed-extension on one of the inputs. This
|
||||
* method will correctly zero-extend the {@code int} parameter before performing any comparison.
|
||||
*
|
||||
* @param a the first value to consider
|
||||
* @param b the second value to consider
|
||||
* @return the maximum
|
||||
*/
|
||||
public static long unsignedMax(long a, int b) {
|
||||
return (Long.compareUnsigned(a, b & 0x0ffffffffL) > 0) ? a : b;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,9 +57,8 @@ public final class NumericUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* parses the given string as a numeric value, detecting whether
|
||||
* or not it begins with a Hex prefix, and if not, parses as a
|
||||
* long int value.
|
||||
* parses the given string as a numeric value, detecting whether or not it begins with a Hex
|
||||
* prefix, and if not, parses as a long int value.
|
||||
*/
|
||||
public static long parseNumber(String numStr) {
|
||||
long value = 0;
|
||||
|
@ -85,9 +84,8 @@ public final class NumericUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* parses the given string as a numeric value, detecting whether
|
||||
* or not it begins with a Hex prefix, and if not, parses as a
|
||||
* long int value.
|
||||
* parses the given string as a numeric value, detecting whether or not it begins with a Hex
|
||||
* prefix, and if not, parses as a long int value.
|
||||
*/
|
||||
public static long parseLong(String numStr) {
|
||||
String origStr = numStr;
|
||||
|
@ -132,9 +130,8 @@ public final class NumericUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* parses the given string as a numeric value, detecting whether
|
||||
* or not it begins with a Hex prefix, and if not, parses as a
|
||||
* long int value.
|
||||
* parses the given string as a numeric value, detecting whether or not it begins with a Hex
|
||||
* prefix, and if not, parses as a long int value.
|
||||
*/
|
||||
public static long parseOctLong(String numStr) {
|
||||
|
||||
|
@ -193,8 +190,9 @@ public final class NumericUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* returns the value of the specified long as hexadecimal, prefixing
|
||||
* with the HEX_PREFIX_x string.
|
||||
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
|
||||
* string.
|
||||
*
|
||||
* @param value the long value to convert
|
||||
*/
|
||||
public final static String toHexString(long value) {
|
||||
|
@ -202,8 +200,9 @@ public final class NumericUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* returns the value of the specified long as hexadecimal, prefixing
|
||||
* with the HEX_PREFIX_x string.
|
||||
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
|
||||
* string.
|
||||
*
|
||||
* @param value the long value to convert
|
||||
* @param size number of bytes to be represented
|
||||
*/
|
||||
|
@ -215,8 +214,9 @@ public final class NumericUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* returns the value of the specified long as signed hexadecimal, prefixing
|
||||
* with the HEX_PREFIX_x string.
|
||||
* returns the value of the specified long as signed hexadecimal, prefixing with the
|
||||
* HEX_PREFIX_x string.
|
||||
*
|
||||
* @param value the long value to convert
|
||||
*/
|
||||
public final static String toSignedHexString(long value) {
|
||||
|
@ -256,8 +256,9 @@ public final class NumericUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get an unsigned aligned value corresponding to the specified unsigned value
|
||||
* which will be greater than or equal the specified value.
|
||||
* Get an unsigned aligned value corresponding to the specified unsigned value which will be
|
||||
* greater than or equal the specified value.
|
||||
*
|
||||
* @param unsignedValue value to be aligned
|
||||
* @param alignment alignment
|
||||
* @return aligned value
|
||||
|
@ -280,19 +281,38 @@ public final class NumericUtilities {
|
|||
/**
|
||||
* Convert a masked value into a hexadecimal-ish string.
|
||||
*
|
||||
* Converts the data to hexadecimal, placing an X where a nibble is unknown. Where a nibble
|
||||
* is partially defined, it is displayed as four bits in brackets []. Bits are displayed
|
||||
* as x, or the defined value.
|
||||
* Converts the data to hexadecimal, placing an X where a nibble is unknown. Where a nibble is
|
||||
* partially defined, it is displayed as four bits in brackets []. Bits are displayed as x, or
|
||||
* the defined value.
|
||||
*
|
||||
* For example, consider the mask 00001111:01011100, and the value 00001001:00011000. This
|
||||
* will display as {@code X8:[x0x1][10xx]}. To see the correlation, consider the table:
|
||||
* <table><caption></caption>
|
||||
* <tr><th>Display</th><th>{@code X}</th> <th>{@code 8}</th> <th>{@code :}</th>
|
||||
* <th>{@code [x0x1]}</th><th>{@code [10xx]}</th></tr>
|
||||
* <tr><th>Mask</th> <td>{@code 0000}</td><td>{@code 1111}</td><td>{@code :}</td>
|
||||
* <td>{@code 0101}</td> <td>{@code 1100}</td> </tr>
|
||||
* <tr><th>Value</th> <td>{@code 0000}</td><td>{@code 1000}</td><td>{@code :}</td>
|
||||
* <td>{@code 0001}</td> <td>{@code 1000}</td> </tr>
|
||||
* For example, consider the mask 00001111:01011100, and the value 00001001:00011000. This will
|
||||
* display as {@code X8:[x0x1][10xx]}. To see the correlation, consider the table:
|
||||
* <table>
|
||||
* <caption></caption>
|
||||
* <tr>
|
||||
* <th>Display</th>
|
||||
* <th>{@code X}</th>
|
||||
* <th>{@code 8}</th>
|
||||
* <th>{@code :}</th>
|
||||
* <th>{@code [x0x1]}</th>
|
||||
* <th>{@code [10xx]}</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <th>Mask</th>
|
||||
* <td>{@code 0000}</td>
|
||||
* <td>{@code 1111}</td>
|
||||
* <td>{@code :}</td>
|
||||
* <td>{@code 0101}</td>
|
||||
* <td>{@code 1100}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <th>Value</th>
|
||||
* <td>{@code 0000}</td>
|
||||
* <td>{@code 1000}</td>
|
||||
* <td>{@code :}</td>
|
||||
* <td>{@code 0001}</td>
|
||||
* <td>{@code 1000}</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @param msk the mask
|
||||
|
@ -364,6 +384,7 @@ public final class NumericUtilities {
|
|||
* Philosophically, it is hexadecimal, but the only valid digits are 0 and F. Any
|
||||
* partially-included nibble will be broken down into bracketed bits. Displaying masks in this
|
||||
* way is convenient when shown proximal to related masked values.
|
||||
*
|
||||
* @param msk the mask
|
||||
* @param n the number of nibbles, starting at the right
|
||||
* @param truncate true if leading Xs may be truncated
|
||||
|
@ -420,6 +441,7 @@ public final class NumericUtilities {
|
|||
|
||||
/**
|
||||
* The reverse of {@link #convertMaskedValueToHexString(long, long, int, boolean, int, String)}
|
||||
*
|
||||
* @param msk an object to receive the resulting mask
|
||||
* @param val an object to receive the resulting value
|
||||
* @param hex the input string to parse
|
||||
|
@ -509,7 +531,8 @@ public final class NumericUtilities {
|
|||
|
||||
/**
|
||||
* Render <code>number</code> in different bases using the default signedness mode.
|
||||
* <p>This invokes {@linkplain #formatNumber(long, int, SignednessFormatMode)} with a
|
||||
* <p>
|
||||
* This invokes {@linkplain #formatNumber(long, int, SignednessFormatMode)} with a
|
||||
* <code>mode</code> parameter of <code>{@linkplain SignednessFormatMode#DEFAULT}</code>.
|
||||
*
|
||||
* @param number The number to represent
|
||||
|
@ -524,30 +547,129 @@ public final class NumericUtilities {
|
|||
/**
|
||||
* Provide renderings of <code>number</code> in different bases:
|
||||
* <ul>
|
||||
* <li> <code>0</code> - renders <code>number</code> as an escaped character sequence</li>
|
||||
* <li> <code>2</code> - renders <code>number</code> as a <code>base-2</code> integer</li>
|
||||
* <li> <code>8</code> - renders <code>number</code> as a <code>base-8</code> integer</li>
|
||||
* <li> <code>10</code> - renders <code>number</code> as a <code>base-10</code> integer</li>
|
||||
* <li> <code>16</code> (default) - renders <code>number</code> as a <code>base-16</code> integer</li>
|
||||
* <li><code>0</code> - renders <code>number</code> as an escaped character sequence</li>
|
||||
* <li><code>2</code> - renders <code>number</code> as a <code>base-2</code> integer</li>
|
||||
* <li><code>8</code> - renders <code>number</code> as a <code>base-8</code> integer</li>
|
||||
* <li><code>10</code> - renders <code>number</code> as a <code>base-10</code> integer</li>
|
||||
* <li><code>16</code> (default) - renders <code>number</code> as a <code>base-16</code>
|
||||
* integer</li>
|
||||
* </ul>
|
||||
* <table><caption></caption>
|
||||
* <tr><th>Number</th><th>Radix</th><th>DEFAULT Mode Alias</th><th style="text-align:center"><i>UNSIGNED</i> Mode Value</th><th><i>SIGNED</i> Mode Value</th></tr>
|
||||
* <tr><td> </td><td></td><td><i></i></td><td></td><td></td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>100</td><td>2</td><td><i>UNSIGNED</i></td><td>1100100b</td><td>1100100b</td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>100</td><td>8</td><td><i>UNSIGNED</i></td><td>144o</td><td>144o</td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>100</td><td>10</td><td><i>SIGNED</i></td><td>100</td><td>100</td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>100</td><td>16</td><td><i>UNSIGNED</i></td><td>64h</td><td>64h</td></tr>
|
||||
* <tr><td> </td><td></td><td><i></i></td><td></td><td></td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>-1</td><td>2</td><td><i>UNSIGNED</i></td><td>1111111111111111111111111111111111111111111111111111111111111111b</td><td>-1b</td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>-1</td><td>8</td><td><i>UNSIGNED</i></td><td>1777777777777777777777o</td><td>-1o</td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>-1</td><td>10</td><td><i>SIGNED</i></td><td>18446744073709551615</td><td>-1</td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>-1</td><td>16</td><td><i>UNSIGNED</i></td><td>ffffffffffffffffh</td><td>-1h</td></tr>
|
||||
*<tr><td> </td><td></td><td><i></i></td><td></td><td></td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>-100</td><td>2</td><td><i>UNSIGNED</i></td><td>1111111111111111111111111111111111111111111111111111111110011100b</td><td>-1100100b</td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>-100</td><td>8</td><td><i>UNSIGNED</i></td><td>1777777777777777777634o</td><td>-144o</td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>-100</td><td>10</td><td><i>SIGNED</i></td><td>18446744073709551516</td><td>-100</td></tr>
|
||||
* <tr style="text-align:right;font-family: monospace"><td>-100</td><td>16</td><td><i>UNSIGNED</i></td><td>ffffffffffffff9ch</td><td>-64h</td></tr>
|
||||
* <table>
|
||||
* <caption></caption>
|
||||
* <tr>
|
||||
* <th>Number</th>
|
||||
* <th>Radix</th>
|
||||
* <th>DEFAULT Mode Alias</th>
|
||||
* <th style="text-align:center"><i>UNSIGNED</i> Mode Value</th>
|
||||
* <th><i>SIGNED</i> Mode Value</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td> </td>
|
||||
* <td></td>
|
||||
* <td><i></i></td>
|
||||
* <td></td>
|
||||
* <td></td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>100</td>
|
||||
* <td>2</td>
|
||||
* <td><i>UNSIGNED</i></td>
|
||||
* <td>1100100b</td>
|
||||
* <td>1100100b</td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>100</td>
|
||||
* <td>8</td>
|
||||
* <td><i>UNSIGNED</i></td>
|
||||
* <td>144o</td>
|
||||
* <td>144o</td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>100</td>
|
||||
* <td>10</td>
|
||||
* <td><i>SIGNED</i></td>
|
||||
* <td>100</td>
|
||||
* <td>100</td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>100</td>
|
||||
* <td>16</td>
|
||||
* <td><i>UNSIGNED</i></td>
|
||||
* <td>64h</td>
|
||||
* <td>64h</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td> </td>
|
||||
* <td></td>
|
||||
* <td><i></i></td>
|
||||
* <td></td>
|
||||
* <td></td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>-1</td>
|
||||
* <td>2</td>
|
||||
* <td><i>UNSIGNED</i></td>
|
||||
* <td>1111111111111111111111111111111111111111111111111111111111111111b</td>
|
||||
* <td>-1b</td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>-1</td>
|
||||
* <td>8</td>
|
||||
* <td><i>UNSIGNED</i></td>
|
||||
* <td>1777777777777777777777o</td>
|
||||
* <td>-1o</td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>-1</td>
|
||||
* <td>10</td>
|
||||
* <td><i>SIGNED</i></td>
|
||||
* <td>18446744073709551615</td>
|
||||
* <td>-1</td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>-1</td>
|
||||
* <td>16</td>
|
||||
* <td><i>UNSIGNED</i></td>
|
||||
* <td>ffffffffffffffffh</td>
|
||||
* <td>-1h</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td> </td>
|
||||
* <td></td>
|
||||
* <td><i></i></td>
|
||||
* <td></td>
|
||||
* <td></td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>-100</td>
|
||||
* <td>2</td>
|
||||
* <td><i>UNSIGNED</i></td>
|
||||
* <td>1111111111111111111111111111111111111111111111111111111110011100b</td>
|
||||
* <td>-1100100b</td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>-100</td>
|
||||
* <td>8</td>
|
||||
* <td><i>UNSIGNED</i></td>
|
||||
* <td>1777777777777777777634o</td>
|
||||
* <td>-144o</td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>-100</td>
|
||||
* <td>10</td>
|
||||
* <td><i>SIGNED</i></td>
|
||||
* <td>18446744073709551516</td>
|
||||
* <td>-100</td>
|
||||
* </tr>
|
||||
* <tr style="text-align:right;font-family: monospace">
|
||||
* <td>-100</td>
|
||||
* <td>16</td>
|
||||
* <td><i>UNSIGNED</i></td>
|
||||
* <td>ffffffffffffff9ch</td>
|
||||
* <td>-64h</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @param number The number to represent
|
||||
* @param radix The base in which <code>number</code> is represented
|
||||
* @param mode Specifies how the number is formatted with respect to its signed-ness
|
||||
|
@ -637,8 +759,7 @@ public final class NumericUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convert the given byte into a two character String, padding with a leading 0 if
|
||||
* needed.
|
||||
* Convert the given byte into a two character String, padding with a leading 0 if needed.
|
||||
*
|
||||
* @param b the byte
|
||||
* @return the byte string
|
||||
|
@ -728,6 +849,7 @@ public final class NumericUtilities {
|
|||
|
||||
/**
|
||||
* Determine if the provided Number is an integer type -- Byte, Short, Integer, or Long.
|
||||
*
|
||||
* @param number the object to check for for integer-type
|
||||
* @return true if the provided number is an integer-type, false otherwise
|
||||
*/
|
||||
|
@ -738,6 +860,7 @@ public final class NumericUtilities {
|
|||
|
||||
/**
|
||||
* Determine if the provided Number class is an integer type.
|
||||
*
|
||||
* @param numClass Class of an object
|
||||
* @return true if the class parameter is a integer type, false otherwise
|
||||
*/
|
||||
|
@ -747,6 +870,7 @@ public final class NumericUtilities {
|
|||
|
||||
/**
|
||||
* Determine if the provided Number is a floating-point type -- Float or Double.
|
||||
*
|
||||
* @param number the object to check for for floating-point-type
|
||||
* @return true if the provided number is a floating-point-type, false otherwise
|
||||
*/
|
||||
|
@ -757,6 +881,7 @@ public final class NumericUtilities {
|
|||
|
||||
/**
|
||||
* Determine if the provided Number class is a floating-point type.
|
||||
*
|
||||
* @param numClass Class of an object
|
||||
* @return true if the class parameter is a floating-point type, false otherwise
|
||||
*/
|
||||
|
@ -765,12 +890,12 @@ public final class NumericUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Provides the protocol for rendering integer-type numbers in different
|
||||
* signed-ness modes.
|
||||
* Provides the protocol for rendering integer-type numbers in different signed-ness modes.
|
||||
*/
|
||||
private static interface IntegerRadixRenderer {
|
||||
/**
|
||||
* Format the given number in the provided radix base.
|
||||
*
|
||||
* @param number the number to render
|
||||
* @param radix the base in which to render
|
||||
* @return a string representing the provided number in the given base
|
||||
|
@ -836,8 +961,8 @@ public final class NumericUtilities {
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* Values to be rendered in binary, octal, or hexadecimal bases are rendered
|
||||
* as unsigned, numbers rendered in decimal are rendered as signed.
|
||||
* Values to be rendered in binary, octal, or hexadecimal bases are rendered as unsigned,
|
||||
* numbers rendered in decimal are rendered as signed.
|
||||
*/
|
||||
@Override
|
||||
public String toString(long number, int radix) {
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
/* ###
|
||||
* 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.datastruct;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Class to provide a map with weak values, backed by a given map
|
||||
*
|
||||
* @param <K> the type of keys
|
||||
* @param <V> the type of values
|
||||
*/
|
||||
public abstract class AbstractWeakValueMap<K, V> implements Map<K, V> {
|
||||
protected ReferenceQueue<V> refQueue;
|
||||
|
||||
/**
|
||||
* Constructs a new weak map
|
||||
*/
|
||||
protected AbstractWeakValueMap() {
|
||||
refQueue = new ReferenceQueue<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the backing map
|
||||
*
|
||||
* @return the map
|
||||
*/
|
||||
protected abstract Map<K, WeakValueRef<K, V>> getRefMap();
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
processQueue();
|
||||
WeakValueRef<K, V> ref = new WeakValueRef<>(key, value, refQueue);
|
||||
WeakValueRef<K, V> oldRef = getRefMap().put(key, ref);
|
||||
if (oldRef != null) {
|
||||
return oldRef.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
processQueue();
|
||||
WeakValueRef<K, V> ref = getRefMap().get(key);
|
||||
if (ref != null) {
|
||||
return ref.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
processQueue();
|
||||
return getRefMap().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
getRefMap().clear();
|
||||
refQueue = new ReferenceQueue<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
processQueue();
|
||||
return getRefMap().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
processQueue();
|
||||
return getRefMap().containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
processQueue();
|
||||
Iterator<WeakValueRef<K, V>> it = getRefMap().values().iterator();
|
||||
while (it.hasNext()) {
|
||||
WeakValueRef<K, V> ref = it.next();
|
||||
if (value.equals(ref.get())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
ArrayList<V> list = new ArrayList<>(getRefMap().size());
|
||||
Iterator<WeakValueRef<K, V>> it = getRefMap().values().iterator();
|
||||
while (it.hasNext()) {
|
||||
WeakValueRef<K, V> ref = it.next();
|
||||
V value = ref.get();
|
||||
if (value != null) {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> map) {
|
||||
Iterator<? extends K> it = map.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
K key = it.next();
|
||||
V value = map.get(key);
|
||||
if (value != null) {
|
||||
put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<K, V>> entrySet() {
|
||||
processQueue();
|
||||
Set<Map.Entry<K, V>> list = new HashSet<>();
|
||||
Set<Map.Entry<K, WeakValueRef<K, V>>> entrySet = getRefMap().entrySet();
|
||||
Iterator<Map.Entry<K, WeakValueRef<K, V>>> it = entrySet.iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<K, WeakValueRef<K, V>> next = it.next();
|
||||
WeakValueRef<K, V> valueRef = next.getValue();
|
||||
V value = valueRef.get();
|
||||
if (value != null) {
|
||||
list.add(new GeneratedEntry(next.getKey(), value));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
processQueue();
|
||||
return getRefMap().keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
WeakValueRef<K, V> ref = getRefMap().remove(key);
|
||||
if (ref != null) {
|
||||
return ref.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void processQueue() {
|
||||
WeakValueRef<K, V> ref;
|
||||
while ((ref = (WeakValueRef<K, V>) refQueue.poll()) != null) {
|
||||
getRefMap().remove(ref.key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry for the "entrySet" method, since internally, entries are of weak-referenced values.
|
||||
*/
|
||||
protected class GeneratedEntry implements Map.Entry<K, V> {
|
||||
K key;
|
||||
V value;
|
||||
|
||||
GeneratedEntry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
this.value = value;
|
||||
return put(key, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A weak value ref that also knows its key in the map.
|
||||
*
|
||||
* <p>
|
||||
* Used for processing the reference queue, so we know which keys to remove.
|
||||
*
|
||||
* @param <K> the type of key
|
||||
* @param <V> the type of value
|
||||
*/
|
||||
protected static class WeakValueRef<K, V> extends WeakReference<V> {
|
||||
K key;
|
||||
|
||||
WeakValueRef(K key, V value, ReferenceQueue<V> refQueue) {
|
||||
super(value, refQueue);
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
/* ###
|
||||
* 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.datastruct;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Class to provide a navigable, e.g., tree-, map with weak values
|
||||
*
|
||||
* @param <K> the type of keys
|
||||
* @param <V> the type of values
|
||||
*/
|
||||
public abstract class AbstractWeakValueNavigableMap<K, V> extends AbstractWeakValueMap<K, V>
|
||||
implements NavigableMap<K, V> {
|
||||
|
||||
/**
|
||||
* A view of this same map that limits or changes the order of the keys
|
||||
*
|
||||
* <p>
|
||||
* TODO: By virtue of extending (indirectly) {@link AbstractWeakValueMap}, this view inherits a
|
||||
* unique, but totally unused, {@link AbstractWeakValueMap#refQueue}. This is a small and
|
||||
* harmless, but unnecessary waste.
|
||||
*
|
||||
* @param <K> the type of keys
|
||||
* @param <V> the type of values
|
||||
*/
|
||||
protected static class NavigableView<K, V> extends AbstractWeakValueNavigableMap<K, V> {
|
||||
protected final AbstractWeakValueNavigableMap<K, V> map;
|
||||
protected final NavigableMap<K, WeakValueRef<K, V>> mod;
|
||||
|
||||
public NavigableView(AbstractWeakValueNavigableMap<K, V> map,
|
||||
NavigableMap<K, WeakValueRef<K, V>> sub) {
|
||||
this.map = map;
|
||||
this.mod = Collections.unmodifiableNavigableMap(sub);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NavigableMap<K, WeakValueRef<K, V>> getRefMap() {
|
||||
map.processQueue();
|
||||
return mod;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract NavigableMap<K, WeakValueRef<K, V>> getRefMap();
|
||||
|
||||
@Override
|
||||
public Comparator<? super K> comparator() {
|
||||
return getRefMap().comparator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K firstKey() {
|
||||
processQueue();
|
||||
return getRefMap().firstKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K lastKey() {
|
||||
processQueue();
|
||||
return getRefMap().lastKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a generated (wrapper) entry, for the entry-retrieval methods.
|
||||
*
|
||||
* <p>
|
||||
* This handles the null case in one place.
|
||||
*
|
||||
* @param ent the entry to wrap, possibly null
|
||||
* @return the generated entry, or null
|
||||
*/
|
||||
protected GeneratedEntry generateEntry(Entry<K, WeakValueRef<K, V>> ent) {
|
||||
if (ent == null) {
|
||||
return null;
|
||||
}
|
||||
return new GeneratedEntry(ent.getKey(), ent.getValue().get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> lowerEntry(K key) {
|
||||
processQueue();
|
||||
return generateEntry(getRefMap().lowerEntry(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public K lowerKey(K key) {
|
||||
processQueue();
|
||||
return getRefMap().lowerKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> floorEntry(K key) {
|
||||
processQueue();
|
||||
return generateEntry(getRefMap().floorEntry(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public K floorKey(K key) {
|
||||
processQueue();
|
||||
return getRefMap().floorKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> ceilingEntry(K key) {
|
||||
processQueue();
|
||||
return generateEntry(getRefMap().ceilingEntry(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public K ceilingKey(K key) {
|
||||
processQueue();
|
||||
return getRefMap().ceilingKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> higherEntry(K key) {
|
||||
processQueue();
|
||||
return generateEntry(getRefMap().higherEntry(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public K higherKey(K key) {
|
||||
processQueue();
|
||||
return getRefMap().higherKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> firstEntry() {
|
||||
processQueue();
|
||||
return generateEntry(getRefMap().firstEntry());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> lastEntry() {
|
||||
processQueue();
|
||||
return generateEntry(getRefMap().lastEntry());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> pollFirstEntry() {
|
||||
processQueue();
|
||||
return generateEntry(getRefMap().pollFirstEntry());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> pollLastEntry() {
|
||||
processQueue();
|
||||
return generateEntry(getRefMap().pollLastEntry());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, V> descendingMap() {
|
||||
processQueue();
|
||||
return new NavigableView<>(this, getRefMap().descendingMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<K> navigableKeySet() {
|
||||
return getRefMap().navigableKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<K> descendingKeySet() {
|
||||
return getRefMap().descendingKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey,
|
||||
boolean toInclusive) {
|
||||
processQueue();
|
||||
return new NavigableView<>(this,
|
||||
getRefMap().subMap(fromKey, fromInclusive, toKey, toInclusive));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
|
||||
processQueue();
|
||||
return new NavigableView<>(this, getRefMap().headMap(toKey, inclusive));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
|
||||
processQueue();
|
||||
return new NavigableView<>(this, getRefMap().tailMap(fromKey, inclusive));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedMap<K, V> subMap(K fromKey, K toKey) {
|
||||
processQueue();
|
||||
return subMap(fromKey, true, toKey, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedMap<K, V> headMap(K toKey) {
|
||||
return headMap(toKey, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedMap<K, V> tailMap(K fromKey) {
|
||||
return tailMap(fromKey, true);
|
||||
}
|
||||
}
|
|
@ -15,192 +15,36 @@
|
|||
*/
|
||||
package ghidra.util.datastruct;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Class to provide a hash map with weak values.
|
||||
*/
|
||||
|
||||
public class WeakValueHashMap<K, V> implements Map<K, V> {
|
||||
private HashMap<K, WeakValueRef<K, V>> hashMap;
|
||||
private ReferenceQueue<V> refQueue;
|
||||
public class WeakValueHashMap<K, V> extends AbstractWeakValueMap<K, V> {
|
||||
private Map<K, WeakValueRef<K, V>> refMap;
|
||||
|
||||
/**
|
||||
* Constructs a new weak map
|
||||
*/
|
||||
public WeakValueHashMap() {
|
||||
hashMap = new HashMap<>();
|
||||
refQueue = new ReferenceQueue<>();
|
||||
super();
|
||||
refMap = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new weak map with the given initial size
|
||||
*
|
||||
* @param initialSize the initial size of the backing map
|
||||
*/
|
||||
public WeakValueHashMap(int initialSize) {
|
||||
hashMap = new HashMap<>(initialSize);
|
||||
refQueue = new ReferenceQueue<>();
|
||||
super();
|
||||
refMap = new HashMap<>(initialSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
processQueue();
|
||||
WeakValueRef<K, V> ref = new WeakValueRef<>(key, value, refQueue);
|
||||
WeakValueRef<K, V> oldRef = hashMap.put(key, ref);
|
||||
if (oldRef != null) {
|
||||
return oldRef.get();
|
||||
}
|
||||
return null;
|
||||
protected Map<K, WeakValueRef<K, V>> getRefMap() {
|
||||
return refMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
processQueue();
|
||||
WeakValueRef<K, V> ref = hashMap.get(key);
|
||||
if (ref != null) {
|
||||
return ref.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
processQueue();
|
||||
return hashMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
hashMap.clear();
|
||||
refQueue = new ReferenceQueue<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
processQueue();
|
||||
return hashMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
processQueue();
|
||||
return hashMap.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
processQueue();
|
||||
Iterator<WeakValueRef<K, V>> it = hashMap.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
WeakValueRef<K, V> ref = it.next();
|
||||
if (value.equals(ref.get())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
ArrayList<V> list = new ArrayList<>(hashMap.size());
|
||||
Iterator<WeakValueRef<K, V>> it = hashMap.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
WeakValueRef<K, V> ref = it.next();
|
||||
V value = ref.get();
|
||||
if (value != null) {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> map) {
|
||||
Iterator<? extends K> it = map.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
K key = it.next();
|
||||
V value = map.get(key);
|
||||
if (value != null) {
|
||||
put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<K, V>> entrySet() {
|
||||
processQueue();
|
||||
Set<Map.Entry<K, V>> list = new HashSet<>();
|
||||
Set<Map.Entry<K, WeakValueRef<K, V>>> entrySet = hashMap.entrySet();
|
||||
Iterator<Map.Entry<K, WeakValueRef<K, V>>> it = entrySet.iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<K, WeakValueRef<K, V>> next = it.next();
|
||||
WeakValueRef<K, V> valueRef = next.getValue();
|
||||
V value = valueRef.get();
|
||||
if (value != null) {
|
||||
list.add(new GeneratedEntry(next.getKey(), value));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
processQueue();
|
||||
return hashMap.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
WeakValueRef<K, V> ref = hashMap.remove(key);
|
||||
if (ref != null) {
|
||||
return ref.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void processQueue() {
|
||||
WeakValueRef<K, V> ref;
|
||||
while ((ref = (WeakValueRef<K, V>) refQueue.poll()) != null) {
|
||||
hashMap.remove(ref.key);
|
||||
}
|
||||
}
|
||||
|
||||
class GeneratedEntry implements Map.Entry<K, V> {
|
||||
K key;
|
||||
V value;
|
||||
|
||||
GeneratedEntry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
return put(key, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class WeakValueRef<K, V> extends WeakReference<V> {
|
||||
K key;
|
||||
|
||||
WeakValueRef(K key, V value, ReferenceQueue<V> refQueue) {
|
||||
super(value, refQueue);
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/* ###
|
||||
* 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.datastruct;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Class to provide a tree map with weak values.
|
||||
*/
|
||||
public class WeakValueTreeMap<K, V> extends AbstractWeakValueNavigableMap<K, V> {
|
||||
protected final NavigableMap<K, WeakValueRef<K, V>> refMap;
|
||||
|
||||
/**
|
||||
* Constructs a new weak map
|
||||
*/
|
||||
public WeakValueTreeMap() {
|
||||
super();
|
||||
refMap = new TreeMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new weak map with keys ordered according to the given comparator
|
||||
*
|
||||
* @param comparator the comparator, or {@code null} for the natural ordering
|
||||
*/
|
||||
public WeakValueTreeMap(Comparator<K> comparator) {
|
||||
super();
|
||||
refMap = new TreeMap<>(comparator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NavigableMap<K, WeakValueRef<K, V>> getRefMap() {
|
||||
return refMap;
|
||||
}
|
||||
}
|
|
@ -13,35 +13,24 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.util;
|
||||
package ghidra.generic.util.datastruct;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.collections4.comparators.ReverseComparator;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.generic.util.datastruct.DynamicValueSortedTreeMap;
|
||||
|
||||
public class DynamicValueSortedTreeMapTest {
|
||||
public static class NonComparable {
|
||||
}
|
||||
import ghidra.generic.util.datastruct.TreeValueSortedMap;
|
||||
import ghidra.generic.util.datastruct.ValueSortedMap;
|
||||
|
||||
public class TreeValueSortedMapTest {
|
||||
@Test
|
||||
public void testNaturalOrder() {
|
||||
DynamicValueSortedTreeMap<String, Integer> queue = new DynamicValueSortedTreeMap<>();
|
||||
ValueSortedMap<String, Integer> queue = TreeValueSortedMap.createWithNaturalOrder();
|
||||
queue.put("2nd", 2);
|
||||
queue.put("1st", 1);
|
||||
queue.put("3rd", 3);
|
||||
|
@ -49,17 +38,10 @@ public class DynamicValueSortedTreeMapTest {
|
|||
assertEquals(Arrays.asList(new String[] { "1st", "2nd", "3rd" }), ordered);
|
||||
}
|
||||
|
||||
@Test(expected = ClassCastException.class)
|
||||
public void testUnorderedError() {
|
||||
DynamicValueSortedTreeMap<String, NonComparable> queue = new DynamicValueSortedTreeMap<>();
|
||||
queue.put("2nd", new NonComparable());
|
||||
queue.put("1st", new NonComparable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExplicitOrdered() {
|
||||
DynamicValueSortedTreeMap<String, Integer> queue =
|
||||
new DynamicValueSortedTreeMap<>(new ReverseComparator<>());
|
||||
ValueSortedMap<String, Integer> queue =
|
||||
TreeValueSortedMap.createWithComparator(new ReverseComparator<>());
|
||||
queue.put("2nd", 2);
|
||||
queue.put("1st", 1);
|
||||
queue.put("3rd", 3);
|
||||
|
@ -67,15 +49,101 @@ public class DynamicValueSortedTreeMapTest {
|
|||
assertEquals(Arrays.asList(new String[] { "3rd", "2nd", "1st" }), ordered);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoundsSearches() {
|
||||
ValueSortedMap<String, Integer> queue = TreeValueSortedMap.createWithNaturalOrder();
|
||||
|
||||
assertNull(queue.lowerEntryByValue(4));
|
||||
assertNull(queue.floorEntryByValue(4));
|
||||
assertNull(queue.ceilingEntryByValue(4));
|
||||
assertNull(queue.higherEntryByValue(4));
|
||||
|
||||
assertEquals(-1, queue.values().lowerIndex(4));
|
||||
assertEquals(-1, queue.values().floorIndex(4));
|
||||
assertEquals(-1, queue.values().ceilingIndex(4));
|
||||
assertEquals(-1, queue.values().higherIndex(4));
|
||||
|
||||
queue.put("4th", 4);
|
||||
|
||||
assertNull(queue.lowerEntryByValue(3));
|
||||
assertEquals(-1, queue.values().lowerIndex(3));
|
||||
assertNull(queue.lowerEntryByValue(4));
|
||||
assertEquals(-1, queue.values().lowerIndex(4));
|
||||
assertEquals("4th", queue.lowerEntryByValue(5).getKey());
|
||||
assertEquals(0, queue.values().lowerIndex(5));
|
||||
|
||||
assertNull(queue.floorEntryByValue(3));
|
||||
assertEquals(-1, queue.values().floorIndex(3));
|
||||
assertEquals("4th", queue.floorEntryByValue(4).getKey());
|
||||
assertEquals(0, queue.values().floorIndex(4));
|
||||
assertEquals("4th", queue.floorEntryByValue(5).getKey());
|
||||
assertEquals(0, queue.values().floorIndex(5));
|
||||
|
||||
assertEquals("4th", queue.ceilingEntryByValue(3).getKey());
|
||||
assertEquals(0, queue.values().ceilingIndex(3));
|
||||
assertEquals("4th", queue.ceilingEntryByValue(4).getKey());
|
||||
assertEquals(0, queue.values().ceilingIndex(4));
|
||||
assertNull(queue.ceilingEntryByValue(5));
|
||||
assertEquals(-1, queue.values().ceilingIndex(5));
|
||||
|
||||
assertEquals("4th", queue.higherEntryByValue(3).getKey());
|
||||
assertEquals(0, queue.values().higherIndex(3));
|
||||
assertNull(queue.higherEntryByValue(4));
|
||||
assertEquals(-1, queue.values().higherIndex(4));
|
||||
assertNull(queue.higherEntryByValue(5));
|
||||
assertEquals(-1, queue.values().higherIndex(5));
|
||||
|
||||
queue.put("2nd", 2);
|
||||
queue.put("6th", 6);
|
||||
|
||||
assertNull(queue.lowerEntryByValue(1));
|
||||
assertNull(queue.lowerEntryByValue(2));
|
||||
assertEquals("2nd", queue.lowerEntryByValue(3).getKey());
|
||||
assertEquals("2nd", queue.lowerEntryByValue(4).getKey());
|
||||
assertEquals("4th", queue.lowerEntryByValue(5).getKey());
|
||||
assertEquals("4th", queue.lowerEntryByValue(6).getKey());
|
||||
assertEquals("6th", queue.lowerEntryByValue(7).getKey());
|
||||
assertEquals(2, queue.values().lowerIndex(7)); // Only this once
|
||||
|
||||
assertNull(queue.floorEntryByValue(1));
|
||||
assertEquals("2nd", queue.floorEntryByValue(2).getKey());
|
||||
assertEquals("2nd", queue.floorEntryByValue(3).getKey());
|
||||
assertEquals("4th", queue.floorEntryByValue(4).getKey());
|
||||
assertEquals("4th", queue.floorEntryByValue(5).getKey());
|
||||
assertEquals("6th", queue.floorEntryByValue(6).getKey());
|
||||
assertEquals("6th", queue.floorEntryByValue(7).getKey());
|
||||
|
||||
assertEquals("2nd", queue.ceilingEntryByValue(1).getKey());
|
||||
assertEquals("2nd", queue.ceilingEntryByValue(2).getKey());
|
||||
assertEquals("4th", queue.ceilingEntryByValue(3).getKey());
|
||||
assertEquals("4th", queue.ceilingEntryByValue(4).getKey());
|
||||
assertEquals("6th", queue.ceilingEntryByValue(5).getKey());
|
||||
assertEquals("6th", queue.ceilingEntryByValue(6).getKey());
|
||||
assertNull(queue.ceilingEntryByValue(7));
|
||||
|
||||
assertEquals("2nd", queue.higherEntryByValue(1).getKey());
|
||||
assertEquals("4th", queue.higherEntryByValue(2).getKey());
|
||||
assertEquals("4th", queue.higherEntryByValue(3).getKey());
|
||||
assertEquals("6th", queue.higherEntryByValue(4).getKey());
|
||||
assertEquals("6th", queue.higherEntryByValue(5).getKey());
|
||||
assertNull(queue.higherEntryByValue(6));
|
||||
assertNull(queue.higherEntryByValue(7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEmpty() {
|
||||
DynamicValueSortedTreeMap<String, Integer> queue = new DynamicValueSortedTreeMap<>();
|
||||
ValueSortedMap<String, Integer> queue = TreeValueSortedMap.createWithNaturalOrder();
|
||||
assertTrue(queue.isEmpty());
|
||||
|
||||
assertFalse(queue.containsKey("1st"));
|
||||
assertFalse(queue.containsValue(1));
|
||||
assertEquals(-1, queue.values().indexOf(1));
|
||||
|
||||
queue.put("1st", 1);
|
||||
assertFalse(queue.isEmpty());
|
||||
}
|
||||
|
||||
protected <K, V> void checkConsistent(DynamicValueSortedTreeMap<K, V> queue) {
|
||||
protected <K, V> void checkConsistent(ValueSortedMap<K, V> queue) {
|
||||
Iterator<Entry<K, V>> it = queue.entrySet().iterator();
|
||||
V last = null;
|
||||
Set<K> seen = new HashSet<>();
|
||||
|
@ -119,7 +187,7 @@ public class DynamicValueSortedTreeMapTest {
|
|||
final int COUNT = 1000;
|
||||
final int ROUNDS = 5;
|
||||
Random rand = new Random();
|
||||
DynamicValueSortedTreeMap<String, Integer> queue = new DynamicValueSortedTreeMap<>();
|
||||
ValueSortedMap<String, Integer> queue = TreeValueSortedMap.createWithNaturalOrder();
|
||||
for (int r = 0; r < ROUNDS; r++) {
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
queue.put("Element" + i, rand.nextInt(50));
|
||||
|
@ -133,7 +201,7 @@ public class DynamicValueSortedTreeMapTest {
|
|||
public void testRemoveRandomly() {
|
||||
final int COUNT = 100;
|
||||
Random rand = new Random();
|
||||
DynamicValueSortedTreeMap<String, Integer> queue = new DynamicValueSortedTreeMap<>();
|
||||
ValueSortedMap<String, Integer> queue = TreeValueSortedMap.createWithNaturalOrder();
|
||||
HashSet<String> all = new HashSet<>();
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
queue.put("Element" + i, rand.nextInt(50));
|
||||
|
@ -157,7 +225,7 @@ public class DynamicValueSortedTreeMapTest {
|
|||
public void testUpdateRandomly() {
|
||||
final int COUNT = 100;
|
||||
Random rand = new Random();
|
||||
DynamicValueSortedTreeMap<String, Integer> queue = new DynamicValueSortedTreeMap<>();
|
||||
ValueSortedMap<String, Integer> queue = TreeValueSortedMap.createWithNaturalOrder();
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
queue.put("Element" + i, rand.nextInt(50));
|
||||
}
|
||||
|
@ -176,7 +244,7 @@ public class DynamicValueSortedTreeMapTest {
|
|||
public void testValueIndices() {
|
||||
final int ROUNDS = 1000;
|
||||
Random rand = new Random();
|
||||
DynamicValueSortedTreeMap<String, Integer> queue = new DynamicValueSortedTreeMap<>();
|
||||
ValueSortedMap<String, Integer> queue = TreeValueSortedMap.createWithNaturalOrder();
|
||||
int[] vals = // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
||||
new int[] { 0, 0, 1, 1, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 10 };
|
||||
for (int r = 0; r < ROUNDS; r++) {
|
||||
|
@ -216,7 +284,7 @@ public class DynamicValueSortedTreeMapTest {
|
|||
public void testAsMonotonicQueue() {
|
||||
final int COUNT = 1000;
|
||||
Random rand = new Random();
|
||||
DynamicValueSortedTreeMap<String, Integer> queue = new DynamicValueSortedTreeMap<>();
|
||||
ValueSortedMap<String, Integer> queue = TreeValueSortedMap.createWithNaturalOrder();
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
queue.put("ElementA" + i, rand.nextInt(50));
|
||||
}
|
||||
|
@ -238,4 +306,46 @@ public class DynamicValueSortedTreeMapTest {
|
|||
assertEquals(0, queue.size());
|
||||
assertTrue(queue.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearViaKeyIterator() {
|
||||
ValueSortedMap<String, Integer> queue = TreeValueSortedMap.createWithNaturalOrder();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
queue.put("Element" + i, i);
|
||||
}
|
||||
Iterator<String> kit = queue.keySet().iterator();
|
||||
while (kit.hasNext()) {
|
||||
kit.next();
|
||||
kit.remove();
|
||||
}
|
||||
|
||||
assertTrue(queue.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveOddsViaValueIterator() {
|
||||
ValueSortedMap<String, Integer> queue = TreeValueSortedMap.createWithNaturalOrder();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
queue.put("Element" + i, i);
|
||||
}
|
||||
Iterator<Integer> vit = queue.values().iterator();
|
||||
while (vit.hasNext()) {
|
||||
int val = vit.next();
|
||||
if (val % 2 == 1) {
|
||||
vit.remove();
|
||||
}
|
||||
}
|
||||
|
||||
for (int val : queue.values()) {
|
||||
assertEquals(0, val % 2);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testNominalBehaviorIteratorRemoveBeforeNext() {
|
||||
Set<Integer> set = new HashSet<>();
|
||||
set.add(5);
|
||||
Iterator<Integer> it = set.iterator();
|
||||
it.remove();
|
||||
}
|
||||
}
|
|
@ -1,211 +0,0 @@
|
|||
/* ###
|
||||
* 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.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DynamicSortedTreeSetTest {
|
||||
public static class NonComparable {
|
||||
public NonComparable(String key, int cost) {
|
||||
this.key = key;
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + "=" + cost;
|
||||
}
|
||||
|
||||
protected String key;
|
||||
protected int cost;
|
||||
}
|
||||
|
||||
public static class TestElem extends NonComparable implements Comparable<TestElem> {
|
||||
public TestElem(String key, int cost) {
|
||||
super(key, cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TestElem that) {
|
||||
return key.compareTo(that.key);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CostComparator implements Comparator<TestElem> {
|
||||
@Override
|
||||
public int compare(TestElem a, TestElem b) {
|
||||
return a.cost - b.cost;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalOrder() {
|
||||
DynamicSortedTreeSet<String> queue = new DynamicSortedTreeSet<>();
|
||||
queue.add("2nd");
|
||||
queue.add("1st");
|
||||
queue.add("3rd");
|
||||
List<String> ordered = new ArrayList<>(queue);
|
||||
assertEquals(Arrays.asList(new String[] { "1st", "2nd", "3rd" }), ordered);
|
||||
}
|
||||
|
||||
@Test(expected = ClassCastException.class)
|
||||
public void testUnorderedError() {
|
||||
DynamicSortedTreeSet<NonComparable> queue = new DynamicSortedTreeSet<>();
|
||||
queue.add(new NonComparable("2nd", 2));
|
||||
queue.add(new NonComparable("1st", 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExplicitOrdered() {
|
||||
DynamicSortedTreeSet<TestElem> queue = new DynamicSortedTreeSet<>(new CostComparator());
|
||||
queue.add(new TestElem("2ndB", 2));
|
||||
queue.add(new TestElem("2ndA", 2));
|
||||
queue.add(new TestElem("1st", 1));
|
||||
queue.add(new TestElem("3rd", 3));
|
||||
List<String> ordered = new ArrayList<>();
|
||||
for (TestElem elem : queue) {
|
||||
ordered.add(elem.key);
|
||||
}
|
||||
assertEquals(Arrays.asList(new String[] { "1st", "2ndB", "2ndA", "3rd" }), ordered);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEmpty() {
|
||||
DynamicSortedTreeSet<TestElem> queue = new DynamicSortedTreeSet<>(new CostComparator());
|
||||
assertTrue(queue.isEmpty());
|
||||
queue.add(new TestElem("1st", 1));
|
||||
assertFalse(queue.isEmpty());
|
||||
}
|
||||
|
||||
protected <E> void checkConsistent(DynamicSortedTreeSet<E> queue, Comparator<E> comp) {
|
||||
Iterator<E> it = queue.iterator();
|
||||
E last = null;
|
||||
Set<E> seen = new HashSet<>();
|
||||
for (int i = 0; i < queue.size(); i++) {
|
||||
E e = it.next();
|
||||
assertTrue("Indices and iterator did not give same order", queue.get(i) == e);
|
||||
assertEquals("Incorrect computed index", i, queue.indexOf(e));
|
||||
if (!seen.add(e)) {
|
||||
fail("Unique index did not give unique element");
|
||||
}
|
||||
if (last != null && comp.compare(last, e) > 0) {
|
||||
fail("Costs should be monotonic");
|
||||
}
|
||||
last = e;
|
||||
}
|
||||
for (int i = queue.size(); i < queue.size() * 2; i++) {
|
||||
try {
|
||||
queue.get(i);
|
||||
fail();
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
for (int i = -queue.size(); i < 0; i++) {
|
||||
try {
|
||||
queue.get(i);
|
||||
fail();
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddRandomly() {
|
||||
final int COUNT = 1000;
|
||||
final int ROUNDS = 10;
|
||||
Random rand = new Random();
|
||||
CostComparator comp = new CostComparator();
|
||||
DynamicSortedTreeSet<TestElem> queue = new DynamicSortedTreeSet<>(comp);
|
||||
for (int r = 0; r < ROUNDS; r++) {
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
queue.add(new TestElem("Element" + i, rand.nextInt(50)));
|
||||
}
|
||||
checkConsistent(queue, comp);
|
||||
queue.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveRandomly() {
|
||||
final int COUNT = 100;
|
||||
Random rand = new Random();
|
||||
CostComparator comp = new CostComparator();
|
||||
DynamicSortedTreeSet<TestElem> queue = new DynamicSortedTreeSet<>(comp);
|
||||
HashSet<TestElem> all = new HashSet<>();
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
TestElem e = new TestElem("Element" + i, rand.nextInt(50));
|
||||
queue.add(e);
|
||||
all.add(e);
|
||||
}
|
||||
checkConsistent(queue, comp);
|
||||
|
||||
TestElem[] shuffled = all.toArray(new TestElem[all.size()]);
|
||||
for (int i = 0; i < shuffled.length; i++) {
|
||||
ArrayUtils.swap(shuffled, i, i + rand.nextInt(shuffled.length - i));
|
||||
}
|
||||
for (TestElem e : shuffled) {
|
||||
queue.remove(e);
|
||||
checkConsistent(queue, comp);
|
||||
}
|
||||
assertTrue(queue.isEmpty());
|
||||
assertTrue(queue.size() == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateRandomly() {
|
||||
final int COUNT = 100;
|
||||
Random rand = new Random();
|
||||
CostComparator comp = new CostComparator();
|
||||
DynamicSortedTreeSet<TestElem> queue = new DynamicSortedTreeSet<>(comp);
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
queue.add(new TestElem("Element" + i, rand.nextInt(50)));
|
||||
}
|
||||
checkConsistent(queue, comp);
|
||||
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
TestElem e = queue.get(rand.nextInt(queue.size()));
|
||||
int oldCost = e.cost;
|
||||
if (rand.nextInt(2) == 0) {
|
||||
e.cost = rand.nextInt(50);
|
||||
}
|
||||
boolean result = queue.update(e);
|
||||
if (oldCost == e.cost) {
|
||||
assertEquals(false, result);
|
||||
}
|
||||
// NOTE: A different cost does not necessarily promote the updated element
|
||||
checkConsistent(queue, comp);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue