mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 04:05:39 +00:00
GP-4534 Use replacement strategy for MemoryMapDB allAddrsSet update
This commit is contained in:
parent
4494e32596
commit
43cd7f331a
@ -107,7 +107,7 @@ public class MemoryBlockDB implements MemoryBlock {
|
||||
* @return collection of blocks which map onto this block or null if none identified
|
||||
*/
|
||||
Collection<MemoryBlockDB> getMappedBlocks() {
|
||||
memMap.buildAddressSets(); // updates mappedBlocks if needed
|
||||
memMap.buildAddressSets(false); // updates mappedBlocks if needed
|
||||
return mappedBlocks;
|
||||
}
|
||||
|
||||
|
@ -52,15 +52,15 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
|
||||
private DataConverter defaultEndian;
|
||||
private List<MemoryBlockDB> blocks;// sorted list of blocks
|
||||
private SynchronizedAddressSet allAddrSet = new SynchronizedAddressSet(); // continuously updated
|
||||
private AddressSetView allAddrSet = new AddressSetViewAdapter(); // replaced on update
|
||||
|
||||
private MemoryAddressSetView addrSetView;
|
||||
private MemoryAddressSetViews addrSetViews;
|
||||
|
||||
/**
|
||||
* Address set views into program memory which are iterator safe
|
||||
* for public API methods.
|
||||
*/
|
||||
private class MemoryAddressSetView {
|
||||
private class MemoryAddressSetViews {
|
||||
private AddressSet initializedAndLoaded = new AddressSet();
|
||||
private AddressSet initialized = new AddressSet();
|
||||
private AddressSet externalBlock = new AddressSet();
|
||||
@ -103,7 +103,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
adapter = MemoryMapDBAdapter.getAdapter(handle, openMode, this, monitor);
|
||||
fileBytesAdapter = FileBytesAdapter.getAdapter(handle, openMode, monitor);
|
||||
initializeBlocks();
|
||||
buildAddressSets();
|
||||
buildAddressSets(true);
|
||||
}
|
||||
|
||||
// for testing
|
||||
@ -135,45 +135,62 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
}
|
||||
}
|
||||
|
||||
MemoryAddressSetView buildAddressSets() {
|
||||
if (addrSetView != null) {
|
||||
return addrSetView;
|
||||
/**
|
||||
* Get the address set views {@code addrSetView} and rebuild if needed and optionally
|
||||
* rebuild {@code allAddrSet}. This method also updates mapped-block details when
|
||||
* rebuilding the views.
|
||||
* <br>
|
||||
* NOTE: The {@link #initializeBlocks()} method is used to invalidate the {code addrSetViews}
|
||||
* without affecting {@code allAddrSet}, while {@link #reloadAll()} will force a complete
|
||||
* rebuild of all addresss sets.
|
||||
*
|
||||
* @param rebuildAllAddrSets if true all address sets will be rebuilt before returning the
|
||||
* address set view object.
|
||||
* @return the address set view object
|
||||
*/
|
||||
MemoryAddressSetViews buildAddressSets(boolean rebuildAllAddrSets) {
|
||||
MemoryAddressSetViews localAddrSetViews = addrSetViews;
|
||||
if (!rebuildAllAddrSets && localAddrSetViews != null) {
|
||||
return localAddrSetViews;
|
||||
}
|
||||
lock.acquire();
|
||||
try {
|
||||
// have to try and get it again, another thread may have already filled it out
|
||||
if (addrSetView != null) {
|
||||
return addrSetView;
|
||||
if (!rebuildAllAddrSets && addrSetViews != null) {
|
||||
return addrSetViews;
|
||||
}
|
||||
|
||||
// set cached addressSet view to null, so other threads will end up here waiting
|
||||
// on the above lock if they try to access the cached address sets
|
||||
addrSetView = null;
|
||||
MemoryAddressSetView newAddrSetView = new MemoryAddressSetView();
|
||||
|
||||
// The allAddrSet instance is generally maintained with all memory
|
||||
// block addresses and need only be updated if currently empty.
|
||||
// Any time allAddrSet is modified addrSetView is set to null to
|
||||
// signal it becoming stale.
|
||||
boolean addToAll = allAddrSet.isEmpty();
|
||||
// Begin rebuilding a complete set of address set views
|
||||
MemoryAddressSetViews newAddrSetViews = new MemoryAddressSetViews();
|
||||
|
||||
// The allAddrSet instance is generally kept up-to-date with all memory
|
||||
// block addresses and need only be rebuilt under certain conditions as
|
||||
// signaled by the rebuildAllAddrs parameter.
|
||||
AddressSet newAllAddrs = null;
|
||||
if (rebuildAllAddrSets) {
|
||||
newAllAddrs = new AddressSet();
|
||||
}
|
||||
|
||||
// we have to process the non-mapped blocks first because to process the mapped
|
||||
// blocks we need the address sets for the non-mapped blocks to be complete
|
||||
for (MemoryBlockDB block : blocks) {
|
||||
block.clearMappedBlockList();
|
||||
if (!block.isMapped()) {
|
||||
addBlockAddresses(newAddrSetView, block, addToAll);
|
||||
addBlockAddresses(block, newAddrSetViews, newAllAddrs);
|
||||
}
|
||||
}
|
||||
// process all mapped blocks after non-mapped-blocks above
|
||||
for (MemoryBlockDB block : blocks) {
|
||||
if (block.isMapped()) {
|
||||
addBlockAddresses(newAddrSetView, block, addToAll);
|
||||
addBlockAddresses(block, newAddrSetViews, newAllAddrs);
|
||||
}
|
||||
}
|
||||
addrSetView = newAddrSetView;
|
||||
|
||||
return addrSetView;
|
||||
if (newAllAddrs != null) {
|
||||
// replace allAddrSet with fully updated address set
|
||||
allAddrSet = new AddressSetViewAdapter(newAllAddrs);
|
||||
}
|
||||
addrSetViews = newAddrSetViews;
|
||||
return addrSetViews;
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@ -181,23 +198,26 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the <code>addrSetView</code> address sets with relevant addresses from the
|
||||
* specified memory block. In addition, allAddrSet will be updated if addToAll parameter is true.
|
||||
* Update address sets with relevant addresses from the specified memory block.
|
||||
* In addition, allAddrSet will be updated if addToAll parameter is true.
|
||||
*
|
||||
* @param block memory block
|
||||
* @param addToAll if true the allAddrSet should be updated with the specified block's address range
|
||||
* @param block memory block to be added
|
||||
* @param newAddrSetViews address set views which should be built-up
|
||||
* @param newAllAddrs if not null this set will be updated with the specified block's address range,
|
||||
* otherwise only the {@code addrSetView} sets will be updated.
|
||||
*/
|
||||
private void addBlockAddresses(MemoryAddressSetView newSet, MemoryBlockDB block, boolean addToAll) {
|
||||
private void addBlockAddresses(MemoryBlockDB block, MemoryAddressSetViews newAddrSetViews,
|
||||
AddressSet newAllAddrs) {
|
||||
Address start = block.getStart();
|
||||
Address end = block.getEnd();
|
||||
if (addToAll) {
|
||||
allAddrSet.add(start, end);
|
||||
if (newAllAddrs != null) {
|
||||
newAllAddrs.add(start, end);
|
||||
}
|
||||
if (block.isExternalBlock()) {
|
||||
newSet.externalBlock.add(start, end);
|
||||
newAddrSetViews.externalBlock.add(start, end);
|
||||
}
|
||||
else if (block.isExecute()) {
|
||||
newSet.execute.add(start, end);
|
||||
newAddrSetViews.execute.add(start, end);
|
||||
}
|
||||
if (block.isMapped()) {
|
||||
// Identify source-blocks which block maps onto and add as a mapped-block to each of these
|
||||
@ -208,26 +228,37 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
b.addMappedBlock(block);
|
||||
}
|
||||
}
|
||||
AddressSet mappedSet = getMappedIntersection(block, newSet.initialized);
|
||||
newSet.initialized.add(mappedSet);
|
||||
newSet.initializedAndLoaded
|
||||
.add(getMappedIntersection(block, newSet.initializedAndLoaded));
|
||||
AddressSet mappedSet = getMappedIntersection(block, newAddrSetViews.initialized);
|
||||
newAddrSetViews.initialized.add(mappedSet);
|
||||
newAddrSetViews.initializedAndLoaded
|
||||
.add(getMappedIntersection(block, newAddrSetViews.initializedAndLoaded));
|
||||
}
|
||||
else if (block.isInitialized()) {
|
||||
newSet.initialized.add(block.getStart(), block.getEnd());
|
||||
newAddrSetViews.initialized.add(block.getStart(), block.getEnd());
|
||||
if (block.isLoaded()) {
|
||||
newSet.initializedAndLoaded.add(block.getStart(), block.getEnd());
|
||||
newAddrSetViews.initializedAndLoaded.add(block.getStart(), block.getEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addToAllAddressSet(Address minAddr, Address maxAddr) {
|
||||
AddressSet updatedAllAddrSet = new AddressSet(allAddrSet);
|
||||
updatedAllAddrSet.add(minAddr, maxAddr);
|
||||
allAddrSet = new AddressSetViewAdapter(updatedAllAddrSet);
|
||||
}
|
||||
|
||||
private void removeFromAllAddressSet(Address minAddr, Address maxAddr) {
|
||||
AddressSet updatedAllAddrSet = new AddressSet(allAddrSet);
|
||||
updatedAllAddrSet.delete(minAddr, maxAddr);
|
||||
allAddrSet = new AddressSetViewAdapter(updatedAllAddrSet);
|
||||
}
|
||||
|
||||
private void reloadAll() throws IOException {
|
||||
synchronized (this) {
|
||||
fileBytesAdapter.refresh();
|
||||
adapter.refreshMemory();
|
||||
allAddrSet = new SynchronizedAddressSet(); // buildAddressSets() will populate
|
||||
initializeBlocks();
|
||||
buildAddressSets();
|
||||
buildAddressSets(true);
|
||||
}
|
||||
if (liveMemory != null) {
|
||||
liveMemory.clearCache();
|
||||
@ -239,7 +270,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
blocks = adapter.getMemoryBlocks();
|
||||
lastBlock = null;
|
||||
nameBlockMap = new HashMap<>();
|
||||
addrSetView = null; // signal stale view
|
||||
addrSetViews = null; // signal stale views
|
||||
addrMap.memoryMapChanged(this);
|
||||
if (program != null) {
|
||||
program.getAddressFactory().invalidateOverlayCache();
|
||||
@ -248,18 +279,18 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
|
||||
void blockExecuteChanged(MemoryBlockDB block) {
|
||||
// lock must be active
|
||||
if (addrSetView == null) {
|
||||
if (addrSetViews == null) {
|
||||
return;
|
||||
}
|
||||
// copy must be made to remain iterator safe
|
||||
AddressSet set = new AddressSet(addrSetView.execute);
|
||||
AddressSet set = new AddressSet(addrSetViews.execute);
|
||||
if (block.isExecute()) {
|
||||
set.addRange(block.getStart(), block.getEnd());
|
||||
}
|
||||
else {
|
||||
set.deleteRange(block.getStart(), block.getEnd());
|
||||
}
|
||||
addrSetView.execute = set;
|
||||
addrSetViews.execute = set;
|
||||
}
|
||||
|
||||
public void setLanguage(Language newLanguage) {
|
||||
@ -322,8 +353,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
|
||||
@Override
|
||||
public AddressSetView getAllInitializedAddressSet() {
|
||||
MemoryAddressSetView localAddrSetView = buildAddressSets();
|
||||
return new AddressSetViewAdapter(localAddrSetView.initialized);
|
||||
MemoryAddressSetViews localAddrSetViews = buildAddressSets(false);
|
||||
return new AddressSetViewAdapter(localAddrSetViews.initialized);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -331,21 +362,21 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
if (liveMemory != null) {
|
||||
return this; // all memory is initialized!
|
||||
}
|
||||
|
||||
MemoryAddressSetView localAddrSetView = buildAddressSets();
|
||||
return new AddressSetViewAdapter(localAddrSetView.initializedAndLoaded);
|
||||
|
||||
MemoryAddressSetViews localAddrSetViews = buildAddressSets(false);
|
||||
return new AddressSetViewAdapter(localAddrSetViews.initializedAndLoaded);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isExternalBlockAddress(Address addr) {
|
||||
MemoryAddressSetView localAddrSetView = buildAddressSets();
|
||||
return localAddrSetView.externalBlock.contains(addr);
|
||||
MemoryAddressSetViews localAddrSetViews = buildAddressSets(false);
|
||||
return localAddrSetViews.externalBlock.contains(addr);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AddressSetView getExecuteSet() {
|
||||
MemoryAddressSetView localAddrSetView = buildAddressSets();
|
||||
return new AddressSetViewAdapter(localAddrSetView.execute);
|
||||
MemoryAddressSetViews localAddrSetViews = buildAddressSets(false);
|
||||
return new AddressSetViewAdapter(localAddrSetViews.execute);
|
||||
}
|
||||
|
||||
void checkMemoryWrite(MemoryBlockDB block, Address start, long length)
|
||||
@ -489,7 +520,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
return null;
|
||||
}
|
||||
|
||||
void fireBlockAdded(MemoryBlock newBlock) {
|
||||
private void fireBlockAdded(MemoryBlock newBlock) {
|
||||
AddressRange range = new AddressRangeImpl(newBlock.getStart(), newBlock.getEnd());
|
||||
program.getTreeManager().addMemoryBlock(newBlock.getName(), range);
|
||||
program.setChanged(ProgramEvent.MEMORY_BLOCK_ADDED, newBlock.getStart(), newBlock.getEnd(),
|
||||
@ -497,16 +528,17 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
program.fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
}
|
||||
|
||||
void fireBlockSplit() {
|
||||
private void fireBlockSplit(MemoryBlockDB originalBlock, MemoryBlockDB newBlock) {
|
||||
program.setChanged(ProgramEvent.MEMORY_BLOCK_SPLIT, null, null, originalBlock, newBlock);
|
||||
program.fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
}
|
||||
|
||||
void fireBlockRemoved(Address blockStartAddr) {
|
||||
private void fireBlockRemoved(Address blockStartAddr) {
|
||||
program.setChanged(ProgramEvent.MEMORY_BLOCK_REMOVED, blockStartAddr, null);
|
||||
program.fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
}
|
||||
|
||||
void fireBlockMoved(MemoryBlockDB block, Address oldStartAddr) {
|
||||
private void fireBlockMoved(MemoryBlockDB block, Address oldStartAddr) {
|
||||
program.setChanged(ProgramEvent.MEMORY_BLOCKS_JOINED, oldStartAddr, block);
|
||||
program.fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
}
|
||||
@ -518,14 +550,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
* @param newBlock new joined memory block
|
||||
* @param oldBlockStartAddr original start address of affected block
|
||||
*/
|
||||
void fireBlocksJoined(MemoryBlock newBlock, Address oldBlockStartAddr) {
|
||||
private void fireBlocksJoined(MemoryBlock newBlock, Address oldBlockStartAddr) {
|
||||
program.setChanged(ProgramEvent.MEMORY_BLOCKS_JOINED, oldBlockStartAddr, newBlock);
|
||||
}
|
||||
|
||||
void fireBlockSplit(MemoryBlockDB originalBlock, MemoryBlockDB newBlock) {
|
||||
program.setChanged(ProgramEvent.MEMORY_BLOCK_SPLIT, null, null, originalBlock, newBlock);
|
||||
}
|
||||
|
||||
void fireBlockChanged(MemoryBlock block) {
|
||||
if (program != null) {
|
||||
program.setChanged(ProgramEvent.MEMORY_BLOCK_CHANGED, block, null);
|
||||
@ -685,7 +713,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
try {
|
||||
MemoryBlockDB newBlock =
|
||||
adapter.createInitializedBlock(name, start, is, length, MemoryBlock.READ);
|
||||
allAddrSet.add(newBlock.getStart(), newBlock.getEnd());
|
||||
addToAllAddressSet(newBlock.getStart(), newBlock.getEnd());
|
||||
initializeBlocks();
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
@ -729,7 +757,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
try {
|
||||
MemoryBlockDB newBlock = adapter.createFileBytesBlock(name, start, length,
|
||||
fileBytes, offset, MemoryBlock.READ);
|
||||
allAddrSet.add(newBlock.getStart(), newBlock.getEnd());
|
||||
addToAllAddressSet(newBlock.getStart(), newBlock.getEnd());
|
||||
initializeBlocks();
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
@ -782,7 +810,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
try {
|
||||
MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.DEFAULT, name, start,
|
||||
size, null, false, MemoryBlock.READ, 0);
|
||||
allAddrSet.add(newBlock.getStart(), newBlock.getEnd());
|
||||
addToAllAddressSet(newBlock.getStart(), newBlock.getEnd());
|
||||
initializeBlocks();
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
@ -817,7 +845,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
try {
|
||||
MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.BIT_MAPPED, name,
|
||||
start, length, mappedAddress, false, MemoryBlock.READ, 0);
|
||||
allAddrSet.add(newBlock.getStart(), newBlock.getEnd());
|
||||
addToAllAddressSet(newBlock.getStart(), newBlock.getEnd());
|
||||
initializeBlocks();
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
@ -861,7 +889,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
try {
|
||||
MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.BYTE_MAPPED, name,
|
||||
start, length, mappedAddress, false, MemoryBlock.READ, mappingScheme);
|
||||
allAddrSet.add(newBlock.getStart(), newBlock.getEnd());
|
||||
addToAllAddressSet(newBlock.getStart(), newBlock.getEnd());
|
||||
initializeBlocks();
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
@ -909,7 +937,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
}
|
||||
MemoryBlockDB newBlock = adapter.createBlock(block.getType(), name, start, length,
|
||||
mappedAddr, block.isInitialized(), block.getFlags(), mappingScheme);
|
||||
allAddrSet.add(newBlock.getStart(), newBlock.getEnd());
|
||||
addToAllAddressSet(newBlock.getStart(), newBlock.getEnd());
|
||||
initializeBlocks();
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
@ -1031,9 +1059,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
throw new IllegalArgumentException("Split cannot be done on a bit mapped block");
|
||||
}
|
||||
try {
|
||||
memBlock.split(addr);
|
||||
MemoryBlockDB newBlock = memBlock.split(addr);
|
||||
initializeBlocks();
|
||||
fireBlockSplit();
|
||||
fireBlockSplit(memBlock, newBlock);
|
||||
}
|
||||
catch (IOException e) {
|
||||
program.dbError(e);
|
||||
@ -1919,7 +1947,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
try {
|
||||
program.deleteAddressRange(startAddress, endAddress, monitor);
|
||||
memBlock.delete();
|
||||
allAddrSet.delete(startAddress, endAddress);
|
||||
removeFromAllAddressSet(startAddress, endAddress);
|
||||
initializeBlocks();
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
@ -1,122 +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.program.database.mem;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
/**
|
||||
* <code>RecoverableAddressIterator</code> provides the ability to iterator over an {@link AddressSet}
|
||||
* which is getting modified concurrent with the iteration of Addresses contained within it. Do to
|
||||
* multiple levels of prefetch caching, the results returned may be stale relative to the actual
|
||||
* {@link AddressSet} at any point in time. The primary intent is to return addresses in proper order
|
||||
* and avoid throwing a {@link ConcurrentModificationException} which the standard iterators are
|
||||
* subject to.
|
||||
* <p>
|
||||
* NOTES:
|
||||
* <ol>
|
||||
* <li>The iterator methods are not symchronized but could be made so if restricted to
|
||||
* use in conjunction with the {@link SynchronizedAddressSet} where it would synchronize on
|
||||
* the set itself.</li>
|
||||
* <li>This class and {@link SynchronizedAddressSet} could be made public alongside {@link AddressSet}
|
||||
* if so desired in the future. Its current use has been limited until proven to be thread-safe
|
||||
* and useful.</li>
|
||||
* </ol>
|
||||
*/
|
||||
class RecoverableAddressIterator implements AddressIterator {
|
||||
|
||||
private SynchronizedAddressSet synchSet;
|
||||
private AddressSet internalSet;
|
||||
private boolean forward;
|
||||
private AddressIterator iterator;
|
||||
private Address next;
|
||||
private int changeID;
|
||||
|
||||
/**
|
||||
* Construct iterator
|
||||
* @param set address set
|
||||
* @param start address to start iterating at in the address set or null for all addresses
|
||||
* @param forward if true address are return from lowest to highest, else from highest to lowest
|
||||
*/
|
||||
RecoverableAddressIterator(SynchronizedAddressSet set, Address start, boolean forward) {
|
||||
this.synchSet = set;
|
||||
this.internalSet = set.getInternalSet();
|
||||
changeID = set.getModificationID();
|
||||
this.forward = forward;
|
||||
initIterator(start);
|
||||
this.next = iterator.next();
|
||||
}
|
||||
|
||||
private void initIterator(Address start) {
|
||||
if (start == null) {
|
||||
iterator = internalSet.getAddresses(forward);
|
||||
}
|
||||
else {
|
||||
iterator = internalSet.getAddresses(start, forward);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Address> iterator() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address next() {
|
||||
Address addr = next;
|
||||
if (addr != null) {
|
||||
try {
|
||||
if (synchSet.hasChanged(changeID)) {
|
||||
next = recoverNext(addr);
|
||||
} else {
|
||||
next = iterator.next();
|
||||
}
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
// will never happen, set in hand is never changed
|
||||
next = recoverNext(addr);
|
||||
}
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
private Address recoverNext(Address lastAddr) {
|
||||
changeID = synchSet.getModificationID();
|
||||
internalSet = synchSet.getInternalSet();
|
||||
while (true) {
|
||||
try {
|
||||
initIterator(lastAddr);
|
||||
Address a = iterator.next();
|
||||
if (a != null && a.equals(lastAddr)) {
|
||||
a = iterator.next();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
// will never happen, set in hand is never changed
|
||||
// set must have changed - try re-initializing again
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
}
|
@ -1,142 +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.program.database.mem;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
/**
|
||||
* <code>RecoverableAddressRangeIterator</code> provides the ability to iterator over an {@link AddressSet}
|
||||
* which is getting modified concurrent with the iteration of {@link AddressRange}es contained within it. Do to
|
||||
* multiple levels of prefetch caching, the results returned may be stale relative to the actual
|
||||
* {@link AddressSet} at any point in time. The primary intent is to return address ranges in proper order
|
||||
* and avoid throwing a {@link ConcurrentModificationException} which the standard iterators are
|
||||
* subject to.
|
||||
* <p>
|
||||
* NOTES:
|
||||
* <ol>
|
||||
* <li>The iterator methods are not symchronized but could be made so if restricted to
|
||||
* use in conjunction with the {@link SynchronizedAddressSet} where it would synchronize on
|
||||
* the set itself.</li>
|
||||
* <li>This class and {@link SynchronizedAddressSet} could be made public alongside {@link AddressSet}
|
||||
* if so desired in the future. Its current use has been limited until proven to be thread-safe
|
||||
* and useful.</li>
|
||||
* </ol>
|
||||
*/
|
||||
class RecoverableAddressRangeIterator implements AddressRangeIterator {
|
||||
|
||||
private SynchronizedAddressSet synchSet;
|
||||
private AddressSet internalSet;
|
||||
private boolean forward;
|
||||
private AddressRangeIterator iterator;
|
||||
private AddressRange next;
|
||||
private int changeID;
|
||||
|
||||
/**
|
||||
* Construct iterator
|
||||
* @param set address set
|
||||
* @param start the address the the first range should contain.
|
||||
* @param forward true iterators forward, false backwards
|
||||
*/
|
||||
RecoverableAddressRangeIterator(SynchronizedAddressSet set, Address start, boolean forward) {
|
||||
this.synchSet = set;
|
||||
this.internalSet = set.getInternalSet();
|
||||
changeID = set.getModificationID();
|
||||
this.forward = forward;
|
||||
initIterator(start);
|
||||
try {
|
||||
this.next = iterator.next();
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
this.next = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initIterator(Address start) {
|
||||
if (start == null) {
|
||||
iterator = internalSet.getAddressRanges(forward);
|
||||
}
|
||||
else {
|
||||
iterator = internalSet.getAddressRanges(start, forward);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange next() throws NoSuchElementException {
|
||||
AddressRange range = next;
|
||||
if (range == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
try {
|
||||
if (synchSet.hasChanged(changeID)) {
|
||||
next = recoverNext(range);
|
||||
} else {
|
||||
next = iterator.next();
|
||||
}
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
// will never happen, set in hand is never changed
|
||||
next = recoverNext(range);
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
next = null;
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
private AddressRange recoverNext(AddressRange lastRange) {
|
||||
changeID = synchSet.getModificationID();
|
||||
internalSet = synchSet.getInternalSet();
|
||||
while (true) {
|
||||
try {
|
||||
Address lastAddr = forward ? lastRange.getMaxAddress() : lastRange.getMinAddress();
|
||||
initIterator(lastAddr);
|
||||
AddressRange r = iterator.next();
|
||||
if (!r.intersects(lastRange)) {
|
||||
return r;
|
||||
}
|
||||
if (forward) {
|
||||
if (r.getMaxAddress().compareTo(lastAddr) > 0) {
|
||||
return new AddressRangeImpl(lastAddr.next(), r.getMaxAddress());
|
||||
}
|
||||
}
|
||||
else if (r.getMinAddress().compareTo(lastAddr) < 0) { // reverse
|
||||
return new AddressRangeImpl(r.getMinAddress(), lastAddr.previous());
|
||||
}
|
||||
return iterator.next(); // skip range and return next
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
// will never happen, set in hand is never changed
|
||||
// set must have changed - try re-initializing again
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
}
|
@ -1,253 +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.program.database.mem;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
/**
|
||||
* <code>SynchronizedAddressSet</code> provides a synchronized address set which
|
||||
* implements the {@link AddressSetView} interface. Iterators returned by this
|
||||
* implementation will recover from concurrent modification of this address set.
|
||||
* See {@link RecoverableAddressRangeIterator} and {@link RecoverableAddressIterator}.
|
||||
*/
|
||||
class SynchronizedAddressSet implements AddressSetView {
|
||||
|
||||
private AddressSet set;
|
||||
private int modificationID = 1; // updated if set above ever replaced
|
||||
|
||||
SynchronizedAddressSet() {
|
||||
set = new AddressSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* get the modification number of the internal address set
|
||||
* If the underlying set if ever replaced, modification id is changed
|
||||
* @return current modification id
|
||||
*/
|
||||
int getModificationID() {
|
||||
return modificationID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all addresses of the given AddressSet to this set.
|
||||
* @param addrSet set of addresses to add.
|
||||
* @see AddressSet#add(AddressSetView)
|
||||
*/
|
||||
synchronized void add(AddressSet addrSet) {
|
||||
AddressSet newSet = new AddressSet(set);
|
||||
newSet.add(addrSet);
|
||||
set = newSet;
|
||||
modificationID++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the range to this set
|
||||
* @param start the start address of the range to add
|
||||
* @param end the end address of the range to add
|
||||
* @see AddressSet#add(Address, Address)
|
||||
*/
|
||||
synchronized void add(Address start, Address end) {
|
||||
AddressSet newSet = new AddressSet(set);
|
||||
newSet.add(start, end);
|
||||
set = newSet;
|
||||
modificationID++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a range of addresses from this set
|
||||
* @param start the starting address of the range to be removed
|
||||
* @param end the ending address of the range to be removed (inclusive)
|
||||
* @see AddressSet#delete(Address, Address)
|
||||
*/
|
||||
synchronized void delete(Address start, Address end) {
|
||||
AddressSet newSet = new AddressSet(set);
|
||||
newSet.delete(start, end);
|
||||
set = newSet;
|
||||
modificationID++;
|
||||
}
|
||||
|
||||
synchronized AddressSet getInternalSet() {
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the internal set has been modified
|
||||
* If the mod id is different, then the set has changed.
|
||||
*
|
||||
* @param modID modification id to check
|
||||
* @return true if internal mod id is different
|
||||
*/
|
||||
public boolean hasChanged(int modID) {
|
||||
return modID != modificationID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address addr) {
|
||||
return set.contains(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address start, Address end) {
|
||||
return set.contains(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(AddressSetView addrSet) {
|
||||
return set.contains(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return set.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMinAddress() {
|
||||
return set.getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMaxAddress() {
|
||||
return set.getMaxAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumAddressRanges() {
|
||||
return set.getNumAddressRanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges() {
|
||||
return set.getAddressRanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(boolean forward) {
|
||||
return set.getAddressRanges(forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressRangeIterator getAddressRanges(Address start, boolean forward) {
|
||||
return new RecoverableAddressRangeIterator(this, start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Iterator<AddressRange> iterator() {
|
||||
return set.getAddressRanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator(boolean forward) {
|
||||
return set.getAddressRanges(forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator(Address start, boolean forward) {
|
||||
return set.getAddressRanges(start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNumAddresses() {
|
||||
return set.getNumAddresses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressIterator getAddresses(boolean forward) {
|
||||
return new RecoverableAddressIterator(this, null, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressIterator getAddresses(Address start, boolean forward) {
|
||||
return new RecoverableAddressIterator(this, start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(AddressSetView addrSet) {
|
||||
return set.intersects(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Address start, Address end) {
|
||||
return set.intersects(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet intersect(AddressSetView addrSet) {
|
||||
return set.intersect(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet intersectRange(Address start, Address end) {
|
||||
return set.intersectRange(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet union(AddressSetView addrSet) {
|
||||
return set.union(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet subtract(AddressSetView addrSet) {
|
||||
return set.subtract(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet xor(AddressSetView addrSet) {
|
||||
return set.xor(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSameAddresses(AddressSetView addrSet) {
|
||||
return set.hasSameAddresses(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getFirstRange() {
|
||||
return set.getFirstRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getLastRange() {
|
||||
return set.getLastRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRangeContaining(Address address) {
|
||||
return set.getRangeContaining(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address findFirstAddressInCommon(AddressSetView addrSet) {
|
||||
return set.findFirstAddressInCommon(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return set.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return set.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return set.toString();
|
||||
}
|
||||
}
|
@ -33,6 +33,13 @@ public class AddressSetViewAdapter implements AddressSetView {
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an empty AddressSetViewAdapter.
|
||||
*/
|
||||
public AddressSetViewAdapter() {
|
||||
this.set = new AddressSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address addr) {
|
||||
return set.contains(addr);
|
||||
|
@ -1,174 +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.program.database.mem;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class RecoverableAddressIteratorTest extends AbstractGenericTest {
|
||||
|
||||
private AddressSpace space;
|
||||
private AddressSet set;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
space = new GenericAddressSpace("xx", 32, AddressSpace.TYPE_RAM, 0);
|
||||
|
||||
set = new AddressSet();
|
||||
set.add(range(0x100, 0x200));
|
||||
set.add(range(0x250, 0x250));
|
||||
set.add(range(0x300, 0x400));
|
||||
}
|
||||
|
||||
private Address addr(long offset) {
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
private AddressRange range(long start, long end) {
|
||||
return new AddressRangeImpl(addr(start), addr(end));
|
||||
}
|
||||
|
||||
private void assertHasAddressRange(AddressRange range, boolean forward, AddressIterator it) {
|
||||
Address nextAddr, endAddr;
|
||||
if (forward) {
|
||||
nextAddr = range.getMinAddress();
|
||||
endAddr = range.getMaxAddress();
|
||||
}
|
||||
else {
|
||||
nextAddr = range.getMaxAddress();
|
||||
endAddr = range.getMinAddress();
|
||||
}
|
||||
while (true) {
|
||||
assertEquals(nextAddr, it.next());
|
||||
if (nextAddr.equals(endAddr)) {
|
||||
break;
|
||||
}
|
||||
nextAddr = forward ? nextAddr.next() : nextAddr.previous();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1Forward() {
|
||||
|
||||
AddressIterator it = new RecoverableAddressIterator(set, addr(0x150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
|
||||
set.add(range(0x350, 0x500));
|
||||
|
||||
assertHasAddressRange(range(0x150, 0x200), true, it);
|
||||
|
||||
set.add(addr(0x220)); // will get skipped due to underlying iterator prefetch
|
||||
|
||||
assertHasAddressRange(range(0x250, 0x250), true, it);
|
||||
|
||||
assertHasAddressRange(range(0x300, 0x400), true, it);
|
||||
|
||||
assertHasAddressRange(range(0x401, 0x500), true, it);
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
assertNull(it.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2Forward() {
|
||||
|
||||
AddressIterator it = new RecoverableAddressIterator(set, addr(0x150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
|
||||
set.add(range(0x210, 0x215));
|
||||
|
||||
assertHasAddressRange(range(0x150, 0x200), true, it);
|
||||
|
||||
set.add(range(0x220, 0x500));
|
||||
|
||||
assertHasAddressRange(range(0x210, 0x215), true, it);
|
||||
|
||||
assertHasAddressRange(range(0x220, 0x500), true, it);
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3Forward() {
|
||||
|
||||
AddressIterator it = new RecoverableAddressIterator(set, addr(0x150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
|
||||
set.add(range(0x210, 0x215));
|
||||
|
||||
assertHasAddressRange(range(0x150, 0x200), true, it);
|
||||
|
||||
set.delete(range(0x220, 0x500));
|
||||
|
||||
assertHasAddressRange(range(0x210, 0x215), true, it);
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1Reverse() {
|
||||
|
||||
AddressIterator it = new RecoverableAddressIterator(set, addr(0x350), false);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
|
||||
set.add(addr(0x240));
|
||||
|
||||
assertHasAddressRange(range(0x300, 0x350), false, it);
|
||||
|
||||
set.add(range(0x50, 0x150));
|
||||
|
||||
assertHasAddressRange(range(0x250, 0x250), false, it);
|
||||
|
||||
set.add(range(0x50, 0x150));
|
||||
|
||||
assertHasAddressRange(range(0x240, 0x240), false, it);
|
||||
|
||||
assertHasAddressRange(range(0x50, 0x200), false, it);
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2Reverse() {
|
||||
|
||||
AddressIterator it = new RecoverableAddressIterator(set, addr(0x350), false);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
|
||||
set.add(addr(0x240));
|
||||
|
||||
assertHasAddressRange(range(0x300, 0x350), false, it);
|
||||
|
||||
set.delete(range(0x100, 0x200));
|
||||
|
||||
assertHasAddressRange(range(0x250, 0x250), false, it);
|
||||
|
||||
assertHasAddressRange(range(0x240, 0x240), false, it);
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
}
|
@ -1,164 +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.program.database.mem;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class RecoverableAddressRangeIteratorTest extends AbstractGenericTest {
|
||||
|
||||
private AddressSpace space;
|
||||
private AddressSet set;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
space = new GenericAddressSpace("xx", 32, AddressSpace.TYPE_RAM, 0);
|
||||
|
||||
set = new AddressSet();
|
||||
set.add(range(0x100, 0x200));
|
||||
set.add(range(0x250, 0x250));
|
||||
set.add(range(0x300, 0x400));
|
||||
}
|
||||
|
||||
private Address addr(long offset) {
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
private AddressRange range(long start, long end) {
|
||||
return new AddressRangeImpl(addr(start), addr(end));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1Forward() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x100, 0x200), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.add(addr(0x220)); // will get skipped due to underlying iterator prefetch
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers prefetch of range(0x300, 0x400)
|
||||
|
||||
set.add(range(0x350, 0x500)); // modifies existing RedBlackEntry node - no iterator recovery triggered
|
||||
|
||||
assertEquals(range(0x300, 0x400), it.next()); // triggers prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
try {
|
||||
it.next();
|
||||
fail("Expected NoSuchElementException");
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2Forward() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x100, 0x200), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.add(range(0x220, 0x400));
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers recovery prefetch of partial range(0x251, 0x400)
|
||||
|
||||
assertEquals(range(0x251, 0x400), it.next()); // triggers prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3Forward() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x100, 0x200), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.delete(range(0x220, 0x400));
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers recovery prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1Reverse() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(0x350), false);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x300, 0x400), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.add(addr(0x220));
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers recovery prefetch of range(0x220, 0x220)
|
||||
|
||||
assertEquals(range(0x220, 0x220), it.next()); // triggers prefetch of range(0x100, 0x200)
|
||||
|
||||
assertEquals(range(0x100, 0x200), it.next()); // triggers prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2Reverse() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(0x350), false);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x300, 0x400), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.add(range(0x220, 0x380));
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers recovery prefetch of partial range(0x220, 0x24f)
|
||||
|
||||
assertEquals(range(0x220, 0x24f), it.next()); // triggers prefetch of range(0x100, 0x200)
|
||||
|
||||
assertEquals(range(0x100, 0x200), it.next()); // triggers prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3Reverse() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(0x350), false);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x300, 0x400), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.delete(range(0x100, 0x220));
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers recovery prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user