mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-21 19:42:14 +00:00
GP-134: Mainline changes cherry-picked from Debugger branch
This commit is contained in:
parent
192aba987a
commit
724df5a44c
2
.gitignore
vendored
2
.gitignore
vendored
@ -53,6 +53,8 @@ Release
|
||||
*.aps
|
||||
*.vcproj.*
|
||||
*.vcxproj.*
|
||||
# dump files
|
||||
*.mdmp
|
||||
.vs/
|
||||
|
||||
# Ignore UNIX backup files
|
||||
|
@ -10,7 +10,7 @@
|
||||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||
##MODULE IP: Tango Icons - Public Domain
|
||||
.gitignore||GHIDRA||||END|
|
||||
.launch/Ghidra.launch||GHIDRA||||END|
|
||||
Ghidra.launch||GHIDRA||||END|
|
||||
Module.manifest||GHIDRA||||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
data/ElfFunctionsThatDoNotReturn||GHIDRA||||END|
|
||||
|
@ -35,7 +35,6 @@ import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.app.context.ListingActionContext;
|
||||
import ghidra.app.plugin.assembler.Assembler;
|
||||
import ghidra.app.plugin.assembler.Assemblers;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.GhidraDBTransaction;
|
||||
import ghidra.app.plugin.core.assembler.AssemblyDualTextField.*;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||
import ghidra.app.util.PluginConstants;
|
||||
@ -43,6 +42,7 @@ import ghidra.app.util.viewer.field.ListingField;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingModelAdapter;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.util.ProgramTransaction;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.listing.*;
|
||||
@ -106,8 +106,8 @@ public class AssembleDockingAction extends DockingAction {
|
||||
/*
|
||||
* A class for all my callbacks
|
||||
*
|
||||
* For autocompletion, this causes activation of an assembled instruction to actually patch
|
||||
* the instruction in.
|
||||
* For autocompletion, this causes activation of an assembled instruction to actually patch the
|
||||
* instruction in.
|
||||
*
|
||||
* For keyboard, it causes the escape key, if not already consumed by the autocompleter, to
|
||||
* cancel the assembly action altogether.
|
||||
@ -117,8 +117,8 @@ public class AssembleDockingAction extends DockingAction {
|
||||
public void completionActivated(AutocompletionEvent<AssemblyCompletion> ev) {
|
||||
if (ev.getSelection() instanceof AssemblyInstruction) {
|
||||
AssemblyInstruction ins = (AssemblyInstruction) ev.getSelection();
|
||||
try (GhidraDBTransaction trans =
|
||||
new GhidraDBTransaction(prog, "Assemble @" + addr + ": " + input.getText())) {
|
||||
try (ProgramTransaction trans =
|
||||
ProgramTransaction.open(prog, "Assemble @" + addr + ": " + input.getText())) {
|
||||
assembler.patchProgram(ins.getData(), addr);
|
||||
trans.commit();
|
||||
cancel(); // Not really, since I've committed. Just hides the editors.
|
||||
@ -219,6 +219,7 @@ public class AssembleDockingAction extends DockingAction {
|
||||
/**
|
||||
* Retrieve the location in the code viewer's {@link FieldPanel} for the field at the given
|
||||
* address having the given header text
|
||||
*
|
||||
* @param addr the address
|
||||
* @param fieldName the name of the field
|
||||
* @return if found, the {@link FieldLocation}, otherwise {@code null}
|
||||
|
@ -109,8 +109,8 @@ public class CodeBrowserPlugin extends Plugin
|
||||
// - Icon -
|
||||
private ImageIcon CURSOR_LOC_ICON =
|
||||
ResourceManager.loadImage("images/cursor_arrow_flipped.gif");
|
||||
private CodeViewerProvider connectedProvider;
|
||||
private List<CodeViewerProvider> disconnectedProviders = new ArrayList<>();
|
||||
protected final CodeViewerProvider connectedProvider;
|
||||
protected List<CodeViewerProvider> disconnectedProviders = new ArrayList<>();
|
||||
private FormatManager formatMgr;
|
||||
private ViewManagerService viewManager;
|
||||
private MarkerService markerService;
|
||||
@ -149,7 +149,7 @@ public class CodeBrowserPlugin extends Plugin
|
||||
formatMgr = new FormatManager(displayOptions, fieldOptions);
|
||||
formatMgr.addFormatModelListener(this);
|
||||
formatMgr.setServiceProvider(tool);
|
||||
connectedProvider = new CodeViewerProvider(this, formatMgr, true);
|
||||
connectedProvider = createProvider(formatMgr, true);
|
||||
tool.showComponentProvider(connectedProvider, true);
|
||||
initOptions(fieldOptions);
|
||||
initDisplayOptions(displayOptions);
|
||||
@ -164,6 +164,10 @@ public class CodeBrowserPlugin extends Plugin
|
||||
createActions();
|
||||
}
|
||||
|
||||
protected CodeViewerProvider createProvider(FormatManager formatManager, boolean isConnected) {
|
||||
return new CodeViewerProvider(this, formatManager, isConnected);
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
DockingAction selectAllAction = new SelectAllAction(getName());
|
||||
selectAllAction.getMenuBarData().setMenuSubGroup("a");
|
||||
@ -178,7 +182,7 @@ public class CodeBrowserPlugin extends Plugin
|
||||
tool.addAction(selectComplementAction);
|
||||
}
|
||||
|
||||
void viewChanged(AddressSetView addrSet) {
|
||||
protected void viewChanged(AddressSetView addrSet) {
|
||||
ProgramLocation currLoc = getCurrentLocation();
|
||||
currentView = addrSet;
|
||||
if (addrSet != null && !addrSet.isEmpty()) {
|
||||
@ -217,7 +221,7 @@ public class CodeBrowserPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBackgroundColorModel() {
|
||||
protected void updateBackgroundColorModel() {
|
||||
ListingPanel listingPanel = connectedProvider.getListingPanel();
|
||||
if (markerService != null) {
|
||||
AddressIndexMap indexMap = connectedProvider.getListingPanel().getAddressIndexMap();
|
||||
@ -234,7 +238,7 @@ public class CodeBrowserPlugin extends Plugin
|
||||
@Override
|
||||
public CodeViewerProvider createNewDisconnectedProvider() {
|
||||
CodeViewerProvider newProvider =
|
||||
new CodeViewerProvider(this, formatMgr.createClone(), false);
|
||||
createProvider(formatMgr.createClone(), false);
|
||||
newProvider.setClipboardService(tool.getService(ClipboardService.class));
|
||||
disconnectedProviders.add(newProvider);
|
||||
if (dndProvider != null) {
|
||||
@ -425,7 +429,7 @@ public class CodeBrowserPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
private void programClosed(Program closedProgram) {
|
||||
protected void programClosed(Program closedProgram) {
|
||||
Iterator<CodeViewerProvider> iterator = disconnectedProviders.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
CodeViewerProvider provider = iterator.next();
|
||||
@ -1008,6 +1012,7 @@ public class CodeBrowserPlugin extends Plugin
|
||||
|
||||
/**
|
||||
* Positions the cursor to the given location
|
||||
*
|
||||
* @param address the address to goto
|
||||
* @param fieldName the name of the field to
|
||||
* @param row the row within the given field
|
||||
@ -1020,6 +1025,7 @@ public class CodeBrowserPlugin extends Plugin
|
||||
|
||||
/**
|
||||
* Positions the cursor to the given location
|
||||
*
|
||||
* @param addr the address to goto
|
||||
* @param fieldName the name of the field to
|
||||
* @param occurrence specifies the which occurrence for multiple fields of same type
|
||||
|
@ -417,7 +417,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
void updateTitle() {
|
||||
protected void updateTitle() {
|
||||
String subTitle = program == null ? "" : ' ' + program.getDomainFile().getName();
|
||||
String newTitle = TITLE + subTitle;
|
||||
if (!isConnected()) {
|
||||
@ -1004,8 +1004,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that allows clients to install transient highlighters while keeping the
|
||||
* middle-mouse highlighting on at the same time.
|
||||
* A class that allows clients to install transient highlighters while keeping the middle-mouse
|
||||
* highlighting on at the same time.
|
||||
*/
|
||||
private class ProgramHighlighterProvider implements HighlightProvider {
|
||||
|
||||
@ -1043,6 +1043,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
|
||||
/**
|
||||
* Add the ListingDisplayListener to the listing panel
|
||||
*
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
public void addListingDisplayListener(ListingDisplayListener listener) {
|
||||
@ -1051,6 +1052,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
|
||||
/**
|
||||
* Remove the ListingDisplayListener from the listing panel
|
||||
*
|
||||
* @param listener the listener to remove
|
||||
*/
|
||||
public void removeListingDisplayListener(ListingDisplayListener listener) {
|
||||
|
@ -24,28 +24,41 @@ import ghidra.app.util.viewer.listingpanel.ListingBackgroundColorModel;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* {@link BackgroundColorModel} for coloring the Listing based on the {@link MarkerService}
|
||||
*/
|
||||
public class MarkerServiceBackgroundColorModel implements ListingBackgroundColorModel {
|
||||
private MarkerService markerService;
|
||||
private Program program;
|
||||
private AddressIndexMap indexMap;
|
||||
private Color defaultBackgroundColor = Color.WHITE;
|
||||
|
||||
public MarkerServiceBackgroundColorModel(MarkerService markerService,
|
||||
public MarkerServiceBackgroundColorModel(MarkerService markerService, Program program,
|
||||
AddressIndexMap indexMap) {
|
||||
this.markerService = markerService;
|
||||
this.program = program;
|
||||
this.indexMap = indexMap;
|
||||
}
|
||||
|
||||
public MarkerServiceBackgroundColorModel(MarkerService markerService,
|
||||
AddressIndexMap indexMap) {
|
||||
this(markerService, null, indexMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getBackgroundColor(BigInteger index) {
|
||||
Address addr = indexMap.getAddress(index);
|
||||
Color color = null;
|
||||
if (addr != null) {
|
||||
if (program == null) {
|
||||
color = markerService.getBackgroundColor(addr);
|
||||
}
|
||||
else {
|
||||
color = markerService.getBackgroundColor(program, addr);
|
||||
}
|
||||
}
|
||||
if (color == null) {
|
||||
color = defaultBackgroundColor;
|
||||
}
|
||||
@ -64,7 +77,7 @@ public class MarkerServiceBackgroundColorModel implements ListingBackgroundColor
|
||||
|
||||
@Override
|
||||
public void modelDataChanged(ListingPanel listingPanel) {
|
||||
this.program = listingPanel.getProgram();
|
||||
this.indexMap = listingPanel.getAddressIndexMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -158,6 +158,15 @@ public class InterpreterComponentProvider extends ComponentProviderAdapter
|
||||
return panel.getErrWriter();
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing purposes, but should probably be promoted to InterpreterConsole interface
|
||||
*
|
||||
* @return the prompt;
|
||||
*/
|
||||
public String getPrompt() {
|
||||
return panel.getPrompt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrompt(String prompt) {
|
||||
panel.setPrompt(prompt);
|
||||
@ -186,4 +195,33 @@ public class InterpreterComponentProvider extends ComponentProviderAdapter
|
||||
public void addFirstActivationCallback(Callback activationCallback) {
|
||||
firstActivationCallbacks.add(activationCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInputPermitted() {
|
||||
return panel.isInputPermitted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputPermitted(boolean permitted) {
|
||||
panel.setInputPermitted(permitted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
tool.showComponentProvider(this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTitle() {
|
||||
tool.updateTitle(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing purposes only
|
||||
*
|
||||
* @return the text in the output buffer
|
||||
*/
|
||||
public String getOutputText() {
|
||||
return panel.getOutputText();
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ public interface InterpreterConsole extends Disposable {
|
||||
* continuously checks the InterpreterConsole for new input. It would be much easier
|
||||
* if the InterpreterConsole could just asynchronously send an update to whoever is using
|
||||
* it every time a new thing is entered into it.
|
||||
*
|
||||
*The same problem applies to debugger interpreters / consoles.
|
||||
*/
|
||||
|
||||
public void clear();
|
||||
@ -48,19 +50,54 @@ public interface InterpreterConsole extends Disposable {
|
||||
public void setPrompt(String prompt);
|
||||
|
||||
/**
|
||||
* Signals that this console is one that the user can remove from the tool as desired. If
|
||||
* this method is not called, then the user cannot remove the console from the tool, which
|
||||
* means that closing the console only hides it.
|
||||
* Signals that this console is one that the user can remove from the tool as desired. If this
|
||||
* method is not called, then the user cannot remove the console from the tool, which means that
|
||||
* closing the console only hides it.
|
||||
*/
|
||||
public void setTransient();
|
||||
|
||||
public void addAction(DockingAction action);
|
||||
|
||||
/**
|
||||
* Adds the given callback which will get called the first time the interpreter console
|
||||
* is activated.
|
||||
* Adds the given callback which will get called the first time the interpreter console is
|
||||
* activated.
|
||||
*
|
||||
* @param activationCallback The callback to execute when activation occurs for the first time.
|
||||
*/
|
||||
public void addFirstActivationCallback(Callback activationCallback);
|
||||
|
||||
/**
|
||||
* Checks whether the user can input commands.
|
||||
*
|
||||
* @return true if permitted, false if prohibited
|
||||
*/
|
||||
public boolean isInputPermitted();
|
||||
|
||||
/**
|
||||
* Controls whether the user can input commands.
|
||||
*
|
||||
* @param permitted true to permit input, false to prohibit input
|
||||
*/
|
||||
public void setInputPermitted(boolean permitted);
|
||||
|
||||
/**
|
||||
* Check if the console is visible
|
||||
*
|
||||
* <p>
|
||||
* Note if the console is on-screen, but occluded by other windows, this still returns
|
||||
* {@code true}.
|
||||
*
|
||||
* @return true if visible, false if hidden
|
||||
*/
|
||||
public boolean isVisible();
|
||||
|
||||
/**
|
||||
* Show the console's provider in the tool
|
||||
*/
|
||||
public void show();
|
||||
|
||||
/**
|
||||
* Notify the tool that this console's title has changed
|
||||
*/
|
||||
public void updateTitle();
|
||||
}
|
||||
|
@ -564,6 +564,10 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||
outputTextPane.setText("");
|
||||
}
|
||||
|
||||
public String getOutputText() {
|
||||
return outputTextPane.getText();
|
||||
}
|
||||
|
||||
public InputStream getStdin() {
|
||||
return stdin;
|
||||
}
|
||||
@ -584,6 +588,10 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||
return errWriter;
|
||||
}
|
||||
|
||||
public String getPrompt() {
|
||||
return promptTextPane.getText();
|
||||
}
|
||||
|
||||
public void setPrompt(String prompt) {
|
||||
try {
|
||||
final Document document = promptTextPane.getDocument();
|
||||
@ -654,6 +662,14 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||
inputAttributes.addAttributes(attributes);
|
||||
}
|
||||
|
||||
public boolean isInputPermitted() {
|
||||
return inputTextPane.isEditable();
|
||||
}
|
||||
|
||||
public void setInputPermitted(boolean permitted) {
|
||||
inputTextPane.setEditable(permitted);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
@ -735,6 +751,7 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||
|
||||
/**
|
||||
* Overridden to stop this stream from blocking.
|
||||
*
|
||||
* @throws IOException not
|
||||
*/
|
||||
@Override
|
||||
@ -760,5 +777,4 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -346,12 +346,12 @@ public abstract class BiDirectionDataType extends StructureDataType
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the size of the bidirectional data type
|
||||
* If amount is positive then the positive offset side will grow by the
|
||||
* indicated amount. If amount is negative, the data type grows on the
|
||||
* negative offsets side.
|
||||
* @param amount Positive value indicates number of bytes to add to positive side.
|
||||
* Negative value indicates number of bytes to add to negative side.
|
||||
* Increases the size of the bidirectional data type If amount is positive then the positive
|
||||
* offset side will grow by the indicated amount. If amount is negative, the data type grows on
|
||||
* the negative offsets side.
|
||||
*
|
||||
* @param amount Positive value indicates number of bytes to add to positive side. Negative
|
||||
* value indicates number of bytes to add to negative side.
|
||||
*/
|
||||
@Override
|
||||
public void growStructure(int amount) {
|
||||
@ -567,7 +567,7 @@ public abstract class BiDirectionDataType extends StructureDataType
|
||||
// }
|
||||
|
||||
@Override
|
||||
public abstract DataType clone(DataTypeManager dtm);
|
||||
public abstract BiDirectionDataType clone(DataTypeManager dtm);
|
||||
|
||||
@Override
|
||||
public void clearComponent(int index) {
|
||||
@ -709,21 +709,20 @@ public abstract class BiDirectionDataType extends StructureDataType
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the indicated component with a new component containing the
|
||||
* specified data type.
|
||||
* Replace the indicated component with a new component containing the specified data type.
|
||||
*
|
||||
* @param origDtc the original data type component in this structure.
|
||||
* @param dataType the data type of the new component
|
||||
* @param length the length of the new component
|
||||
* @param newName the field name of the new component
|
||||
* @param comment the comment for the new component
|
||||
* @return the new component or null if the new component couldn't fit.
|
||||
* @throws IllegalArgumentException if the dataType.getLength() is positive
|
||||
* and does not match the given length parameter.
|
||||
* @throws IllegalArgumentException if the specified data type is not
|
||||
* allowed to replace a component in this composite data type.
|
||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||
* to replace a dt2 component with dt1 since this would cause a cyclic
|
||||
* dependency.
|
||||
* @throws IllegalArgumentException if the dataType.getLength() is positive and does not match
|
||||
* the given length parameter.
|
||||
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
|
||||
* component in this composite data type. For example, suppose dt1 contains dt2.
|
||||
* Therefore it is not valid to replace a dt2 component with dt1 since this would
|
||||
* cause a cyclic dependency.
|
||||
*/
|
||||
private DataTypeComponent replace(DataTypeComponent origDtc, DataType dataType, int length,
|
||||
String newName, String comment) {
|
||||
|
@ -15,6 +15,9 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.stackeditor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
@ -25,9 +28,6 @@ import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* StackFrameDataType provides an editable copy of a function stack frame.
|
||||
*/
|
||||
@ -43,17 +43,20 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
|
||||
/**
|
||||
* Constructor for an editable stack frame for use with the editor.
|
||||
*
|
||||
* @param stack the function stack frame to be edited.
|
||||
*/
|
||||
public StackFrameDataType(StackFrame stack, DataTypeManager dtm) {
|
||||
super((stack.getFunction() != null) ? stack.getFunction().getName()
|
||||
: "StackWithoutFunction", 0, 0, stack.getParameterOffset(), dtm);
|
||||
super(
|
||||
(stack.getFunction() != null) ? stack.getFunction().getName() : "StackWithoutFunction",
|
||||
0, 0, stack.getParameterOffset(), dtm);
|
||||
this.stack = stack;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for an editable stack frame for use with the editor.
|
||||
*
|
||||
* @param stack the function stack frame to be edited.
|
||||
*/
|
||||
public StackFrameDataType(StackFrameDataType stackDt, DataTypeManager dtm) {
|
||||
@ -67,8 +70,8 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
this.defaultSettings = stackDt.defaultSettings;
|
||||
for (int i = 0; i < stackDt.components.size(); i++) {
|
||||
DataTypeComponent dtc = stackDt.components.get(i);
|
||||
replaceAtOffset(dtc.getOffset(), dtc.getDataType(), dtc.getLength(),
|
||||
dtc.getFieldName(), dtc.getComment());
|
||||
replaceAtOffset(dtc.getOffset(), dtc.getDataType(), dtc.getLength(), dtc.getFieldName(),
|
||||
dtc.getComment());
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,6 +135,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.program.model.data.DataType#getRepresentation(ghidra.program.model.mem.MemBuffer, ghidra.util.settings.Settings, int)
|
||||
*/
|
||||
@Override
|
||||
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
|
||||
return "";
|
||||
}
|
||||
@ -145,7 +149,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType clone(DataTypeManager dtm) {
|
||||
public StackFrameDataType clone(DataTypeManager dtm) {
|
||||
return new StackFrameDataType(this, dtm);
|
||||
}
|
||||
|
||||
@ -164,10 +168,10 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
}
|
||||
|
||||
/**
|
||||
* If a stack variable is defined in the editor at the specified offset,
|
||||
* this retrieves the editor element containing that stack variable
|
||||
* <BR>Note: if a stack variable isn't defined at the indicated offset
|
||||
* then null is returned.
|
||||
* If a stack variable is defined in the editor at the specified offset, this retrieves the
|
||||
* editor element containing that stack variable <BR>
|
||||
* Note: if a stack variable isn't defined at the indicated offset then null is returned.
|
||||
*
|
||||
* @param offset the offset
|
||||
* @return the stack editor's element at the offset. Otherwise, null.
|
||||
*/
|
||||
@ -183,10 +187,10 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
}
|
||||
|
||||
/**
|
||||
* If a stack variable is defined in the editor at the specified ordinal,
|
||||
* this retrieves the editor element containing that stack variable.
|
||||
* <BR>Note: if a stack variable isn't defined for the indicated ordinal
|
||||
* then null is returned.
|
||||
* If a stack variable is defined in the editor at the specified ordinal, this retrieves the
|
||||
* editor element containing that stack variable. <BR>
|
||||
* Note: if a stack variable isn't defined for the indicated ordinal then null is returned.
|
||||
*
|
||||
* @param ordinal the ordinal
|
||||
* @return the stack editor's element at the ordinal. Otherwise, null.
|
||||
*/
|
||||
@ -315,6 +319,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
|
||||
/**
|
||||
* Undefines any defined stack variables in the indicated offset range.
|
||||
*
|
||||
* @param minOffset the range's minimum offset on the stack frame
|
||||
* @param maxOffset the range's maximum offset on the stack frame
|
||||
*/
|
||||
@ -335,6 +340,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
|
||||
/**
|
||||
* Deletes the indicated range of bytes from this stack frame data type.
|
||||
*
|
||||
* @param minOffset the range's minimum offset on the stack frame
|
||||
* @param maxOffset the range's maximum offset on the stack frame
|
||||
*/
|
||||
@ -379,8 +385,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
String fieldName = dtc.getFieldName();
|
||||
int offset = dtc.getOffset();
|
||||
try {
|
||||
vars[i] =
|
||||
new LocalVariableImpl(fieldName, dtc.getDataType(), offset,
|
||||
vars[i] = new LocalVariableImpl(fieldName, dtc.getDataType(), offset,
|
||||
function.getProgram());
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
@ -402,6 +407,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
|
||||
/**
|
||||
* Sets the name of the component at the specified ordinal.
|
||||
*
|
||||
* @param ordinal the ordinal
|
||||
* @param name the new name. Null indicates the default name.
|
||||
*/
|
||||
@ -433,6 +439,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
|
||||
/**
|
||||
* Sets the comment at the specified ordinal.
|
||||
*
|
||||
* @param ordinal the ordinal
|
||||
* @param comment the new comment.
|
||||
*/
|
||||
@ -483,6 +490,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
|
||||
/**
|
||||
* Currently no validation is done on the name.
|
||||
*
|
||||
* @param ordinal
|
||||
* @param newName
|
||||
* @throws InvalidNameException
|
||||
@ -509,8 +517,9 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Effectively moves a component for a defined stack variable if it will fit
|
||||
* where it is being moved to in the stack frame.
|
||||
* Effectively moves a component for a defined stack variable if it will fit where it is being
|
||||
* moved to in the stack frame.
|
||||
*
|
||||
* @param ordinal the ordinal of the component to move by changing its offset.
|
||||
* @param newOffset the offset to move the variable to.
|
||||
* @return the component representing the stack variable at the new offset.
|
||||
@ -523,8 +532,8 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
if (newOffset == oldOffset) {
|
||||
return comp;
|
||||
}
|
||||
if ((oldOffset >= splitOffset) && (newOffset < splitOffset) || (oldOffset < splitOffset) &&
|
||||
(newOffset >= splitOffset)) {
|
||||
if ((oldOffset >= splitOffset) && (newOffset < splitOffset) ||
|
||||
(oldOffset < splitOffset) && (newOffset >= splitOffset)) {
|
||||
throw new InvalidInputException(
|
||||
"Cannot move a stack variable/parameter across the parameter offset.");
|
||||
}
|
||||
@ -548,15 +557,15 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
String defaultName = getDefaultName(comp);
|
||||
String oldName = comp.getFieldName();
|
||||
boolean isDefault = (oldName == null) || (oldName.equals(defaultName));
|
||||
DataTypeComponent newComp =
|
||||
replaceAtOffset(newOffset, comp.getDataType(), comp.getLength(), isDefault ? null
|
||||
: oldName, comp.getComment());
|
||||
DataTypeComponent newComp = replaceAtOffset(newOffset, comp.getDataType(), comp.getLength(),
|
||||
isDefault ? null : oldName, comp.getComment());
|
||||
return newComp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a component representing the defined stack variable at the indicated
|
||||
* ordinal to have the specified data type and length.
|
||||
* Sets a component representing the defined stack variable at the indicated ordinal to have the
|
||||
* specified data type and length.
|
||||
*
|
||||
* @param ordinal the ordinal
|
||||
* @param type the data type
|
||||
* @param length the length or size of this variable.
|
||||
@ -569,6 +578,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
|
||||
/**
|
||||
* Get the maximum variable size that will fit at the indicated offset if a replace is done.
|
||||
*
|
||||
* @param offset
|
||||
* @return the maximum size
|
||||
*/
|
||||
@ -604,6 +614,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
|
||||
/**
|
||||
* Returns the default name for the indicated stack offset.
|
||||
*
|
||||
* @param offset
|
||||
* @return the default stack variable name.
|
||||
*/
|
||||
@ -623,9 +634,8 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
|
||||
/**
|
||||
* @param element
|
||||
* @return the index number for this parameter
|
||||
* (starting at 1 for the first parameter.)
|
||||
* 0 if the element is not a parameter.
|
||||
* @return the index number for this parameter (starting at 1 for the first parameter.) 0 if the
|
||||
* element is not a parameter.
|
||||
*/
|
||||
private int getParameterIndex(DataTypeComponent element) {
|
||||
int numComps = components.size();
|
||||
@ -668,6 +678,7 @@ public class StackFrameDataType extends BiDirectionDataType {
|
||||
|
||||
/**
|
||||
* Returns true if a stack variable is defined at the specified ordinal.
|
||||
*
|
||||
* @param ordinal
|
||||
* @return true if variable is defined at ordinal or false if undefined.
|
||||
*/
|
||||
|
@ -39,7 +39,7 @@ class MSRichProductInfoDataType extends StructureDataType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType clone(DataTypeManager dtm) {
|
||||
public MSRichProductInfoDataType clone(DataTypeManager dtm) {
|
||||
if (dtm == getDataTypeManager()) {
|
||||
return this;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class RichTableRecordDataType extends StructureDataType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType clone(DataTypeManager dtm) {
|
||||
public RichTableRecordDataType clone(DataTypeManager dtm) {
|
||||
if (dtm == getDataTypeManager()) {
|
||||
return this;
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Constructs a new ListingPanel using the given FormatManager and ServiceProvider.
|
||||
*
|
||||
* @param manager the FormatManager to use.
|
||||
*/
|
||||
public ListingPanel(FormatManager manager) {
|
||||
@ -122,6 +123,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Constructs a new ListingPanel for the given program.
|
||||
*
|
||||
* @param mgr the FormatManager to use.
|
||||
* @param program the program for which to create a new ListingPanel
|
||||
*/
|
||||
@ -132,6 +134,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Constructs a new ListingPanel with the given FormatManager and ListingLayoutModel
|
||||
*
|
||||
* @param mgr the FormatManager to use
|
||||
* @param model the ListingLayoutModel to use.
|
||||
*/
|
||||
@ -177,6 +180,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Sets the ProgramLocationListener. Only one listener is supported
|
||||
*
|
||||
* @param listener the ProgramLocationListener to use.
|
||||
*/
|
||||
public void setProgramLocationListener(ProgramLocationListener listener) {
|
||||
@ -185,6 +189,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Sets the ProgramSelectionListener. Only one listener is supported
|
||||
*
|
||||
* @param listener the ProgramSelectionListener to use.
|
||||
*/
|
||||
public void setProgramSelectionListener(ProgramSelectionListener listener) {
|
||||
@ -197,6 +202,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Sets the ListingLayoutModel to use.
|
||||
*
|
||||
* @param newModel the model to use.
|
||||
*/
|
||||
public void setListingModel(ListingModel newModel) {
|
||||
@ -215,8 +221,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not the field header component is visible at the top of the
|
||||
* listing panel
|
||||
* Sets whether or not the field header component is visible at the top of the listing panel
|
||||
*
|
||||
* @param show if true, the header component will be show, otherwise it will be hidden.
|
||||
*/
|
||||
public void showHeader(boolean show) {
|
||||
@ -275,8 +281,9 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Adds the MarginProvider to this panel
|
||||
* @param provider the MarginProvider that will provide components to display in this
|
||||
* panel's left margin area.
|
||||
*
|
||||
* @param provider the MarginProvider that will provide components to display in this panel's
|
||||
* left margin area.
|
||||
*/
|
||||
public void addMarginProvider(MarginProvider provider) {
|
||||
if (provider.isResizeable()) {
|
||||
@ -366,6 +373,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Add a change listener to be notified whenever the indexMap changes.
|
||||
*
|
||||
* @param listener the listener to be added.
|
||||
*/
|
||||
public void addIndexMapChangeListener(ChangeListener listener) {
|
||||
@ -374,6 +382,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Removes the change listener to be notified when the indexMap changes.
|
||||
*
|
||||
* @param listener the listener to be removed.
|
||||
*/
|
||||
public void removeIndexMapChangeListener(ChangeListener listener) {
|
||||
@ -382,6 +391,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Removes the given margin provider from this panel
|
||||
*
|
||||
* @param provider the MarginProvider to remove.
|
||||
*/
|
||||
public void removeMarginProvider(MarginProvider provider) {
|
||||
@ -390,8 +400,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given OverviewProvider with will be displayed in this panels right margin
|
||||
* area.
|
||||
* Adds the given OverviewProvider with will be displayed in this panels right margin area.
|
||||
*
|
||||
* @param provider the OverviewProvider to display.
|
||||
*/
|
||||
public void addOverviewProvider(OverviewProvider provider) {
|
||||
@ -402,6 +412,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Removes the given OverviewProvider from this panel
|
||||
*
|
||||
* @param provider the OverviewProvider to remove.
|
||||
*/
|
||||
public void removeOverviewProvider(OverviewProvider provider) {
|
||||
@ -410,8 +421,9 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a ButtonPressedListener to be notified when the user presses the mouse button while
|
||||
* over this panel
|
||||
* Adds a ButtonPressedListener to be notified when the user presses the mouse button while over
|
||||
* this panel
|
||||
*
|
||||
* @param listener the ButtonPressedListener to add.
|
||||
*/
|
||||
public void addButtonPressedListener(ButtonPressedListener listener) {
|
||||
@ -422,6 +434,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Removes the given ButtonPressedListener.
|
||||
*
|
||||
* @param listener the ButtonPressedListener to remove.
|
||||
*/
|
||||
public void removeButtonPressedListener(ButtonPressedListener listener) {
|
||||
@ -441,8 +454,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link HighlightProvider} to this listing. This highlight provider will be used
|
||||
* with any other registered providers to paint all the highlights for this listing.
|
||||
* Adds a {@link HighlightProvider} to this listing. This highlight provider will be used with
|
||||
* any other registered providers to paint all the highlights for this listing.
|
||||
*
|
||||
* @param highlightProvider The provider to add
|
||||
*/
|
||||
@ -492,6 +505,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Sets the divider location between the left margin areas and the main display.
|
||||
*
|
||||
* @param dividerLocation the location to set on the divider.
|
||||
*/
|
||||
public void setDividerLocation(int dividerLocation) {
|
||||
@ -537,8 +551,9 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the cursor to the given program location and repositions the scrollbar to
|
||||
* show that location in the screen.
|
||||
* Moves the cursor to the given program location and repositions the scrollbar to show that
|
||||
* location in the screen.
|
||||
*
|
||||
* @param loc the location to move to.
|
||||
*/
|
||||
public boolean goTo(ProgramLocation loc) {
|
||||
@ -546,15 +561,14 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the cursor to the given program location. Also, repositions the scrollbar to
|
||||
* show that location, if the location is not on the screen.
|
||||
* Moves the cursor to the given program location. Also, repositions the scrollbar to show that
|
||||
* location, if the location is not on the screen.
|
||||
*
|
||||
* @param loc the location to move to.
|
||||
* @param centerWhenNotVisible this variable only has an effect if the given location is not
|
||||
* on the screen. In that case, when this parameter is true, then
|
||||
* the given location will be placed in the center of the screen;
|
||||
* when the parameter is false, then the screen will be scrolled
|
||||
* only enough to show the cursor.
|
||||
* @param centerWhenNotVisible this variable only has an effect if the given location is not on
|
||||
* the screen. In that case, when this parameter is true, then the given location
|
||||
* will be placed in the center of the screen; when the parameter is false, then the
|
||||
* screen will be scrolled only enough to show the cursor.
|
||||
*/
|
||||
public boolean goTo(ProgramLocation loc, boolean centerWhenNotVisible) {
|
||||
final FieldLocation floc = getFieldLocation(loc);
|
||||
@ -695,6 +709,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Positions the ListingPanel to the given address.
|
||||
*
|
||||
* @param addr the address at which to position the listing.
|
||||
*/
|
||||
public boolean goTo(Address addr) {
|
||||
@ -707,6 +722,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Positions the ListingPanel to the given address.
|
||||
*
|
||||
* @param currentAddress used to determine which symbol to goto if the goto address has more
|
||||
* than one
|
||||
* @param gotoAddress the address at which to position to listing.
|
||||
@ -749,6 +765,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Sets the program to be displayed by this listing panel
|
||||
*
|
||||
* @param program the program to display.
|
||||
*/
|
||||
public void setProgram(Program program) {
|
||||
@ -782,6 +799,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Restricts the program's view to the given address set
|
||||
*
|
||||
* @param view the set of address to include in the view.
|
||||
*/
|
||||
public void setView(AddressSetView view) {
|
||||
@ -799,11 +817,11 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the externally supplied {@link ListingBackgroundColorModel} to be blended with
|
||||
* its own {@link PropertyBasedBackgroundColorModel}.
|
||||
* Sets the externally supplied {@link ListingBackgroundColorModel} to be blended with its own
|
||||
* {@link PropertyBasedBackgroundColorModel}.
|
||||
*
|
||||
* @param colorModel the {@link ListingBackgroundColorModel} to use in conjunction with
|
||||
* the built-in {@link PropertyBasedBackgroundColorModel}
|
||||
* @param colorModel the {@link ListingBackgroundColorModel} to use in conjunction with the
|
||||
* built-in {@link PropertyBasedBackgroundColorModel}
|
||||
*/
|
||||
public void setBackgroundColorModel(ListingBackgroundColorModel colorModel) {
|
||||
if (colorModel == null) {
|
||||
@ -817,8 +835,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the background color for the listing panel. This will set the background for the
|
||||
* main listing display.
|
||||
* Sets the background color for the listing panel. This will set the background for the main
|
||||
* listing display.
|
||||
*/
|
||||
public void setTextBackgroundColor(Color c) {
|
||||
if (fieldPanel != null) {
|
||||
@ -854,8 +872,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Get a program location for the given point.
|
||||
* @return program location, or null if point does not correspond
|
||||
* to a program location
|
||||
*
|
||||
* @return program location, or null if point does not correspond to a program location
|
||||
*/
|
||||
public ProgramLocation getProgramLocation(Point point) {
|
||||
FieldLocation dropLoc = new FieldLocation();
|
||||
@ -890,13 +908,27 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Sets the cursor to the given program location.
|
||||
*
|
||||
* @param loc the location at which to move the cursor.
|
||||
*/
|
||||
public void setCursorPosition(ProgramLocation loc) {
|
||||
setCursorPosition(loc, EventTrigger.API_CALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cursor to the given program location with a given trigger
|
||||
*
|
||||
* This method should only be used in automated testing to programmatically simulate a user
|
||||
* navigating within the listing panel.
|
||||
*
|
||||
* @param loc the location at which to move the cursor.
|
||||
* @param trigger the event trigger
|
||||
*/
|
||||
public void setCursorPosition(ProgramLocation loc, EventTrigger trigger) {
|
||||
FieldLocation floc = getFieldLocation(loc);
|
||||
if (floc != null) {
|
||||
fieldPanel.setCursorPosition(floc.getIndex(), floc.getFieldNum(), floc.getRow(),
|
||||
floc.getCol());
|
||||
floc.getCol(), trigger);
|
||||
}
|
||||
}
|
||||
|
||||
@ -991,8 +1023,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selection to the complement of the current selection in the
|
||||
* listing view.
|
||||
* Sets the selection to the complement of the current selection in the listing view.
|
||||
*/
|
||||
public AddressSet selectComplement() {
|
||||
fieldPanel.requestFocus();
|
||||
@ -1006,6 +1037,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Sets the selection.
|
||||
*
|
||||
* @param sel the new selection
|
||||
*/
|
||||
public void setSelection(ProgramSelection sel) {
|
||||
@ -1043,6 +1075,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
/**
|
||||
* Sets the highlight.
|
||||
*
|
||||
* @param highlight the new highlight.
|
||||
*/
|
||||
public void setHighlight(ProgramSelection highlight) {
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package docking.widgets.fieldpanel;
|
||||
|
||||
import static docking.widgets.EventTrigger.*;
|
||||
import static docking.widgets.EventTrigger.INTERNAL_ONLY;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
@ -129,14 +129,12 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the location is completely visible on the screen. If it
|
||||
* already is visible, this routine will do nothing. If the location is
|
||||
* above the screen (at an index less than the first line on the
|
||||
* screen), the view will be scrolled such that the location will appear
|
||||
* at the top of the screen. If the location is below the screen, the view
|
||||
* will be scrolled such that the location will appear at the bottom the
|
||||
* screen. The layouts[] array will be updated to reflect the current
|
||||
* view.
|
||||
* Makes sure the location is completely visible on the screen. If it already is visible, this
|
||||
* routine will do nothing. If the location is above the screen (at an index less than the first
|
||||
* line on the screen), the view will be scrolled such that the location will appear at the top
|
||||
* of the screen. If the location is below the screen, the view will be scrolled such that the
|
||||
* location will appear at the bottom the screen. The layouts[] array will be updated to reflect
|
||||
* the current view.
|
||||
*/
|
||||
private void doScrollTo(FieldLocation location) {
|
||||
if (layouts.isEmpty()) {
|
||||
@ -409,8 +407,7 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Sets the default background color
|
||||
*
|
||||
* @param c
|
||||
* the color to use for the background.
|
||||
* @param c the color to use for the background.
|
||||
*/
|
||||
public void setBackgroundColor(Color c) {
|
||||
backgroundColorModel.setDefaultBackgroundColor(c);
|
||||
@ -582,8 +579,7 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Add a new hover provider to be managed.
|
||||
*
|
||||
* @param hoverProvider
|
||||
* the new hover provider to be managed.
|
||||
* @param hoverProvider the new hover provider to be managed.
|
||||
*/
|
||||
public void setHoverProvider(HoverProvider hoverProvider) {
|
||||
hoverHandler.setHoverProvider(hoverProvider);
|
||||
@ -591,6 +587,7 @@ public class FieldPanel extends JPanel
|
||||
|
||||
/**
|
||||
* Returns the class responsible for triggering popups for this field panel.
|
||||
*
|
||||
* @return the hover handler.
|
||||
*/
|
||||
public HoverHandler getHoverHandler() {
|
||||
@ -598,16 +595,13 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Field at the given x,y coordinates. Note the x,y must
|
||||
* currently be visible on the screen or else this method will return null.
|
||||
* Returns the Field at the given x,y coordinates. Note the x,y must currently be visible on the
|
||||
* screen or else this method will return null.
|
||||
*
|
||||
* @param x
|
||||
* the x mouse coordinate in the component.
|
||||
* @param y
|
||||
* the y mouse coordinate in the component.
|
||||
* @param loc
|
||||
* will be filled in with the FieldLocation for the given point.
|
||||
* Values will be undefined if the Field return value is null.
|
||||
* @param x the x mouse coordinate in the component.
|
||||
* @param y the y mouse coordinate in the component.
|
||||
* @param loc will be filled in with the FieldLocation for the given point. Values will be
|
||||
* undefined if the Field return value is null.
|
||||
* @return Field the Field object the point is over.
|
||||
*/
|
||||
public Field getFieldAt(int x, int y, FieldLocation loc) {
|
||||
@ -644,9 +638,7 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Sets the cursor color for when this component has focus.
|
||||
*
|
||||
* @param color
|
||||
* Color to use for the cursor when this component has keyboard
|
||||
* focus.
|
||||
* @param color Color to use for the cursor when this component has keyboard focus.
|
||||
*/
|
||||
public void setFocusedCursorColor(Color color) {
|
||||
paintContext.setFocusedCursorColor(color);
|
||||
@ -659,9 +651,7 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Sets the cursor color for when this component does not have focus.
|
||||
*
|
||||
* @param color
|
||||
* Color to use for the cursor when this component does not have
|
||||
* keyboard focus.
|
||||
* @param color Color to use for the cursor when this component does not have keyboard focus.
|
||||
*/
|
||||
public void setNonFocusCursorColor(Color color) {
|
||||
paintContext.setNotFocusedCursorColor(color);
|
||||
@ -688,8 +678,7 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Sets the current selection.
|
||||
*
|
||||
* @param sel
|
||||
* the selection to set.
|
||||
* @param sel the selection to set.
|
||||
*/
|
||||
public void setSelection(FieldSelection sel) {
|
||||
if (!selectionHandler.isSelectionOn()) {
|
||||
@ -703,8 +692,7 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Sets the current highlight to the specified field selection.
|
||||
*
|
||||
* @param sel
|
||||
* the selection to set as the highlight.
|
||||
* @param sel the selection to set as the highlight.
|
||||
*/
|
||||
public void setHighlight(FieldSelection sel) {
|
||||
highlight = new FieldSelection(sel);
|
||||
@ -716,8 +704,7 @@ public class FieldPanel extends JPanel
|
||||
* Sets the cursorPosition to the given location.
|
||||
*
|
||||
* @param index the index of the Layout on which to place the cursor.
|
||||
* @param fieldNum the index of the field within its layout on which to place the
|
||||
* cursor.
|
||||
* @param fieldNum the index of the field within its layout on which to place the cursor.
|
||||
* @param row the row within the field to place the cursor.
|
||||
* @param col the col within the row to place the cursor.
|
||||
* @return true if the cursor changed
|
||||
@ -727,7 +714,17 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
|
||||
// for subclasses to control the event trigger
|
||||
protected boolean setCursorPosition(BigInteger index, int fieldNum, int row, int col,
|
||||
/**
|
||||
* Sets the cursorPosition to the given location with the given trigger.
|
||||
*
|
||||
* @param index the index of the Layout on which to place the cursor.
|
||||
* @param fieldNum the index of the field within its layout on which to place the cursor.
|
||||
* @param row the row within the field to place the cursor.
|
||||
* @param col the col within the row to place the cursor.
|
||||
* @param trigger a caller-specified event trigger.
|
||||
* @return true if the cursor changed
|
||||
*/
|
||||
public boolean setCursorPosition(BigInteger index, int fieldNum, int row, int col,
|
||||
EventTrigger trigger) {
|
||||
if (cursorHandler.doSetCursorPosition(index, fieldNum, row, col, trigger)) {
|
||||
repaint();
|
||||
@ -737,11 +734,10 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cursor on or off. When the cursor is turned off, there is no
|
||||
* visible cursor displayed on the screen.
|
||||
* Sets the cursor on or off. When the cursor is turned off, there is no visible cursor
|
||||
* displayed on the screen.
|
||||
*
|
||||
* @param cursorOn
|
||||
* true turns the cursor on, false turns it off.
|
||||
* @param cursorOn true turns the cursor on, false turns it off.
|
||||
*/
|
||||
public void setCursorOn(boolean cursorOn) {
|
||||
cursorHandler.setCursorOn(cursorOn);
|
||||
@ -759,20 +755,15 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cursor to the given Field location and attempts to show that
|
||||
* location in the center of the screen.
|
||||
* Sets the cursor to the given Field location and attempts to show that location in the center
|
||||
* of the screen.
|
||||
*
|
||||
* @param index
|
||||
* the index of the line to go to.
|
||||
* @param fieldNum
|
||||
* the field on the line to go to.
|
||||
* @param row
|
||||
* the row in the field to go to.
|
||||
* @param col
|
||||
* the column in the field to go to.
|
||||
* @param alwaysCenterCursor
|
||||
* if true, centers cursor on screen. Otherwise, only centers
|
||||
* cursor if cursor is offscreen.
|
||||
* @param index the index of the line to go to.
|
||||
* @param fieldNum the field on the line to go to.
|
||||
* @param row the row in the field to go to.
|
||||
* @param col the column in the field to go to.
|
||||
* @param alwaysCenterCursor if true, centers cursor on screen. Otherwise, only centers cursor
|
||||
* if cursor is offscreen.
|
||||
*/
|
||||
public void goTo(BigInteger index, int fieldNum, int row, int col, boolean alwaysCenterCursor) {
|
||||
goTo(index, fieldNum, row, col, alwaysCenterCursor, EventTrigger.API_CALL);
|
||||
@ -804,12 +795,10 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the view so that the cursor is at the given offset from the top
|
||||
* of the screen
|
||||
* Scrolls the view so that the cursor is at the given offset from the top of the screen
|
||||
*
|
||||
* @param offset
|
||||
* the pixel distance from the top of the screen at which to
|
||||
* scroll the display such that the cursor is at that offset.
|
||||
* @param offset the pixel distance from the top of the screen at which to scroll the display
|
||||
* such that the cursor is at that offset.
|
||||
*/
|
||||
public void positionCursor(int offset) {
|
||||
if (offset < 0) {
|
||||
@ -831,8 +820,7 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Sets the selection color
|
||||
*
|
||||
* @param color
|
||||
* the color to use for selection.
|
||||
* @param color the color to use for selection.
|
||||
*/
|
||||
public void setSelectionColor(Color color) {
|
||||
paintContext.setSelectionColor(color);
|
||||
@ -841,20 +829,18 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Sets the highlight color
|
||||
*
|
||||
* @param color
|
||||
* the color to use for highlights.
|
||||
* @param color the color to use for highlights.
|
||||
*/
|
||||
public void setHighlightColor(Color color) {
|
||||
paintContext.setHighlightColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ViewerPosition object which contains the top of screen
|
||||
* information. The ViewerPosition will have the index of the layout at the
|
||||
* top of the screen and the yPos of that layout. For example, if the layout
|
||||
* is completely displayed, yPos will be 0. If part of the layout is off the
|
||||
* top off the screen, then yPos will have a negative value (indicating that
|
||||
* it begins above the displayable part of the screen.
|
||||
* Returns a ViewerPosition object which contains the top of screen information. The
|
||||
* ViewerPosition will have the index of the layout at the top of the screen and the yPos of
|
||||
* that layout. For example, if the layout is completely displayed, yPos will be 0. If part of
|
||||
* the layout is off the top off the screen, then yPos will have a negative value (indicating
|
||||
* that it begins above the displayable part of the screen.
|
||||
*/
|
||||
public ViewerPosition getViewerPosition() {
|
||||
if (layouts.size() > 0) {
|
||||
@ -864,15 +850,12 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the display to show the layout specified by index at the vertical
|
||||
* position specified by yPos. Generally, the index will be layout at the
|
||||
* top of the screen and the yPos will be <= 0, meaning the layout may be
|
||||
* partially off the top of the screen.
|
||||
* Scrolls the display to show the layout specified by index at the vertical position specified
|
||||
* by yPos. Generally, the index will be layout at the top of the screen and the yPos will be
|
||||
* <= 0, meaning the layout may be partially off the top of the screen.
|
||||
*
|
||||
* @param index
|
||||
* the index of the layout to show at the top of the screen.
|
||||
* @param yPos
|
||||
* the position to show the layout.
|
||||
* @param index the index of the layout to show at the top of the screen.
|
||||
* @param yPos the position to show the layout.
|
||||
*/
|
||||
public void setViewerPosition(BigInteger index, int xPos, int yPos) {
|
||||
if (index.compareTo(BigInteger.ZERO) >= 0 && index.compareTo(model.getNumIndexes()) < 0) {
|
||||
@ -894,8 +877,7 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Sets the layout model for this field panel
|
||||
*
|
||||
* @param model
|
||||
* the layout model to use.
|
||||
* @param model the layout model to use.
|
||||
*/
|
||||
public void setLayoutModel(LayoutModel model) {
|
||||
invalidate();
|
||||
@ -1663,18 +1645,18 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
|
||||
/**
|
||||
* Basically checks if the the "shift" modifier is on and the "control"
|
||||
* modifier is not. Note that "control" is operating system dependent.
|
||||
* It is <control> on windows, and <command> on mac.
|
||||
* Basically checks if the the "shift" modifier is on and the "control" modifier is not.
|
||||
* Note that "control" is operating system dependent. It is <control> on windows, and
|
||||
* <command> on mac.
|
||||
*/
|
||||
private boolean isAddToContiguousSelectionActivator(MouseEvent e) {
|
||||
return (e.isShiftDown() && !DockingUtils.isControlModifier(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* Basically checks if the the "control" modifier is on and the shift
|
||||
* modifier is not. Note that "control" is operating system dependent.
|
||||
* It is <control> on windows, and <command> on mac.
|
||||
* Basically checks if the the "control" modifier is on and the shift modifier is not. Note
|
||||
* that "control" is operating system dependent. It is <control> on windows, and <command>
|
||||
* on mac.
|
||||
*/
|
||||
private boolean isAddRemoveDisjointSelectionActivator(MouseEvent e) {
|
||||
return DockingUtils.isControlModifier(e) && !e.isShiftDown();
|
||||
@ -1896,10 +1878,8 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Sets the cursor as close to the given point as possible.
|
||||
*
|
||||
* @param x
|
||||
* the horizontal coordinate.
|
||||
* @param y
|
||||
* the vertical coordinate.
|
||||
* @param x the horizontal coordinate.
|
||||
* @param y the vertical coordinate.
|
||||
*/
|
||||
private void setCursorPos(int x, int y, EventTrigger trigger) {
|
||||
currentField = null;
|
||||
|
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() {
|
||||
@ -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
|
||||
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
|
||||
@ -528,26 +551,125 @@ public final class NumericUtilities {
|
||||
* <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>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;
|
||||
}
|
||||
|
||||
@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;
|
||||
protected Map<K, WeakValueRef<K, V>> getRefMap() {
|
||||
return refMap;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -20,17 +20,18 @@ import java.util.*;
|
||||
/**
|
||||
* A directed graph
|
||||
*
|
||||
* Unlike {@link GImplicitDirectedGraph}, this graph is constructed explicitly in memory. Edges and
|
||||
* vertices are added and removed like any other collection, and these elements represent the
|
||||
* entirety of the graph at any given time.
|
||||
* Unlike {@link GImplicitDirectedGraph}, this graph is constructed explicitly
|
||||
* in memory. Edges and vertices are added and removed like any other
|
||||
* collection, and these elements represent the entirety of the graph at any
|
||||
* given time.
|
||||
*
|
||||
* @param <V> the type of vertices
|
||||
* @param <E> the type of edges
|
||||
*/
|
||||
public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
|
||||
public interface GDirectedGraph<V, E extends GEdge<V>> extends GImplicitDirectedGraph<V, E> {
|
||||
/**
|
||||
* Add a vertex
|
||||
*
|
||||
* @param v the vertex
|
||||
* @return true if the add was successful, false otherwise
|
||||
*/
|
||||
@ -38,6 +39,7 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
|
||||
/**
|
||||
* Remove a vertex
|
||||
*
|
||||
* @param v the vertex
|
||||
* @return true
|
||||
*/
|
||||
@ -52,12 +54,14 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
|
||||
/**
|
||||
* Add an edge
|
||||
*
|
||||
* @param e the edge
|
||||
*/
|
||||
public void addEdge(E e);
|
||||
|
||||
/**
|
||||
* Removes an edge
|
||||
*
|
||||
* @param e the edge
|
||||
* @return true if the graph contained the given edge
|
||||
*/
|
||||
@ -81,18 +85,21 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
|
||||
/**
|
||||
* Retrieve all the vertices
|
||||
*
|
||||
* @return the vertices
|
||||
*/
|
||||
public Collection<V> getVertices();
|
||||
|
||||
/**
|
||||
* Retrieve all the edges
|
||||
*
|
||||
* @return the edges
|
||||
*/
|
||||
public Collection<E> getEdges();
|
||||
|
||||
/**
|
||||
* Test if the graph contains a given vertex
|
||||
*
|
||||
* @param v the vertex
|
||||
* @return true if the vertex is in the graph, or false
|
||||
*/
|
||||
@ -100,6 +107,7 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
|
||||
/**
|
||||
* Test if the graph contains a given edge
|
||||
*
|
||||
* @param e the ege
|
||||
* @return true if the edge is in the graph, or false
|
||||
*/
|
||||
@ -107,6 +115,7 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
|
||||
/**
|
||||
* Test if the graph contains an edge from one given vertex to another
|
||||
*
|
||||
* @param from the source vertex
|
||||
* @param to the destination vertex
|
||||
* @return true if such an edge exists, or false
|
||||
@ -115,18 +124,21 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
|
||||
/**
|
||||
* Test if the graph is empty, i.e., contains no vertices or edges
|
||||
*
|
||||
* @return true if the graph is empty, or false
|
||||
*/
|
||||
public boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Count the number of vertices in the graph
|
||||
*
|
||||
* @return the count
|
||||
*/
|
||||
public int getVertexCount();
|
||||
|
||||
/**
|
||||
* Count the number of edges in the graph
|
||||
*
|
||||
* @return the count
|
||||
*/
|
||||
public int getEdgeCount();
|
||||
@ -137,6 +149,7 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
* @param v the destination vertex
|
||||
* @return the in-edges to the given vertex
|
||||
*/
|
||||
@Override
|
||||
public Collection<E> getInEdges(V v);
|
||||
|
||||
/**
|
||||
@ -145,6 +158,7 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
* @param v the source vertex
|
||||
* @return the out-edges from the given vertex
|
||||
*/
|
||||
@Override
|
||||
public Collection<E> getOutEdges(V v);
|
||||
|
||||
/**
|
||||
@ -163,11 +177,13 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
/**
|
||||
* Compute a vertex's predecessors
|
||||
*
|
||||
* <P>The default implementation computes this from the in-edges
|
||||
* <P>
|
||||
* The default implementation computes this from the in-edges
|
||||
*
|
||||
* @param v the destination vertex
|
||||
* @return the predecessors
|
||||
*/
|
||||
@Override
|
||||
public default Collection<V> getPredecessors(V v) {
|
||||
Set<V> result = new LinkedHashSet<>();
|
||||
for (E edge : getInEdges(v)) {
|
||||
@ -179,11 +195,13 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
/**
|
||||
* Compute a vertex's successors
|
||||
*
|
||||
* <P>The default implementation compute this from the out-edges
|
||||
* <P>
|
||||
* The default implementation compute this from the out-edges
|
||||
*
|
||||
* @param v the source vertex
|
||||
* @return the successors
|
||||
*/
|
||||
@Override
|
||||
public default Collection<V> getSuccessors(V v) {
|
||||
Set<V> result = new LinkedHashSet<>();
|
||||
for (E edge : getOutEdges(v)) {
|
||||
@ -195,16 +213,19 @@ public interface GDirectedGraph<V, E extends GEdge<V>> {
|
||||
/**
|
||||
* Copy this graph.
|
||||
*
|
||||
* <P>Note: the vertices and edges in the copy may be the same instances in the new graph
|
||||
* and not themselves copies.
|
||||
* <P>
|
||||
* Note: the vertices and edges in the copy may be the same instances in the
|
||||
* new graph and not themselves copies.
|
||||
*
|
||||
* @return the new copy
|
||||
*/
|
||||
@Override
|
||||
public GDirectedGraph<V, E> copy();
|
||||
|
||||
/**
|
||||
* Creates a new instance of this graph with no vertices or edges. This is useful when
|
||||
* you wish to build a new graph using the same type as this graph.
|
||||
* Creates a new instance of this graph with no vertices or edges. This is
|
||||
* useful when you wish to build a new graph using the same type as this
|
||||
* graph.
|
||||
*
|
||||
* @return the new copy
|
||||
*/
|
||||
|
@ -15,33 +15,24 @@
|
||||
*/
|
||||
package ghidra.graph.algo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
import generic.util.DequePush;
|
||||
import ghidra.generic.util.datastruct.DynamicValueSortedTreeMap;
|
||||
import ghidra.graph.GEdge;
|
||||
import ghidra.graph.GEdgeWeightMetric;
|
||||
import ghidra.graph.GImplicitDirectedGraph;
|
||||
import ghidra.graph.GWeightedEdge;
|
||||
import ghidra.generic.util.datastruct.TreeValueSortedMap;
|
||||
import ghidra.generic.util.datastruct.ValueSortedMap;
|
||||
import ghidra.graph.*;
|
||||
|
||||
/**
|
||||
* Dijkstra's shortest-path algorithm
|
||||
*
|
||||
* <p>
|
||||
* This implementation computes the shortest paths between two vertices using Dijkstra's
|
||||
* single-source shortest path finding algorithm. Any time a new source is given, it explores
|
||||
* all destinations in the graph up to a maximum distance from the source. Thus, this
|
||||
* implementation is best applied when many queries are anticipated from relatively few sources.
|
||||
* single-source shortest path finding algorithm. Any time a new source is given, it explores all
|
||||
* destinations in the graph up to a maximum distance from the source. Thus, this implementation is
|
||||
* best applied when many queries are anticipated from relatively few sources.
|
||||
*
|
||||
* @param <V> the type of vertices
|
||||
* @param <E> the type of edges
|
||||
@ -56,6 +47,7 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
/**
|
||||
* Use Dijkstra's algorithm on the given graph
|
||||
*
|
||||
* <p>
|
||||
* This constructor assumes the graph's edges are {@link GWeightedEdge}s. If not, you will
|
||||
* likely encounter a {@link ClassCastException}.
|
||||
*
|
||||
@ -70,6 +62,7 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
/**
|
||||
* Use Dijkstra's algorithm on the given graph with the given maximum distance
|
||||
*
|
||||
* <p>
|
||||
* This constructor assumes the graph's edges are {@link GWeightedEdge}s. If not, you will
|
||||
* likely encounter a {@link ClassCastException}.
|
||||
*
|
||||
@ -96,8 +89,8 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Dijstra's algorithm on the given graph with the given maximum distance and a custom
|
||||
* edge weight metric
|
||||
* Use Dijstra's algorithm on the given graph with the given maximum distance and a custom edge
|
||||
* weight metric
|
||||
*
|
||||
* @param graph the graph
|
||||
* @param maxDistance the maximum distance, or null for no maximum
|
||||
@ -124,6 +117,7 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
/**
|
||||
* Compute the shortest paths from the given source to the given destination
|
||||
*
|
||||
* <p>
|
||||
* This implementation differs from typical implementations in that paths tied for the shortest
|
||||
* distance are all returned. Others tend to choose one arbitrarily.
|
||||
*
|
||||
@ -139,16 +133,17 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
* A class representing all optimal paths from a given source to every other (reachable) vertex
|
||||
* in the graph
|
||||
*
|
||||
* <p>
|
||||
* This is the workhorse of path computation, and implements Dijkstra's Shortest Path algorithm
|
||||
* from one source to all destinations. We considered using JUNG to store the graph and compute
|
||||
* the paths, but we could not, because we would like to find all paths having the
|
||||
* optimal distance. If there are ties, JUNG's implementation chooses one arbitrarily; we would
|
||||
* like all tied paths.
|
||||
* the paths, but we could not, because we would like to find all paths having the optimal
|
||||
* distance. If there are ties, JUNG's implementation chooses one arbitrarily; we would like all
|
||||
* tied paths.
|
||||
*/
|
||||
protected class OneSourceToAll {
|
||||
// For explored, but unvisited nodes
|
||||
protected final DynamicValueSortedTreeMap<V, Double> queueByDistance =
|
||||
new DynamicValueSortedTreeMap<>();
|
||||
protected final ValueSortedMap<V, Double> queueByDistance =
|
||||
TreeValueSortedMap.createWithNaturalOrder();
|
||||
// For visited nodes, i.e., their optimal distance is known
|
||||
protected final Map<V, Double> visitedDistance = new LinkedHashMap<>();
|
||||
protected final Map<V, Set<E>> bestIns =
|
||||
@ -159,6 +154,7 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
/**
|
||||
* Compute the shortest paths from a given vertex to all other reachable vertices in the
|
||||
* graph
|
||||
*
|
||||
* @param src the source (seed) vertex
|
||||
*/
|
||||
protected OneSourceToAll(V src) {
|
||||
@ -169,6 +165,7 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
|
||||
/**
|
||||
* Recover the shortest paths from the source to the given destination, if it is reachable
|
||||
*
|
||||
* @param dst the destination
|
||||
* @return a collection of the shortest paths from source to destination, or the empty set
|
||||
*/
|
||||
@ -179,10 +176,11 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the shortest paths from the source to the given destination into the given
|
||||
* collection
|
||||
* Add the shortest paths from the source to the given destination into the given collection
|
||||
*
|
||||
* <p>
|
||||
* This is used internally to recover the shortest paths
|
||||
*
|
||||
* @param paths a place to store the recovered paths
|
||||
* @param dst the destination
|
||||
*/
|
||||
@ -191,12 +189,14 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the shortest paths from source to a given intermediate, continuing along a given
|
||||
* path to the final destination, into the given collection
|
||||
* Add the shortest paths from source to a given intermediate, continuing along a given path
|
||||
* to the final destination, into the given collection
|
||||
*
|
||||
* <p>
|
||||
* This is a recursive method for constructing the shortest paths overall. Assuming the
|
||||
* given path from intermediate to final destination is the shortest, we can show by
|
||||
* induction, the computed paths from source to destination are the shortest.
|
||||
*
|
||||
* @param paths a place to store the recovered paths
|
||||
* @param prev the intermediate destination
|
||||
* @param soFar a (shortest) path from intermediate to final destination
|
||||
@ -208,10 +208,11 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
paths.add(new LinkedList<>(soFar));
|
||||
}
|
||||
else { // inductive case:
|
||||
/* Dijkstra has computed the best inbound edges. Consider each as a prefix to the
|
||||
* current path from intermediate to final destination. Since we assume that path
|
||||
* is an optimal path, and we prefix an optimal inbound edge, the prefixed path is
|
||||
* an optimal path from a new intermediate source (inbound neighbor) to the final
|
||||
/*
|
||||
* Dijkstra has computed the best inbound edges. Consider each as a prefix to the
|
||||
* current path from intermediate to final destination. Since we assume that path is
|
||||
* an optimal path, and we prefix an optimal inbound edge, the prefixed path is an
|
||||
* optimal path from a new intermediate source (inbound neighbor) to the final
|
||||
* destination. So, just recurse, using the new intermediates.
|
||||
*/
|
||||
for (E e : bestIns.get(prev)) {
|
||||
@ -226,14 +227,17 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
/**
|
||||
* Update the record for the given destination with a new offer of shortest distance
|
||||
*
|
||||
* If either the record doesn't exist yet, or the new offer beats the current best, then
|
||||
* a new record is created and replaces the current record. If present, the list of best
|
||||
* <p>
|
||||
* If either the record doesn't exist yet, or the new offer beats the current best, then a
|
||||
* new record is created and replaces the current record. If present, the list of best
|
||||
* inbound edges is cleared -- because they all correspond to a distance that has just been
|
||||
* beat. The node is also added and/or moved forward in the queue of unvisited vertices.
|
||||
*
|
||||
* If the record exists, and the new offer ties the current offer, nothing happens, but
|
||||
* the method still returns true, since the corresponding inbound edge could be optimal.
|
||||
* <p>
|
||||
* If the record exists, and the new offer ties the current offer, nothing happens, but the
|
||||
* method still returns true, since the corresponding inbound edge could be optimal.
|
||||
*
|
||||
* <p>
|
||||
* If the record's current best beats the offer, nothing happens, and the method returns
|
||||
* false, indicating the inbound edge is definitely not optimal.
|
||||
*
|
||||
@ -278,6 +282,7 @@ public class DijkstraShortestPathsAlgorithm<V, E extends GEdge<V>> {
|
||||
|
||||
/**
|
||||
* Perform one iteration of Dijskstra's path finding algorithm
|
||||
*
|
||||
* @param from the vertex to visit for this iteration
|
||||
*/
|
||||
protected void fillStep(V from, double dist) {
|
||||
|
@ -0,0 +1,29 @@
|
||||
/* ###
|
||||
* 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.graph.algo;
|
||||
|
||||
/**
|
||||
* Occurs when a graph cannot be sorted
|
||||
*/
|
||||
public class SorterException extends Exception {
|
||||
public SorterException(String desc, Object v1, Object v2) {
|
||||
super(desc + ": " + v1 + " ?? " + v2);
|
||||
}
|
||||
|
||||
public SorterException(String desc, Iterable<?> vs) {
|
||||
super(desc + ": " + vs);
|
||||
}
|
||||
}
|
@ -21,10 +21,10 @@ import db.Record;
|
||||
import ghidra.util.Lock;
|
||||
|
||||
/**
|
||||
* Base class for an cached object in the database. Database objects have keys. They are marked
|
||||
* as invalid when a database cache is cleared and can be revived on a refresh as long
|
||||
* as they haven't been deleted. Instantiating an object will cause it to be added
|
||||
* immediately to the associated cache.
|
||||
* Base class for an cached object in the database. Database objects have keys. They are marked as
|
||||
* invalid when a database cache is cleared and can be revived on a refresh as long as they haven't
|
||||
* been deleted. Instantiating an object will cause it to be added immediately to the associated
|
||||
* cache.
|
||||
*/
|
||||
abstract public class DatabaseObject {
|
||||
|
||||
@ -36,6 +36,7 @@ abstract public class DatabaseObject {
|
||||
|
||||
/**
|
||||
* Constructs a new DatabaseObject and adds it to the specified cache.
|
||||
*
|
||||
* @param cache to be used for this object or null if object will not be cached
|
||||
* @param key database key to uniquely identify this object
|
||||
*/
|
||||
@ -64,9 +65,10 @@ abstract public class DatabaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this object has been deleted. Note: once an object has been deleted,
|
||||
* it will never be "refreshed". For example, if an object is ever deleted and is
|
||||
* resurrected via an "undo", you will have get a fresh instance of the object.
|
||||
* Returns true if this object has been deleted. Note: once an object has been deleted, it will
|
||||
* never be "refreshed". For example, if an object is ever deleted and is resurrected via an
|
||||
* "undo", you will have get a fresh instance of the object.
|
||||
*
|
||||
* @return true if this object has been deleted.
|
||||
*/
|
||||
public boolean isDeleted() {
|
||||
@ -75,8 +77,8 @@ abstract public class DatabaseObject {
|
||||
|
||||
/**
|
||||
*
|
||||
* Invalidate this object. This does not necessarily mean that this object can
|
||||
* never be used again. If the object can refresh itself, it may still be useable.
|
||||
* Invalidate this object. This does not necessarily mean that this object can never be used
|
||||
* again. If the object can refresh itself, it may still be useable.
|
||||
*/
|
||||
public void setInvalid() {
|
||||
invalidateCount = getCurrentValidationCount() - 1;
|
||||
@ -99,8 +101,9 @@ abstract public class DatabaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if object is currently invalid. Calling checkIsValid may
|
||||
* successfully refresh object making it valid.
|
||||
* Returns true if object is currently invalid. Calling checkIsValid may successfully refresh
|
||||
* object making it valid.
|
||||
*
|
||||
* @see #checkIsValid()
|
||||
*/
|
||||
public boolean isInvalid() {
|
||||
@ -108,8 +111,8 @@ abstract public class DatabaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this object has been deleted, in which case any use of the object is
|
||||
* not allowed.
|
||||
* Checks if this object has been deleted, in which case any use of the object is not allowed.
|
||||
*
|
||||
* @throws ConcurrentModificationException if the object has been deleted from the database.
|
||||
*/
|
||||
public void checkDeleted() {
|
||||
@ -119,8 +122,9 @@ abstract public class DatabaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this object is still valid. If the object is invalid, the object will
|
||||
* attempt to refresh itself. If the refresh fails, the object will be marked as deleted.
|
||||
* Check whether this object is still valid. If the object is invalid, the object will attempt
|
||||
* to refresh itself. If the refresh fails, the object will be marked as deleted.
|
||||
*
|
||||
* @return true if the object is valid.
|
||||
*/
|
||||
public boolean checkIsValid() {
|
||||
@ -128,10 +132,11 @@ abstract public class DatabaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this object is still valid. If the object is invalid, the object will
|
||||
* attempt to refresh itself using the specified record. If the refresh fails, the
|
||||
* object will be marked as deleted and removed from cache. If this object is already
|
||||
* marked as deleted, the record can not be used to refresh the object.
|
||||
* Check whether this object is still valid. If the object is invalid, the object will attempt
|
||||
* to refresh itself using the specified record. If the refresh fails, the object will be marked
|
||||
* as deleted and removed from cache. If this object is already marked as deleted, the record
|
||||
* can not be used to refresh the object.
|
||||
*
|
||||
* @param record optional record which may be used to refresh invalid object
|
||||
* @return true if the object is valid.
|
||||
*/
|
||||
@ -153,8 +158,9 @@ abstract public class DatabaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method provides a cheap (lock free) way to test if an object is valid. If
|
||||
* this object is invalid, then the lock will be used to refresh as needed.
|
||||
* This method provides a cheap (lock free) way to test if an object is valid. If this object is
|
||||
* invalid, then the lock will be used to refresh as needed.
|
||||
*
|
||||
* @param lock the lock that will be used if the object needs to be refreshed.
|
||||
* @return true if object is valid, else false
|
||||
*/
|
||||
@ -173,23 +179,24 @@ abstract public class DatabaseObject {
|
||||
|
||||
/**
|
||||
* Tells the object to refresh its state from the database.
|
||||
* @return true if the object was able to refresh itself. Return false if the object
|
||||
* was deleted. Objects that extend this class must implement a refresh method. If
|
||||
* an object can never refresh itself, then it should always return false.
|
||||
*
|
||||
* @return true if the object was able to refresh itself. Return false if the object was
|
||||
* deleted. Objects that extend this class must implement a refresh method. If an object
|
||||
* can never refresh itself, then it should always return false.
|
||||
*/
|
||||
protected abstract boolean refresh();
|
||||
|
||||
/**
|
||||
* Tells the object to refresh its state from the database using the specified
|
||||
* record if not null. NOTE: The default implementation ignores the record
|
||||
* and invokes refresh(). Implementations of this method must take care if
|
||||
* multiple database tables are used since the record supplied could correspond
|
||||
* to another object. In some cases it may be best not to override this method
|
||||
* or ignore the record provided.
|
||||
* Tells the object to refresh its state from the database using the specified record if not
|
||||
* null. NOTE: The default implementation ignores the record and invokes refresh().
|
||||
* Implementations of this method must take care if multiple database tables are used since the
|
||||
* record supplied could correspond to another object. In some cases it may be best not to
|
||||
* override this method or ignore the record provided.
|
||||
*
|
||||
* @param record valid record associated with object's key (optional, may be null to force
|
||||
* record lookup or other refresh technique)
|
||||
* @return true if the object was able to refresh itself. Return false if record is null
|
||||
* and object was deleted. Objects that extend this class must implement a refresh method.
|
||||
* @return true if the object was able to refresh itself. Return false if record is null and
|
||||
* object was deleted. Objects that extend this class must implement a refresh method.
|
||||
* If an object can never refresh itself, then it should always return false.
|
||||
*/
|
||||
protected boolean refresh(Record record) {
|
||||
|
@ -141,11 +141,13 @@ public class DataTypeUtilities {
|
||||
|
||||
/**
|
||||
* Check to see if the second data type is the same as the first data type or is part of it.
|
||||
* <br>Note: pointers to the second data type are references and therefore are not considered
|
||||
* to be part of the first and won't cause true to be returned. If you pass a pointer to this
|
||||
* method for the first or second parameter, it will return false.
|
||||
* @param firstDataType the data type whose components or base type should be checked to see
|
||||
* if the second data type is part of it.
|
||||
* <br>
|
||||
* Note: pointers to the second data type are references and therefore are not considered to be
|
||||
* part of the first and won't cause true to be returned. If you pass a pointer to this method
|
||||
* for the first or second parameter, it will return false.
|
||||
*
|
||||
* @param firstDataType the data type whose components or base type should be checked to see if
|
||||
* the second data type is part of it.
|
||||
* @param secondDataType the data type to be checked for in the first data type.
|
||||
* @return true if the second data type is the first data type or is part of it.
|
||||
*/
|
||||
@ -184,6 +186,7 @@ public class DataTypeUtilities {
|
||||
|
||||
/**
|
||||
* Returns true if the two dataTypes have the same sourceArchive and the same UniversalID
|
||||
*
|
||||
* @param dataType1 first data type
|
||||
* @param dataType2 second data type
|
||||
* @return true if types correspond to the same type from a source archive
|
||||
@ -207,13 +210,14 @@ public class DataTypeUtilities {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the two dataTypes have the same sourceArchive and the same UniversalID OR
|
||||
* are equivalent
|
||||
* @param dataType1 first data type (if invoked by DB object or manager, this argument
|
||||
* must correspond to the DataTypeDB).
|
||||
* Returns true if the two dataTypes have the same sourceArchive and the same UniversalID OR are
|
||||
* equivalent
|
||||
*
|
||||
* @param dataType1 first data type (if invoked by DB object or manager, this argument must
|
||||
* correspond to the DataTypeDB).
|
||||
* @param dataType2 second data type
|
||||
* @return true if types correspond to the same type from a source archive
|
||||
* or they are equivelent, otherwise false
|
||||
* @return true if types correspond to the same type from a source archive or they are
|
||||
* equivelent, otherwise false
|
||||
*/
|
||||
public static boolean isSameOrEquivalentDataType(DataType dataType1, DataType dataType2) {
|
||||
// if they contain datatypes that have same ids, then they represent the same dataType
|
||||
@ -226,6 +230,7 @@ public class DataTypeUtilities {
|
||||
|
||||
/**
|
||||
* Get the name of a data type with all conflict naming patterns removed.
|
||||
*
|
||||
* @param dataType data type
|
||||
* @param includeCategoryPath if true the category path will be included with its
|
||||
* @return name with without conflict patterns
|
||||
@ -238,6 +243,7 @@ public class DataTypeUtilities {
|
||||
/**
|
||||
* Compares two data type name strings to determine if they are equivalent names, ignoring
|
||||
* conflict patterns present.
|
||||
*
|
||||
* @param name1 the first name
|
||||
* @param name2 the second name
|
||||
* @return true if the names are equivalent when conflict suffixes are ignored.
|
||||
@ -249,9 +255,8 @@ public class DataTypeUtilities {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base data type for the specified data type stripping
|
||||
* away pointers and arrays only. A null will be returned for a
|
||||
* default pointer.
|
||||
* Get the base data type for the specified data type stripping away pointers and arrays only. A
|
||||
* null will be returned for a default pointer.
|
||||
*
|
||||
* @param dt the data type whose base data type is to be determined.
|
||||
* @return the base data type.
|
||||
@ -333,8 +338,9 @@ public class DataTypeUtilities {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a data type category path derived from the specified namespace and rooted from
|
||||
* the specified baseCategory
|
||||
* Create a data type category path derived from the specified namespace and rooted from the
|
||||
* specified baseCategory
|
||||
*
|
||||
* @param baseCategory category path from which to root the namespace-base path
|
||||
* @param namespace the namespace
|
||||
* @return namespace derived category path
|
||||
@ -343,7 +349,7 @@ public class DataTypeUtilities {
|
||||
Namespace namespace) {
|
||||
Namespace ns = namespace;
|
||||
String path = "";
|
||||
while (!(ns instanceof GlobalNamespace) && !(ns instanceof Library)) {
|
||||
while (!ns.isGlobal() && !(ns instanceof Library)) {
|
||||
if (path.length() != 0) {
|
||||
path = "/" + path;
|
||||
}
|
||||
@ -361,11 +367,11 @@ public class DataTypeUtilities {
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find the data type whose dtName and specified namespace match a
|
||||
* stored data type within the specified dataTypeManager. The best match
|
||||
* will be returned. The namespace will be used in checking data type parent categories,
|
||||
* however if no type corresponds to the namespace another type whose name
|
||||
* matches may be returned.
|
||||
* Attempt to find the data type whose dtName and specified namespace match a stored data type
|
||||
* within the specified dataTypeManager. The best match will be returned. The namespace will be
|
||||
* used in checking data type parent categories, however if no type corresponds to the namespace
|
||||
* another type whose name matches may be returned.
|
||||
*
|
||||
* @param dataTypeManager data type manager
|
||||
* @param namespace namespace associated with dtName (null indicates no namespace constraint)
|
||||
* @param dtName name of data type
|
||||
@ -379,15 +385,15 @@ public class DataTypeUtilities {
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find the data type whose dtNameWithNamespace match a
|
||||
* stored data type within the specified dataTypeManager. The best match
|
||||
* will be returned. The namespace will be used in checking data type parent categories,
|
||||
* however if no type corresponds to the namespace another type whose name
|
||||
* matches may be returned.
|
||||
* NOTE: name parsing assumes :: delimiter and can be thrown off if name include template
|
||||
* information which could contain namespaces.
|
||||
* Attempt to find the data type whose dtNameWithNamespace match a stored data type within the
|
||||
* specified dataTypeManager. The best match will be returned. The namespace will be used in
|
||||
* checking data type parent categories, however if no type corresponds to the namespace another
|
||||
* type whose name matches may be returned. NOTE: name parsing assumes :: delimiter and can be
|
||||
* thrown off if name include template information which could contain namespaces.
|
||||
*
|
||||
* @param dataTypeManager data type manager
|
||||
* @param dtNameWithNamespace name of data type qualified with namespace (e.g., ns1::ns2::dtname)
|
||||
* @param dtNameWithNamespace name of data type qualified with namespace (e.g.,
|
||||
* ns1::ns2::dtname)
|
||||
* @param classConstraint optional data type interface constraint (e.g., Structure), or null
|
||||
* @return best matching data type
|
||||
*/
|
||||
@ -403,6 +409,7 @@ public class DataTypeUtilities {
|
||||
|
||||
/**
|
||||
* Return the appropriate datatype for a given C primitive datatype name.
|
||||
*
|
||||
* @param dataTypeName the datatype name (e.g. "unsigned int", "long long")
|
||||
* @return the appropriate datatype for a given C primitive datatype name.
|
||||
*/
|
||||
@ -452,8 +459,8 @@ public class DataTypeUtilities {
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>NamespaceMatcher</code> is used to check data type categoryPath
|
||||
* for match against preferred namespace.
|
||||
* <code>NamespaceMatcher</code> is used to check data type categoryPath for match against
|
||||
* preferred namespace.
|
||||
*/
|
||||
private static interface NamespaceMatcher {
|
||||
boolean isNamespaceCategoryMatch(DataType dataType);
|
||||
|
@ -685,10 +685,9 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create copy of structure for target dtm (source archive information is
|
||||
* discarded). WARNING! copying unaligned structures which contain bitfields can
|
||||
* produce invalid results when switching endianess due to the differences in
|
||||
* packing order.
|
||||
* Create copy of structure for target dtm (source archive information is discarded). WARNING!
|
||||
* copying unaligned structures which contain bitfields can produce invalid results when
|
||||
* switching endianess due to the differences in packing order.
|
||||
*
|
||||
* @param dtm target data type manager
|
||||
* @return cloned structure
|
||||
@ -703,16 +702,15 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create cloned structure for target dtm preserving source archive information.
|
||||
* WARNING! cloning unaligned structures which contain bitfields can produce
|
||||
* invalid results when switching endianess due to the differences in packing
|
||||
* order.
|
||||
* Create cloned structure for target dtm preserving source archive information. WARNING!
|
||||
* cloning unaligned structures which contain bitfields can produce invalid results when
|
||||
* switching endianess due to the differences in packing order.
|
||||
*
|
||||
* @param dtm target data type manager
|
||||
* @return cloned structure
|
||||
*/
|
||||
@Override
|
||||
public DataType clone(DataTypeManager dtm) {
|
||||
public Structure clone(DataTypeManager dtm) {
|
||||
StructureDataType struct =
|
||||
new StructureDataType(getCategoryPath(), getName(), getLength(), getUniversalID(),
|
||||
getSourceArchive(), getLastChangeTime(), getLastChangeTimeInSourceArchive(), dtm);
|
||||
@ -793,10 +791,9 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup from specified ordinal to the first component which contains the
|
||||
* specified offset. For normal components the specified ordinal will be
|
||||
* returned, however for bit-fields the ordinal of the first bit-field
|
||||
* containing the specified offset will be returned.
|
||||
* Backup from specified ordinal to the first component which contains the specified offset. For
|
||||
* normal components the specified ordinal will be returned, however for bit-fields the ordinal
|
||||
* of the first bit-field containing the specified offset will be returned.
|
||||
*
|
||||
* @param ordinal component ordinal
|
||||
* @param offset offset within structure
|
||||
@ -819,10 +816,9 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance from specified ordinal to the last component which contains the
|
||||
* specified offset. For normal components the specified ordinal will be
|
||||
* returned, however for bit-fields the ordinal of the last bit-field containing
|
||||
* the specified offset will be returned.
|
||||
* Advance from specified ordinal to the last component which contains the specified offset. For
|
||||
* normal components the specified ordinal will be returned, however for bit-fields the ordinal
|
||||
* of the last bit-field containing the specified offset will be returned.
|
||||
*
|
||||
* @param ordinal component ordinal
|
||||
* @param offset offset within structure
|
||||
@ -1137,15 +1133,12 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the internal components of this structure with components of the
|
||||
* given structure.
|
||||
* Replaces the internal components of this structure with components of the given structure.
|
||||
*
|
||||
* @param dataType the structure to get the component information from.
|
||||
* @throws IllegalArgumentException if any of the component data types are not
|
||||
* allowed to replace a component in this
|
||||
* composite data type. For example, suppose
|
||||
* dt1 contains dt2. Therefore it is not valid
|
||||
* to replace a dt2 component with dt1 since
|
||||
* @throws IllegalArgumentException if any of the component data types are not allowed to
|
||||
* replace a component in this composite data type. For example, suppose dt1
|
||||
* contains dt2. Therefore it is not valid to replace a dt2 component with dt1 since
|
||||
* this would cause a cyclic dependency.
|
||||
* @see ghidra.program.database.data.DataTypeDB#replaceWith(ghidra.program.model.data.DataType)
|
||||
*/
|
||||
@ -1179,8 +1172,7 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
*
|
||||
* @param struct
|
||||
* @param notify
|
||||
* @return true if fully completed else false if pointer component post resolve
|
||||
* required
|
||||
* @return true if fully completed else false if pointer component post resolve required
|
||||
* @throws DataTypeDependencyException
|
||||
* @throws IOException
|
||||
*/
|
||||
@ -1489,8 +1481,7 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param definedComponentIndex the index of the defined component that is
|
||||
* consuming the bytes.
|
||||
* @param definedComponentIndex the index of the defined component that is consuming the bytes.
|
||||
* @param numBytes the number of undefined bytes to consume
|
||||
* @return the number of bytes actually consumed
|
||||
*/
|
||||
@ -1554,8 +1545,8 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the indicated component with a new component containing the specified
|
||||
* data type. Flex-array component not handled.
|
||||
* Replace the indicated component with a new component containing the specified data type.
|
||||
* Flex-array component not handled.
|
||||
*
|
||||
* @param origDtc the original data type component in this structure.
|
||||
* @param resolvedDataType the data type of the new component
|
||||
@ -1638,9 +1629,8 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of Undefined bytes beginning at the indicated component
|
||||
* ordinal. Undefined bytes that have a field name or comment specified are also
|
||||
* included.
|
||||
* Gets the number of Undefined bytes beginning at the indicated component ordinal. Undefined
|
||||
* bytes that have a field name or comment specified are also included.
|
||||
*
|
||||
* @param ordinal the component ordinal to begin checking at.
|
||||
* @return the number of contiguous undefined bytes
|
||||
@ -1846,9 +1836,8 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>ComponentComparator</code> provides ability to compare two
|
||||
* DataTypeComponent objects based upon their ordinal. Intended to be used to
|
||||
* sort components based upon ordinal.
|
||||
* <code>ComponentComparator</code> provides ability to compare two DataTypeComponent objects
|
||||
* based upon their ordinal. Intended to be used to sort components based upon ordinal.
|
||||
*/
|
||||
private static class ComponentComparator implements Comparator<DataTypeComponent> {
|
||||
@Override
|
||||
@ -1858,17 +1847,15 @@ class StructureDB extends CompositeDB implements Structure {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the alignment, packing and padding of components within this structure
|
||||
* based upon the current alignment and packing attributes for this structure.
|
||||
* This method should be called to basically fix up the layout of the internal
|
||||
* components of the structure after other code has changed the attributes of
|
||||
* the structure. <BR>
|
||||
* When switching between internally aligned and unaligned this method corrects
|
||||
* the component ordinal numbering also.
|
||||
* Adjust the alignment, packing and padding of components within this structure based upon the
|
||||
* current alignment and packing attributes for this structure. This method should be called to
|
||||
* basically fix up the layout of the internal components of the structure after other code has
|
||||
* changed the attributes of the structure. <BR>
|
||||
* When switching between internally aligned and unaligned this method corrects the component
|
||||
* ordinal numbering also.
|
||||
*
|
||||
* @param notify if true this method will do data type change notification when
|
||||
* it changes the layout of the components or when it changes the
|
||||
* overall size of the structure.
|
||||
* @param notify if true this method will do data type change notification when it changes the
|
||||
* layout of the components or when it changes the overall size of the structure.
|
||||
* @return true if the structure was changed by this method.
|
||||
*/
|
||||
private boolean adjustComponents(boolean notify) {
|
||||
|
@ -13,73 +13,77 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.assembler.sleigh.util;
|
||||
package ghidra.program.database.util;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* A convenience context for transaction IDs on a Ghidra program database
|
||||
*
|
||||
* This is meant to be used idiomatically, as in a try-with-resources block:
|
||||
* <p>
|
||||
* This is meant to be used as an idiom in a try-with-resources block:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* try (GhidraDBTransaction t = new GhidraDBTransaction(program, "Demo")) {
|
||||
* try (ProgramTransaction t = ProgramTransaction.open(program, "Demo")) {
|
||||
* program.getMemory().....
|
||||
* t.commit();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* This idiom is very useful if there is complex logic in your transaction, it's very easy to
|
||||
* forget to close the transaction, especially if an error occurs, leaving the database in an open
|
||||
* transaction indefinitely. Try try-with-resources block will ensure that the transaction is
|
||||
* closed in all circumstances. Note, however, that in order for the transaction to be committed,
|
||||
* you must call {@link #commit()}.
|
||||
* <p>
|
||||
* This idiom is very useful if there is complex logic in your transaction, it's very easy to forget
|
||||
* to close the transaction, especially if an error occurs, leaving the database in an open
|
||||
* transaction indefinitely. The try-with-resources block will ensure that the transaction is closed
|
||||
* in all circumstances. Note, however, that in order for the transaction to be committed, you must
|
||||
* call {@link #commit()}.
|
||||
*
|
||||
* <p>
|
||||
* Any exceptions within the block will cause {@code t.commit()} to be skipped, thus aborting the
|
||||
* transaction.
|
||||
*/
|
||||
public class GhidraDBTransaction implements AutoCloseable {
|
||||
public class ProgramTransaction implements AutoCloseable {
|
||||
protected Program program;
|
||||
protected int tid;
|
||||
protected boolean open;
|
||||
protected boolean commit = false;
|
||||
|
||||
/**
|
||||
* Start a transaction on the given program with the given description
|
||||
*
|
||||
* @param program the program to modify
|
||||
* @param description a description of the transaction
|
||||
*/
|
||||
public GhidraDBTransaction(Program program, String description) {
|
||||
public static ProgramTransaction open(Program program, String description) {
|
||||
int tid = program.startTransaction(description);
|
||||
return new ProgramTransaction(program, tid);
|
||||
}
|
||||
|
||||
private ProgramTransaction(Program program, int tid) {
|
||||
this.program = program;
|
||||
this.tid = program.startTransaction(description);
|
||||
this.open = true;
|
||||
this.tid = tid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish the transaction
|
||||
*
|
||||
* <p>
|
||||
* If this is called before {@link #commit()}, then the transaction is aborted. This is called
|
||||
* automatically at the close of a try-with-resources block.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (open) {
|
||||
program.endTransaction(tid, false);
|
||||
open = false;
|
||||
}
|
||||
program.endTransaction(tid, commit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish the transaction, and commit
|
||||
*
|
||||
* This MUST be called in order to commit the transaction. The transaction is immediately
|
||||
* closed, and any further modifications to the database will likely result in an error.
|
||||
* <p>
|
||||
* This MUST be called in order to commit the transaction. The transaction is not committed
|
||||
* until the close of the try-with-resources block.
|
||||
*/
|
||||
public void commit() {
|
||||
if (open) {
|
||||
program.endTransaction(tid, true);
|
||||
open = false;
|
||||
}
|
||||
commit = true;
|
||||
}
|
||||
}
|
@ -27,7 +27,48 @@ import ghidra.program.model.mem.WrappedMemBuffer;
|
||||
*/
|
||||
public abstract class AbstractComplexDataType extends BuiltIn {
|
||||
|
||||
private final static long serialVersionUID = 1;
|
||||
protected static AbstractComplexDataType getDefaultComplexDataType(int size) {
|
||||
if (size == 8) {
|
||||
return Complex8DataType.dataType;
|
||||
}
|
||||
if (size == 16) {
|
||||
return Complex16DataType.dataType;
|
||||
}
|
||||
if (size == 32) {
|
||||
return Complex32DataType.dataType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DataType getComplexDataType(int size, DataTypeManager dtm) {
|
||||
if (size < 1) {
|
||||
return DefaultDataType.dataType;
|
||||
}
|
||||
if (size % 2 != 0) {
|
||||
return Undefined.getUndefinedDataType(size);
|
||||
}
|
||||
int floatSize = size / 2;
|
||||
if (dtm != null) {
|
||||
DataOrganization dataOrganization = dtm.getDataOrganization();
|
||||
if (dataOrganization != null) {
|
||||
if (floatSize == dataOrganization.getFloatSize()) {
|
||||
return FloatComplexDataType.dataType.clone(dtm);
|
||||
}
|
||||
if (floatSize == dataOrganization.getDoubleSize()) {
|
||||
return DoubleComplexDataType.dataType.clone(dtm);
|
||||
}
|
||||
if (floatSize == dataOrganization.getLongDoubleSize()) {
|
||||
return LongDoubleComplexDataType.dataType.clone(dtm);
|
||||
}
|
||||
}
|
||||
}
|
||||
DataType dt = getDefaultComplexDataType(size);
|
||||
if (dt == null) {
|
||||
return Undefined.getUndefinedDataType(size);
|
||||
}
|
||||
return dt;
|
||||
}
|
||||
|
||||
private final AbstractFloatDataType floatType;
|
||||
|
||||
public AbstractComplexDataType(String name, AbstractFloatDataType floats, DataTypeManager dtm) {
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
|
||||
public enum ArchiveType {
|
||||
//@formatter:off
|
||||
BUILT_IN,
|
||||
|
@ -20,22 +20,25 @@ import java.util.Comparator;
|
||||
/**
|
||||
* The structure interface.
|
||||
* <p>
|
||||
* NOTE: Structures containing only a flexible array will report a length of 1
|
||||
* which will result in improper code unit sizing since we are unable to support a
|
||||
* defined data of length 0.
|
||||
* NOTE: Structures containing only a flexible array will report a length of 1 which will result in
|
||||
* improper code unit sizing since we are unable to support a defined data of length 0.
|
||||
* <p>
|
||||
* NOTE: The use of zero-length bitfields within unaligned structures is discouraged since
|
||||
* they have no real affect and are easily misplaced. Their use should be reserved for
|
||||
* aligned/packed structures.
|
||||
* NOTE: The use of zero-length bitfields within unaligned structures is discouraged since they have
|
||||
* no real affect and are easily misplaced. Their use should be reserved for aligned/packed
|
||||
* structures.
|
||||
*/
|
||||
public interface Structure extends Composite {
|
||||
|
||||
@Override
|
||||
Structure clone(DataTypeManager dtm);
|
||||
|
||||
/**
|
||||
* Returns the component of this structure with the indicated ordinal.
|
||||
* If the specified ordinal equals {@link #getNumComponents()} the defined
|
||||
* flexible array component will be returned, otherwise an out of bounds
|
||||
* exception will be thrown. Use of {@link #getFlexibleArrayComponent()} is preferred
|
||||
* for obtaining this special trailing component.
|
||||
* Returns the component of this structure with the indicated ordinal. If the specified ordinal
|
||||
* equals {@link #getNumComponents()} the defined flexible array component will be returned,
|
||||
* otherwise an out of bounds exception will be thrown. Use of
|
||||
* {@link #getFlexibleArrayComponent()} is preferred for obtaining this special trailing
|
||||
* component.
|
||||
*
|
||||
* @param ordinal the component's ordinal (zero based).
|
||||
* @return the data type component.
|
||||
* @throws ArrayIndexOutOfBoundsException if the ordinal is out of bounds
|
||||
@ -44,259 +47,263 @@ public interface Structure extends Composite {
|
||||
public abstract DataTypeComponent getComponent(int ordinal);
|
||||
|
||||
/**
|
||||
* Gets the immediate child component that contains the byte
|
||||
* at the given offset. If the specified offset corresponds to
|
||||
* a bit-field,the first bit-field component containing the offset
|
||||
* will be returned.
|
||||
* Gets the immediate child component that contains the byte at the given offset. If the
|
||||
* specified offset corresponds to a bit-field,the first bit-field component containing the
|
||||
* offset will be returned.
|
||||
*
|
||||
* @param offset the byte offset into this data type
|
||||
* @return the immediate child component.
|
||||
*/
|
||||
public abstract DataTypeComponent getComponentAt(int offset);
|
||||
|
||||
/**
|
||||
* Returns the primitive Data Type that is at this offset. This is useful
|
||||
* for prototypes that have components that are made up of other components
|
||||
* If the specified offset corresponds to
|
||||
* a bit-field,the BitFieldDataType of the first bit-field component containing
|
||||
* the offset will be returned.
|
||||
* Returns the primitive Data Type that is at this offset. This is useful for prototypes that
|
||||
* have components that are made up of other components If the specified offset corresponds to a
|
||||
* bit-field,the BitFieldDataType of the first bit-field component containing the offset will be
|
||||
* returned.
|
||||
*
|
||||
* @param offset the byte offset into this data type.
|
||||
* @return the primitive data type at the offset.
|
||||
*/
|
||||
public abstract DataTypeComponent getDataTypeAt(int offset);
|
||||
|
||||
/**
|
||||
* Inserts a new bitfield at the specified ordinal position in this structure.
|
||||
* Within aligned structures the specified byteWidth and bitOffset will be
|
||||
* ignored since packing will occur at the specified ordinal position.
|
||||
* The resulting component length and bitfield details will reflect the use
|
||||
* of minimal storage sizing.
|
||||
* Inserts a new bitfield at the specified ordinal position in this structure. Within aligned
|
||||
* structures the specified byteWidth and bitOffset will be ignored since packing will occur at
|
||||
* the specified ordinal position. The resulting component length and bitfield details will
|
||||
* reflect the use of minimal storage sizing.
|
||||
* <p>
|
||||
* For unaligned structures, a component shift will only occur if the bitfield placement
|
||||
* conflicts with another component. If no conflict occurs, the bitfield will be placed
|
||||
* at the specified location consuming any DEFAULT components as needed. When a conflict
|
||||
* does occur a shift will be performed at the ordinal position based upon the specified
|
||||
* byteWidth. When located onto existing bitfields they will be packed together
|
||||
* provided they do not conflict, otherwise the conflict rule above applies.
|
||||
* conflicts with another component. If no conflict occurs, the bitfield will be placed at the
|
||||
* specified location consuming any DEFAULT components as needed. When a conflict does occur a
|
||||
* shift will be performed at the ordinal position based upon the specified byteWidth. When
|
||||
* located onto existing bitfields they will be packed together provided they do not conflict,
|
||||
* otherwise the conflict rule above applies.
|
||||
* <p>
|
||||
* Supported aligned packing starts with bit-0 (lsb) of the first byte for little-endian, and
|
||||
* with bit-7 (msb) of the first byte for big-endian. This is the default behavior for most
|
||||
* compilers. Insertion behavior may not work as expected if packing rules differ from this.
|
||||
*
|
||||
* @param ordinal the ordinal where the new datatype is to be inserted.
|
||||
* @param byteWidth the storage allocation unit width which contains the bitfield. Must be large
|
||||
* enough to contain the "effective bit size" and corresponding bitOffset. The actual
|
||||
* component size used will be recomputed during insertion.
|
||||
* @param bitOffset corresponds to the bitfield left-shift amount with the storage
|
||||
* unit when viewed as big-endian. The final offset may be reduced based upon
|
||||
* the minimal storage size determined during insertion.
|
||||
* @param bitOffset corresponds to the bitfield left-shift amount with the storage unit when
|
||||
* viewed as big-endian. The final offset may be reduced based upon the minimal
|
||||
* storage size determined during insertion.
|
||||
* @param baseDataType the bitfield base datatype (certain restrictions apply).
|
||||
* @param bitSize the declared bitfield size in bits. The effective bit size may be
|
||||
* adjusted based upon the specified baseDataType.
|
||||
* @param bitSize the declared bitfield size in bits. The effective bit size may be adjusted
|
||||
* based upon the specified baseDataType.
|
||||
* @param componentName the field name to associate with this component.
|
||||
* @param comment the comment to associate with this component.
|
||||
* @return the bitfield component created whose associated data type will
|
||||
* be BitFieldDataType.
|
||||
* @throws InvalidDataTypeException if the specified baseDataType is
|
||||
* not a valid base type for bitfields.
|
||||
* @throws ArrayIndexOutOfBoundsException if ordinal is less than 0 or greater than the
|
||||
* current number of components.
|
||||
* @return the bitfield component created whose associated data type will be BitFieldDataType.
|
||||
* @throws InvalidDataTypeException if the specified baseDataType is not a valid base type for
|
||||
* bitfields.
|
||||
* @throws ArrayIndexOutOfBoundsException if ordinal is less than 0 or greater than the current
|
||||
* number of components.
|
||||
*/
|
||||
public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset,
|
||||
DataType baseDataType, int bitSize, String componentName, String comment)
|
||||
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Inserts a new bitfield at the specified location in this composite.
|
||||
* This method is intended to be used with unaligned structures where
|
||||
* the bitfield will be precisely placed. Within an aligned structure the specified
|
||||
* byteOffset, byteWidth and bitOffset will be used to identify the appropriate ordinal
|
||||
* but may not be preserved. The component length will be computed
|
||||
* based upon the specified parameters and will be reduced from byteWidth to
|
||||
* its minimal size for the new component.
|
||||
* Inserts a new bitfield at the specified location in this composite. This method is intended
|
||||
* to be used with unaligned structures where the bitfield will be precisely placed. Within an
|
||||
* aligned structure the specified byteOffset, byteWidth and bitOffset will be used to identify
|
||||
* the appropriate ordinal but may not be preserved. The component length will be computed based
|
||||
* upon the specified parameters and will be reduced from byteWidth to its minimal size for the
|
||||
* new component.
|
||||
* <p>
|
||||
* For unaligned mode, a component shift will only occur if the bitfield placement
|
||||
* conflicts with another component. If no conflict occurs, the bitfield will be placed
|
||||
* at the specified location consuming any DEFAULT components as needed. When a conflict
|
||||
* does occur a shift will be performed at the point of conflict based upon the specified
|
||||
* byteWidth. When located onto existing bitfields they will be packed together
|
||||
* provided they do not conflict, otherwise the conflict rule above applies.
|
||||
* For unaligned mode, a component shift will only occur if the bitfield placement conflicts
|
||||
* with another component. If no conflict occurs, the bitfield will be placed at the specified
|
||||
* location consuming any DEFAULT components as needed. When a conflict does occur a shift will
|
||||
* be performed at the point of conflict based upon the specified byteWidth. When located onto
|
||||
* existing bitfields they will be packed together provided they do not conflict, otherwise the
|
||||
* conflict rule above applies.
|
||||
* <p>
|
||||
* Supported packing for little-endian fills lsb first, whereas big-endian fills msb first.
|
||||
* Insertion behavior may not work as expected if packing rules differ from this.
|
||||
* <p>
|
||||
* Zero length bitfields may be inserted although they have no real affect for
|
||||
* unaligned structures. Only the resulting byte offset within the structure
|
||||
* is of significance in determining its ordinal placement.
|
||||
*
|
||||
* Zero length bitfields may be inserted although they have no real affect for unaligned
|
||||
* structures. Only the resulting byte offset within the structure is of significance in
|
||||
* determining its ordinal placement.
|
||||
* <p>
|
||||
* @param byteOffset the first byte offset within this structure which corresponds to the
|
||||
* first byte of the specified storage unit identified by its byteWidth.
|
||||
* @param byteWidth the storage unit width which contains the bitfield. Must be large
|
||||
* enough to contain the specified bitSize and corresponding bitOffset. The actual
|
||||
* component size used will be recomputed during insertion.
|
||||
* @param bitOffset corresponds to the bitfield left-shift amount with the storage
|
||||
* unit when viewed as big-endian. The final offset may be reduced based upon
|
||||
* the minimal storage size determined during insertion.
|
||||
*
|
||||
* @param byteOffset the first byte offset within this structure which corresponds to the first
|
||||
* byte of the specified storage unit identified by its byteWidth.
|
||||
* @param byteWidth the storage unit width which contains the bitfield. Must be large enough to
|
||||
* contain the specified bitSize and corresponding bitOffset. The actual component
|
||||
* size used will be recomputed during insertion.
|
||||
* @param bitOffset corresponds to the bitfield left-shift amount with the storage unit when
|
||||
* viewed as big-endian. The final offset may be reduced based upon the minimal
|
||||
* storage size determined during insertion.
|
||||
* @param baseDataType the bitfield base datatype (certain restrictions apply).
|
||||
* @param componentName the field name to associate with this component.
|
||||
* @param bitSize the bitfield size in bits. A bitSize of 0 may be specified
|
||||
* although its name will be ignored.
|
||||
* @param bitSize the bitfield size in bits. A bitSize of 0 may be specified although its name
|
||||
* will be ignored.
|
||||
* @param comment the comment to associate with this component.
|
||||
* @return the componentDataType created whose associated data type will
|
||||
* be BitFieldDataType.
|
||||
* @throws InvalidDataTypeException if the specified data type is
|
||||
* not a valid base type for bitfields.
|
||||
* @return the componentDataType created whose associated data type will be BitFieldDataType.
|
||||
* @throws InvalidDataTypeException if the specified data type is not a valid base type for
|
||||
* bitfields.
|
||||
*/
|
||||
public DataTypeComponent insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset,
|
||||
DataType baseDataType, int bitSize, String componentName, String comment)
|
||||
throws InvalidDataTypeException;
|
||||
|
||||
/**
|
||||
* Inserts a new datatype at the specified offset into this structure.
|
||||
* Inserting a component will causing any conflicting component
|
||||
* to shift down to the extent necessary to avoid a conflict.
|
||||
* Inserts a new datatype at the specified offset into this structure. Inserting a component
|
||||
* will causing any conflicting component to shift down to the extent necessary to avoid a
|
||||
* conflict.
|
||||
*
|
||||
* @param offset the byte offset into the structure where the new datatype is to be inserted.
|
||||
* @param dataType the datatype to insert.
|
||||
* @param length the length to associate with the dataType.
|
||||
* For fixed length types a length <= 0 will use the length of the resolved dataType.
|
||||
* @param length the length to associate with the dataType. For fixed length types a length
|
||||
* <= 0 will use the length of the resolved dataType.
|
||||
* @return the componentDataType created.
|
||||
* @throws IllegalArgumentException if the specified data type is not
|
||||
* allowed to be inserted into this composite data type or an invalid length
|
||||
* is specified.
|
||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||
* to insert dt1 to dt2 since this would cause a cyclic dependency.
|
||||
* @throws IllegalArgumentException if the specified data type is not allowed to be inserted
|
||||
* into this composite data type or an invalid length is specified. For example,
|
||||
* suppose dt1 contains dt2. Therefore it is not valid to insert dt1 to dt2 since
|
||||
* this would cause a cyclic dependency.
|
||||
*/
|
||||
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length)
|
||||
throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Inserts a new datatype at the specified offset into this structure.
|
||||
* Inserting a component will causing any conflicting component
|
||||
* to shift down to the extent necessary to avoid a conflict.
|
||||
* Inserts a new datatype at the specified offset into this structure. Inserting a component
|
||||
* will causing any conflicting component to shift down to the extent necessary to avoid a
|
||||
* conflict.
|
||||
*
|
||||
* @param offset the byte offset into the structure where the new datatype is to be inserted.
|
||||
* @param dataType the datatype to insert.
|
||||
* @param length the length to associate with the dataType.
|
||||
* For fixed length types a length <= 0 will use the length of the resolved dataType.
|
||||
* @param length the length to associate with the dataType. For fixed length types a length
|
||||
* <= 0 will use the length of the resolved dataType.
|
||||
* @param name the field name to associate with this component.
|
||||
* @param comment the comment to associate with this component.
|
||||
* @return the componentDataType created.
|
||||
* @throws IllegalArgumentException if the specified data type is not
|
||||
* allowed to be inserted into this composite data type or an invalid length is specified.
|
||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||
* to insert dt1 to dt2 since this would cause a cyclic dependency.
|
||||
* @throws IllegalArgumentException if the specified data type is not allowed to be inserted
|
||||
* into this composite data type or an invalid length is specified. For example,
|
||||
* suppose dt1 contains dt2. Therefore it is not valid to insert dt1 to dt2 since
|
||||
* this would cause a cyclic dependency.
|
||||
*/
|
||||
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name,
|
||||
String comment) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Deletes the component containing the specified offset in this structure. If the offset
|
||||
* corresponds to a bit-field, all bit-fields whose base type group contains the offset will
|
||||
* be removed.
|
||||
* corresponds to a bit-field, all bit-fields whose base type group contains the offset will be
|
||||
* removed.
|
||||
*
|
||||
* @param offset the byte offset into the structure where the datatype is to be deleted.
|
||||
*/
|
||||
public void deleteAtOffset(int offset);
|
||||
|
||||
/**
|
||||
* Remove all components from this structure (including flex-array),
|
||||
* effectively setting the length to zero.
|
||||
* Remove all components from this structure (including flex-array), effectively setting the
|
||||
* length to zero.
|
||||
*/
|
||||
public void deleteAll();
|
||||
|
||||
/**
|
||||
* Clears the defined component at the given component index. Clearing a
|
||||
* component causes a defined component to be replaced with a number of
|
||||
* undefined dataTypes to offset the removal of the defined dataType.
|
||||
* Clears the defined component at the given component index. Clearing a component causes a
|
||||
* defined component to be replaced with a number of undefined dataTypes to offset the removal
|
||||
* of the defined dataType.
|
||||
*
|
||||
* @param index the index of the component to clear.
|
||||
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
|
||||
*/
|
||||
public void clearComponent(int index) throws ArrayIndexOutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Replaces the component at the given component index with a new component
|
||||
* of the indicated data type.
|
||||
* Replaces the component at the given component index with a new component of the indicated
|
||||
* data type.
|
||||
*
|
||||
* @param index the index where the datatype is to be replaced.
|
||||
* @param dataType the datatype to insert.
|
||||
* @param length the length of the dataType to insert.
|
||||
* For fixed length types a length <= 0 will use the length of the resolved dataType.
|
||||
* @param length the length of the dataType to insert. For fixed length types a length <= 0
|
||||
* will use the length of the resolved dataType.
|
||||
* @return the new componentDataType at the index.
|
||||
* @throws IllegalArgumentException if the specified data type is not
|
||||
* allowed to replace a component in this composite data type or an invalid
|
||||
* length is specified.
|
||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||
* to replace a dt2 component with dt1 since this would cause a cyclic
|
||||
* dependency. In addition, any attempt to replace an existing bit-field
|
||||
* component or specify a {@link BitFieldDataType} will produce this error.
|
||||
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
|
||||
* component in this composite data type or an invalid length is specified. For
|
||||
* example, suppose dt1 contains dt2. Therefore it is not valid to replace a dt2
|
||||
* component with dt1 since this would cause a cyclic dependency. In addition, any
|
||||
* attempt to replace an existing bit-field component or specify a
|
||||
* {@link BitFieldDataType} will produce this error.
|
||||
* @throws ArrayIndexOutOfBoundsException if component index is out of bounds
|
||||
*/
|
||||
public DataTypeComponent replace(int index, DataType dataType, int length)
|
||||
throws ArrayIndexOutOfBoundsException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Replaces the component at the given component index with a new component
|
||||
* of the indicated data type.
|
||||
* Replaces the component at the given component index with a new component of the indicated
|
||||
* data type.
|
||||
*
|
||||
* @param index the index where the datatype is to be replaced.
|
||||
* @param dataType the datatype to insert.
|
||||
* @param length the length to associate with the dataType.
|
||||
* For fixed length types a length <= 0 will use the length of the resolved dataType.
|
||||
* @param length the length to associate with the dataType. For fixed length types a length
|
||||
* <= 0 will use the length of the resolved dataType.
|
||||
* @param name the field name to associate with this component.
|
||||
* @param comment the comment to associate with this component.
|
||||
* @return the new componentDataType at the index.
|
||||
* @throws IllegalArgumentException if the specified data type is not
|
||||
* allowed to replace a component in this composite data type or an invalid
|
||||
* length is specified.
|
||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||
* to replace a dt2 component with dt1 since this would cause a cyclic
|
||||
* dependency. In addition, any attempt to replace an existing bit-field
|
||||
* component or specify a {@link BitFieldDataType} will produce this error.
|
||||
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
|
||||
* component in this composite data type or an invalid length is specified. For
|
||||
* example, suppose dt1 contains dt2. Therefore it is not valid to replace a dt2
|
||||
* component with dt1 since this would cause a cyclic dependency. In addition, any
|
||||
* attempt to replace an existing bit-field component or specify a
|
||||
* {@link BitFieldDataType} will produce this error.
|
||||
* @throws ArrayIndexOutOfBoundsException if component index is out of bounds
|
||||
*/
|
||||
public DataTypeComponent replace(int index, DataType dataType, int length, String name,
|
||||
String comment) throws ArrayIndexOutOfBoundsException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Replaces the component at the specified byte offset with a new component
|
||||
* of the indicated data type. If the offset corresponds to a bit-field, all bit-fields
|
||||
* at that offset will be removed and replaced by the specified component. Keep in mind
|
||||
* bit-field or any component removal must clear sufficient space for an unaligned
|
||||
* structure to complete the replacement.
|
||||
* @param offset the byte offset into the structure where the datatype is
|
||||
* to be replaced.
|
||||
* Replaces the component at the specified byte offset with a new component of the indicated
|
||||
* data type. If the offset corresponds to a bit-field, all bit-fields at that offset will be
|
||||
* removed and replaced by the specified component. Keep in mind bit-field or any component
|
||||
* removal must clear sufficient space for an unaligned structure to complete the replacement.
|
||||
*
|
||||
* @param offset the byte offset into the structure where the datatype is to be replaced.
|
||||
* @param dataType the datatype to insert.
|
||||
* @param length the length to associate with the dataType.
|
||||
* For fixed length types a length <= 0 will use the length of the resolved dataType.
|
||||
* @param length the length to associate with the dataType. For fixed length types a length
|
||||
* <= 0 will use the length of the resolved dataType.
|
||||
* @param name the field name to associate with this component.
|
||||
* @param comment the comment to associate with this component.
|
||||
* @return the new componentDataType at the index.
|
||||
* @throws IllegalArgumentException if the specified data type is not
|
||||
* allowed to replace a component in this composite data type or an invalid
|
||||
* length is specified.
|
||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||
* to replace a dt2 component with dt1 since this would cause a cyclic
|
||||
* dependency. In addition, any attempt to replace an existing bit-field
|
||||
* component or specify a {@link BitFieldDataType} will produce this error.
|
||||
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
|
||||
* component in this composite data type or an invalid length is specified. For
|
||||
* example, suppose dt1 contains dt2. Therefore it is not valid to replace a dt2
|
||||
* component with dt1 since this would cause a cyclic dependency. In addition, any
|
||||
* attempt to replace an existing bit-field component or specify a
|
||||
* {@link BitFieldDataType} will produce this error.
|
||||
*/
|
||||
public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, String name,
|
||||
String comment) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Determine if a trailing flexible array component has been defined.
|
||||
*
|
||||
* @return true if trailing flexible array component has been defined.
|
||||
*/
|
||||
public boolean hasFlexibleArrayComponent();
|
||||
|
||||
/**
|
||||
* Get the optional trailing flexible array component associated with this structure.
|
||||
* @return optional trailing flexible array component associated with this structure or null
|
||||
* if not present.
|
||||
*
|
||||
* @return optional trailing flexible array component associated with this structure or null if
|
||||
* not present.
|
||||
*/
|
||||
public DataTypeComponent getFlexibleArrayComponent();
|
||||
|
||||
/**
|
||||
* Set the optional trailing flexible array component associated with this structure.
|
||||
* @param flexType the flexible array dataType (example: for 'char[0]' the type 'char' should be specified)
|
||||
*
|
||||
* @param flexType the flexible array dataType (example: for 'char[0]' the type 'char' should be
|
||||
* specified)
|
||||
* @param name component field name or null for default name
|
||||
* @param comment component comment
|
||||
* @return updated flexible array component
|
||||
* @throws IllegalArgumentException if specified flexType is not permitted (e.g.,
|
||||
* self referencing or unsupported type)
|
||||
* @throws IllegalArgumentException if specified flexType is not permitted (e.g., self
|
||||
* referencing or unsupported type)
|
||||
*/
|
||||
public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name,
|
||||
String comment) throws IllegalArgumentException;
|
||||
@ -307,38 +314,40 @@ public interface Structure extends Composite {
|
||||
public void clearFlexibleArrayComponent();
|
||||
|
||||
/**
|
||||
* Increases the size of the structure by the given amount by adding undefined datatypes
|
||||
* at the end of the structure.
|
||||
* Increases the size of the structure by the given amount by adding undefined datatypes at the
|
||||
* end of the structure.
|
||||
*
|
||||
* @param amount the amount by which to grow the structure.
|
||||
* @throws IllegalArgumentException if amount < 1
|
||||
*/
|
||||
public void growStructure(int amount);
|
||||
|
||||
/**
|
||||
* Sets the current packing value (usually a power of 2). A value of NOT_PACKING should be passed
|
||||
* if this isn't a packed data type. Otherwise this value indicates a maximum alignment
|
||||
* Sets the current packing value (usually a power of 2). A value of NOT_PACKING should be
|
||||
* passed if this isn't a packed data type. Otherwise this value indicates a maximum alignment
|
||||
* for any component within this data type. Calling this method will cause the data type to
|
||||
* become an internally aligned data type.
|
||||
* (Same as {@link Composite#setPackingValue(int)})
|
||||
* @param maxAlignment the new packing value or 0 for NOT_PACKING.
|
||||
* A negative value will be treated the same as 0.
|
||||
* become an internally aligned data type. (Same as {@link Composite#setPackingValue(int)})
|
||||
*
|
||||
* @param maxAlignment the new packing value or 0 for NOT_PACKING. A negative value will be
|
||||
* treated the same as 0.
|
||||
*/
|
||||
public void pack(int maxAlignment);
|
||||
|
||||
/**
|
||||
* <code>BitOffsetComparator</code> provides ability to compare an normalized bit offset
|
||||
* (see {@link #getNormalizedBitfieldOffset(int, int, int, int, boolean)}) with a
|
||||
* <code>BitOffsetComparator</code> provides ability to compare an normalized bit offset (see
|
||||
* {@link #getNormalizedBitfieldOffset(int, int, int, int, boolean)}) with a
|
||||
* {@link DataTypeComponent} object. The offset will be considered equal (0) if the component
|
||||
* contains the offset. A normalized component bit numbering is used to establish the footprint
|
||||
* of each component with an ordinal-based ordering (assumes specific LE/BE allocation rules).
|
||||
* Bit offsets for this comparator number the first allocated bit of the structure as 0 and the
|
||||
* last allocated bit of the structure as (8 * structLength) - 1. For big-endian bitfields
|
||||
* the msb of the bitfield will be assigned the lower bit-number (assumes msb-allocated-first),
|
||||
* last allocated bit of the structure as (8 * structLength) - 1. For big-endian bitfields the
|
||||
* msb of the bitfield will be assigned the lower bit-number (assumes msb-allocated-first),
|
||||
* while little-endian will perform similar numbering assuming byte-swap and bit-reversal of the
|
||||
* storage unit (assumes lsb-allocated-first). Both cases result in a normalized view where
|
||||
* normalized bit-0 is allocated first.
|
||||
*
|
||||
* <pre>{@literal
|
||||
* <pre>
|
||||
* {@literal
|
||||
* Example:
|
||||
*
|
||||
* Big-Endian (normalized view):
|
||||
@ -351,7 +360,8 @@ public interface Structure extends Composite {
|
||||
* | . . . . . . 6 7 | 8 . . . . . . . |
|
||||
* |------------>| bit-offset (6, lsb position within storage unit)
|
||||
* |<--->| bit-size (3)
|
||||
* }</pre>
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public static class BitOffsetComparator implements Comparator<Object> {
|
||||
|
||||
@ -394,8 +404,8 @@ public interface Structure extends Composite {
|
||||
* Compute the normalized bit offset of a bitfield relative to the start of a structure.
|
||||
*
|
||||
* NOTE: This implementation currently relies only on endianess to dictate bit allocation
|
||||
* ordering. If future support is added for alternate bitfield packing, this implementation will
|
||||
* require modification.
|
||||
* ordering. If future support is added for alternate bitfield packing, this implementation
|
||||
* will require modification.
|
||||
*
|
||||
* @param byteOffset byte offset within structure of storage unit
|
||||
* @param storageSize storage unit size (i.e., component length)
|
||||
@ -430,9 +440,9 @@ public interface Structure extends Composite {
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>OffsetComparator</code> provides ability to compare an Integer offset
|
||||
* with a DataTypeComponent object. The offset will be consider equal (0) if
|
||||
* the component contains the offset.
|
||||
* <code>OffsetComparator</code> provides ability to compare an Integer offset with a
|
||||
* DataTypeComponent object. The offset will be consider equal (0) if the component contains the
|
||||
* offset.
|
||||
*/
|
||||
public static class OffsetComparator implements Comparator<Object> {
|
||||
|
||||
@ -455,9 +465,9 @@ public interface Structure extends Composite {
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>OrdinalComparator</code> provides ability to compare an Integer ordinal
|
||||
* with a DataTypeComponent object. The offset will be consider equal (0) if
|
||||
* the component corresponds to the specified ordinal.
|
||||
* <code>OrdinalComparator</code> provides ability to compare an Integer ordinal with a
|
||||
* DataTypeComponent object. The offset will be consider equal (0) if the component corresponds
|
||||
* to the specified ordinal.
|
||||
*/
|
||||
public static class OrdinalComparator implements Comparator<Object> {
|
||||
|
||||
|
@ -40,54 +40,52 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
private int alignment = -1;
|
||||
|
||||
/**
|
||||
* Construct a new structure with the given name and length.
|
||||
* The root category will be used.
|
||||
* Construct a new structure with the given name and length. The root category will be used.
|
||||
*
|
||||
* @param name the name of the new structure
|
||||
* @param length the initial size of the structure in bytes. If 0 is specified
|
||||
* the structure will report its length as 1 and {@link #isNotYetDefined()}
|
||||
* will return true.
|
||||
* @param length the initial size of the structure in bytes. If 0 is specified the structure
|
||||
* will report its length as 1 and {@link #isNotYetDefined()} will return true.
|
||||
*/
|
||||
public StructureDataType(String name, int length) {
|
||||
this(CategoryPath.ROOT, name, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new structure with the given name, length and datatype manager
|
||||
* which conveys data organization. The root category will be used.
|
||||
* Construct a new structure with the given name, length and datatype manager which conveys data
|
||||
* organization. The root category will be used.
|
||||
*
|
||||
* @param name the name of the new structure
|
||||
* @param length the initial size of the structure in bytes. If 0 is specified
|
||||
* the structure will report its length as 1 and {@link #isNotYetDefined()}
|
||||
* will return true.
|
||||
* @param dtm the data type manager associated with this data type. This can be null.
|
||||
* Also, the data type manager may not yet contain this actual data type.
|
||||
* @param length the initial size of the structure in bytes. If 0 is specified the structure
|
||||
* will report its length as 1 and {@link #isNotYetDefined()} will return true.
|
||||
* @param dtm the data type manager associated with this data type. This can be null. Also, the
|
||||
* data type manager may not yet contain this actual data type.
|
||||
*/
|
||||
public StructureDataType(String name, int length, DataTypeManager dtm) {
|
||||
this(CategoryPath.ROOT, name, length, dtm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new structure with the given name and length within the
|
||||
* specified categry path.
|
||||
* Construct a new structure with the given name and length within the specified categry path.
|
||||
*
|
||||
* @param path the category path indicating where this data type is located.
|
||||
* @param name the name of the new structure
|
||||
* @param length the initial size of the structure in bytes. If 0 is specified
|
||||
* the structure will report its length as 1 and {@link #isNotYetDefined()}
|
||||
* will return true.
|
||||
* @param length the initial size of the structure in bytes. If 0 is specified the structure
|
||||
* will report its length as 1 and {@link #isNotYetDefined()} will return true.
|
||||
*/
|
||||
public StructureDataType(CategoryPath path, String name, int length) {
|
||||
this(path, name, length, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new structure with the given name, length and datatype manager
|
||||
* within the specified categry path.
|
||||
* Construct a new structure with the given name, length and datatype manager within the
|
||||
* specified categry path.
|
||||
*
|
||||
* @param path the category path indicating where this data type is located.
|
||||
* @param name the name of the new structure
|
||||
* @param length the initial size of the structure in bytes. If 0 is specified
|
||||
* the structure will report its length as 1 and {@link #isNotYetDefined()}
|
||||
* will return true.
|
||||
* @param dtm the data type manager associated with this data type. This can be null.
|
||||
* Also, the data type manager may not yet contain this actual data type.
|
||||
* @param length the initial size of the structure in bytes. If 0 is specified the structure
|
||||
* will report its length as 1 and {@link #isNotYetDefined()} will return true.
|
||||
* @param dtm the data type manager associated with this data type. This can be null. Also, the
|
||||
* data type manager may not yet contain this actual data type.
|
||||
*/
|
||||
public StructureDataType(CategoryPath path, String name, int length, DataTypeManager dtm) {
|
||||
super(path, name, dtm);
|
||||
@ -102,18 +100,18 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
|
||||
/**
|
||||
* Construct a new structure with the given name and length
|
||||
*
|
||||
* @param path the category path indicating where this data type is located.
|
||||
* @param name the name of the new structure
|
||||
* @param length the initial size of the structure in bytes. If 0 is specified
|
||||
* the structure will report its length as 1 and {@link #isNotYetDefined()}
|
||||
* will return true.
|
||||
* @param length the initial size of the structure in bytes. If 0 is specified the structure
|
||||
* will report its length as 1 and {@link #isNotYetDefined()} will return true.
|
||||
* @param universalID the id for the data type
|
||||
* @param sourceArchive the source archive for this data type
|
||||
* @param lastChangeTime the last time this data type was changed
|
||||
* @param lastChangeTimeInSourceArchive the last time this data type was changed in
|
||||
* its source archive.
|
||||
* @param dtm the data type manager associated with this data type. This can be null.
|
||||
* Also, the data type manager may not yet contain this actual data type.
|
||||
* @param lastChangeTimeInSourceArchive the last time this data type was changed in its source
|
||||
* archive.
|
||||
* @param dtm the data type manager associated with this data type. This can be null. Also, the
|
||||
* data type manager may not yet contain this actual data type.
|
||||
*/
|
||||
public StructureDataType(CategoryPath path, String name, int length, UniversalID universalID,
|
||||
SourceArchive sourceArchive, long lastChangeTime, long lastChangeTimeInSourceArchive,
|
||||
@ -399,21 +397,21 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
/**
|
||||
* Add a new component to the end of this structure.
|
||||
* <p>
|
||||
* NOTE: This method differs from inserting to the end the structure for the unaligned
|
||||
* case in that this method will always grow the structure by the positive length
|
||||
* specified while the insert may limit its growth by the length of a smaller fixed-length
|
||||
* dataType.
|
||||
* NOTE: This method differs from inserting to the end the structure for the unaligned case in
|
||||
* that this method will always grow the structure by the positive length specified while the
|
||||
* insert may limit its growth by the length of a smaller fixed-length dataType.
|
||||
*
|
||||
* @param dataType component data type
|
||||
* @param length maximum component length or -1 to use length of fixed-length dataType
|
||||
* after applying structures data organization as determined by data type manager.
|
||||
* If dataType is Dynamic, a positive length must be specified.
|
||||
* @param isFlexibleArray if true length is ignored and the trailing flexible array will be
|
||||
* set based upon the specified fixed-length dataType;
|
||||
* @param length maximum component length or -1 to use length of fixed-length dataType after
|
||||
* applying structures data organization as determined by data type manager. If
|
||||
* dataType is Dynamic, a positive length must be specified.
|
||||
* @param isFlexibleArray if true length is ignored and the trailing flexible array will be set
|
||||
* based upon the specified fixed-length dataType;
|
||||
* @param componentName component name
|
||||
* @param comment componetn comment
|
||||
* @return newly added component
|
||||
* @throws IllegalArgumentException if the specified data type is not
|
||||
* allowed to be added to this composite data type or an invalid length is specified.
|
||||
* @throws IllegalArgumentException if the specified data type is not allowed to be added to
|
||||
* this composite data type or an invalid length is specified.
|
||||
*/
|
||||
private DataTypeComponent doAdd(DataType dataType, int length, boolean isFlexibleArray,
|
||||
String componentName, String comment) throws IllegalArgumentException {
|
||||
@ -687,10 +685,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup from specified ordinal to the first component which contains
|
||||
* the specified offset. For normal components the specified
|
||||
* ordinal will be returned, however for bit-fields the ordinal of the first
|
||||
* bit-field containing the specified offset will be returned.
|
||||
* Backup from specified ordinal to the first component which contains the specified offset. For
|
||||
* normal components the specified ordinal will be returned, however for bit-fields the ordinal
|
||||
* of the first bit-field containing the specified offset will be returned.
|
||||
*
|
||||
* @param ordinal component ordinal
|
||||
* @param offset offset within structure
|
||||
* @return index of first defined component containing specific offset.
|
||||
@ -712,10 +710,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance from specified ordinal to the last component which contains
|
||||
* the specified offset. For normal components the specified
|
||||
* ordinal will be returned, however for bit-fields the ordinal of the last
|
||||
* bit-field containing the specified offset will be returned.
|
||||
* Advance from specified ordinal to the last component which contains the specified offset. For
|
||||
* normal components the specified ordinal will be returned, however for bit-fields the ordinal
|
||||
* of the last bit-field containing the specified offset will be returned.
|
||||
*
|
||||
* @param ordinal component ordinal
|
||||
* @param offset offset within structure
|
||||
* @return index of last defined component containing specific offset.
|
||||
@ -892,9 +890,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
}
|
||||
|
||||
/**
|
||||
* Create copy of structure for target dtm (source archive information is discarded).
|
||||
* WARNING! copying unaligned structures which contain bitfields can produce
|
||||
* invalid results when switching endianess due to the differences in packing order.
|
||||
* Create copy of structure for target dtm (source archive information is discarded). WARNING!
|
||||
* copying unaligned structures which contain bitfields can produce invalid results when
|
||||
* switching endianess due to the differences in packing order.
|
||||
*
|
||||
* @param dtm target data type manager
|
||||
* @return cloned structure
|
||||
*/
|
||||
@ -907,14 +906,15 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
}
|
||||
|
||||
/**
|
||||
* Create cloned structure for target dtm preserving source archive information.
|
||||
* WARNING! cloning unaligned structures which contain bitfields can produce
|
||||
* invalid results when switching endianess due to the differences in packing order.
|
||||
* Create cloned structure for target dtm preserving source archive information. WARNING!
|
||||
* cloning unaligned structures which contain bitfields can produce invalid results when
|
||||
* switching endianess due to the differences in packing order.
|
||||
*
|
||||
* @param dtm target data type manager
|
||||
* @return cloned structure
|
||||
*/
|
||||
@Override
|
||||
public DataType clone(DataTypeManager dtm) {
|
||||
public StructureDataType clone(DataTypeManager dtm) {
|
||||
if (dataMgr == dtm) {
|
||||
return this;
|
||||
}
|
||||
@ -945,14 +945,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the internal components of this structure with components of the
|
||||
* given structure.
|
||||
* Replaces the internal components of this structure with components of the given structure.
|
||||
*
|
||||
* @param dataType the structure to get the component information from.
|
||||
* @throws IllegalArgumentException if any of the component data types
|
||||
* are not allowed to replace a component in this composite data type.
|
||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||
* to replace a dt2 component with dt1 since this would cause a cyclic
|
||||
* dependency.
|
||||
* @throws IllegalArgumentException if any of the component data types are not allowed to
|
||||
* replace a component in this composite data type. For example, suppose dt1
|
||||
* contains dt2. Therefore it is not valid to replace a dt2 component with dt1 since
|
||||
* this would cause a cyclic dependency.
|
||||
*/
|
||||
@Override
|
||||
public void replaceWith(DataType dataType) {
|
||||
@ -1248,20 +1247,20 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the indicated component with a new component containing the
|
||||
* specified data type.
|
||||
* Replace the indicated component with a new component containing the specified data type.
|
||||
*
|
||||
* @param origDtc the original data type component in this structure.
|
||||
* @param dataType the data type of the new component
|
||||
* @param length the length of the new component
|
||||
* @param componentName the field name of the new component
|
||||
* @param comment the comment for the new component
|
||||
* @return the new component or null if the new component couldn't fit.
|
||||
* @throws IllegalArgumentException if the specified data type is not
|
||||
* allowed to replace a component in this composite data type.
|
||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||
* to replace a dt2 component with dt1 since this would cause a cyclic
|
||||
* dependency. In addition, any attempt to replace an existing bit-field
|
||||
* component or specify a {@link BitFieldDatatype} will produce this error.
|
||||
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
|
||||
* component in this composite data type. For example, suppose dt1 contains dt2.
|
||||
* Therefore it is not valid to replace a dt2 component with dt1 since this would
|
||||
* cause a cyclic dependency. In addition, any attempt to replace an existing
|
||||
* bit-field component or specify a {@link BitFieldDatatype} will produce this
|
||||
* error.
|
||||
*/
|
||||
private DataTypeComponent replaceComponent(DataTypeComponentImpl origDtc, DataType dataType,
|
||||
int length, String componentName, String comment) {
|
||||
@ -1321,9 +1320,9 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of Undefined bytes beginning at the indicated component
|
||||
* index. Undefined bytes that have a field name or comment specified are
|
||||
* also included.
|
||||
* Gets the number of Undefined bytes beginning at the indicated component index. Undefined
|
||||
* bytes that have a field name or comment specified are also included.
|
||||
*
|
||||
* @param index the component index to begin checking at.
|
||||
* @return the number of contiguous undefined bytes
|
||||
*/
|
||||
@ -1385,11 +1384,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
|
||||
/**
|
||||
* Adjust the alignment, packing and padding of components within this structure based upon the
|
||||
* current alignment and packing attributes for this structure. This method should be
|
||||
* called to fix up the layout of the internal components of the structure
|
||||
* after other code has changed the attributes of the structure.
|
||||
* <BR>When switching between internally aligned and unaligned this method corrects the
|
||||
* component ordinal numbering also.
|
||||
* current alignment and packing attributes for this structure. This method should be called to
|
||||
* fix up the layout of the internal components of the structure after other code has changed
|
||||
* the attributes of the structure. <BR>
|
||||
* When switching between internally aligned and unaligned this method corrects the component
|
||||
* ordinal numbering also.
|
||||
*
|
||||
* @return true if the structure was changed by this method.
|
||||
*/
|
||||
protected boolean adjustComponents() {
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -20,9 +19,9 @@ import java.util.Iterator;
|
||||
|
||||
public class ReferenceIteratorAdapter implements ReferenceIterator {
|
||||
|
||||
private final Iterator<Reference> iterator;
|
||||
private final Iterator<? extends Reference> iterator;
|
||||
|
||||
public ReferenceIteratorAdapter(Iterator<Reference> iterator) {
|
||||
public ReferenceIteratorAdapter(Iterator<? extends Reference> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -20,9 +19,9 @@ import java.util.Iterator;
|
||||
|
||||
public class SymbolIteratorAdapter implements SymbolIterator {
|
||||
|
||||
private final Iterator<Symbol> iterator;
|
||||
private final Iterator<? extends Symbol> iterator;
|
||||
|
||||
public SymbolIteratorAdapter(Iterator<Symbol> iterator) {
|
||||
public SymbolIteratorAdapter(Iterator<? extends Symbol> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
|
@ -15,23 +15,26 @@
|
||||
*/
|
||||
package ghidra.program.util;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* <CODE>ProgramLocation</CODE> provides information about a location in a
|
||||
* program in the most generic way.
|
||||
* <CODE>ProgramLocation</CODE> provides information about a location in a program in the most
|
||||
* generic way.
|
||||
*
|
||||
* ProgramLocations refer to a specific location in a program and can be specified down
|
||||
* to an address, a field at that address, and within that field, a row, col, and character
|
||||
* offset. The field is not recorded directly, but by the subclass of the ProgramLocation.
|
||||
* The "cursor position" within a field is specified by three variables: row, col, and character
|
||||
* offset. The row is literally the row (line #) the cursor is on within the field, the
|
||||
* column represents the display item on that row (For example, in the bytes field
|
||||
* the column will represent which "byte" the cursor is on. Most fields only have one
|
||||
* column item per row.) And finally, the character offset
|
||||
* <p>
|
||||
* ProgramLocations refer to a specific location in a program and can be specified down to an
|
||||
* address, a field at that address, and within that field, a row, col, and character offset. The
|
||||
* field is not recorded directly, but by the subclass of the ProgramLocation. The "cursor position"
|
||||
* within a field is specified by three variables: row, col, and character offset. The row is
|
||||
* literally the row (line #) the cursor is on within the field, the column represents the display
|
||||
* item on that row (For example, in the bytes field the column will represent which "byte" the
|
||||
* cursor is on. Most fields only have one column item per row.) And finally, the character offset
|
||||
* is the character position within the display item specified by the row and column. Simple fields
|
||||
* like the address field and Mnemonic field will always have a row and column of 0.
|
||||
*/
|
||||
@ -48,19 +51,19 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
|
||||
/**
|
||||
* Construct a new ProgramLocation.
|
||||
* <br>Note: A NullPointerException will be logged if addr is null.
|
||||
*
|
||||
* @param program the program of the location
|
||||
* @param addr address of the location; cannot be null; This could be a
|
||||
* code unit minimum address where the byteAddr is within the code unit.
|
||||
* @param addr address of the location; cannot be null; This could be a code unit minimum
|
||||
* address where the byteAddr is within the code unit.
|
||||
* @param byteAddr address of the location; cannot be null
|
||||
* @param componentPath array of indexes for each nested data component;
|
||||
* the data index is the data component's index within its parent; may be null
|
||||
* @param refAddr the "referred to" address if the location is
|
||||
* over a reference; may be null
|
||||
* @param componentPath array of indexes for each nested data component; the data index is the
|
||||
* data component's index within its parent; may be null
|
||||
* @param refAddr the "referred to" address if the location is over a reference; may be null
|
||||
* @param row the row within the field.
|
||||
* @param col - the display item index on the given row. (Note most fields only have one display item per row)
|
||||
* @param charOffset - the character offset within the display item.
|
||||
* @param col the display item index on the given row. (Note most fields only have one display
|
||||
* item per row)
|
||||
* @param charOffset the character offset within the display item.
|
||||
* @throws NullPointerException if {@code addr} or {@code program} is null
|
||||
*/
|
||||
public ProgramLocation(Program program, Address addr, Address byteAddr, int[] componentPath,
|
||||
Address refAddr, int row, int col, int charOffset) {
|
||||
@ -89,20 +92,21 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ProgramLocation for the given address. The address will be adjusted
|
||||
* to the beginning of the code unit containing that address(if it exists). The original
|
||||
* address can be retrieved using the "getByteAddress()" method.
|
||||
* <br>Note: A NullPointerException will be logged if addr is null.
|
||||
* @param program the program associated with this program location (also
|
||||
* used to obtain a code-unit-aligned address)
|
||||
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
||||
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
||||
* original address can be retrieved using the {@link #getByteAddress()}" method.
|
||||
*
|
||||
* @param program the program associated with this program location (also used to obtain a
|
||||
* code-unit-aligned address)
|
||||
* @param addr address of the location; cannot be null
|
||||
* @param componentPath array of indexes for each nested data component;
|
||||
* the index is the data component's index within its parent; may be null
|
||||
* @param refAddr the "referred to" address if the location is
|
||||
* over a reference; may be null
|
||||
* @param componentPath array of indexes for each nested data component; the index is the data
|
||||
* component's index within its parent; may be null
|
||||
* @param refAddr the "referred to" address if the location is over a reference; may be null
|
||||
* @param row the row within the field.
|
||||
* @param col - the display item index on the given row. (Note most fields only have one display item per row)
|
||||
* @param charOffset - the character offset within the display item.
|
||||
* @param col the display item index on the given row. (Note most fields only have one display
|
||||
* item per row)
|
||||
* @param charOffset the character offset within the display item.
|
||||
* @throws NullPointerException if {@code addr} or {@code program} is null
|
||||
*/
|
||||
public ProgramLocation(Program program, Address addr, int[] componentPath, Address refAddr,
|
||||
int row, int col, int charOffset) {
|
||||
@ -111,41 +115,47 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ProgramLocation for the given address. The address will be adjusted
|
||||
* to the beginning of the code unit containing that address(if it exists). The original
|
||||
* address can be retrieved using the "getByteAddress()" method.
|
||||
* @param program the program associated with this program location (also
|
||||
* used to obtain a code-unit-aligned address)
|
||||
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
||||
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
||||
* original address can be retrieved using the {@link #getByteAddress()} method.
|
||||
*
|
||||
* @param program the program associated with this program location (also used to obtain a
|
||||
* code-unit-aligned address)
|
||||
* @param addr address for the location
|
||||
* @throws NullPointerException if {@code addr} or {@code program} is null
|
||||
*/
|
||||
public ProgramLocation(Program program, Address addr) {
|
||||
this(program, getCodeUnitAddress(program, addr), addr, null, null, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ProgramLocation for the given address. The address will be adjusted
|
||||
* to the beginning of the code unit containing that address(if it exists). The original
|
||||
* address can be retrieved using the "getByteAddress()" method.
|
||||
* @param program the program associated with this program location (also
|
||||
* used to obtain a code-unit-aligned address)
|
||||
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
||||
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
||||
* original address can be retrieved using the {@link #getByteAddress()} method.
|
||||
*
|
||||
* @param program the program associated with this program location (also used to obtain a
|
||||
* code-unit-aligned address)
|
||||
* @param addr address for the location
|
||||
* @param row the row within the field.
|
||||
* @param col - the display item index on the given row. (Note most fields only have one display item per row)
|
||||
* @param charOffset - the character offset within the display item.
|
||||
* @param col the display item index on the given row. (Note most fields only have one display
|
||||
* item per row)
|
||||
* @param charOffset the character offset within the display item.
|
||||
* @throws NullPointerException if {@code addr} or {@code program} is null
|
||||
*/
|
||||
public ProgramLocation(Program program, Address addr, int row, int col, int charOffset) {
|
||||
this(program, getCodeUnitAddress(program, addr), addr, null, null, row, col, charOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ProgramLocation for the given address. The address will be adjusted
|
||||
* to the beginning of the code unit containing that address(if it exists). The original
|
||||
* address can be retrieved using the "getByteAddress()" method.
|
||||
* @param program the program associated with this program location (also
|
||||
* used to obtain a code-unit-aligned address)
|
||||
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
||||
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
||||
* original address can be retrieved using the {@link #getByteAddress()} method.
|
||||
*
|
||||
* @param program the program associated with this program location (also used to obtain a
|
||||
* code-unit-aligned address)
|
||||
* @param addr address for the location
|
||||
* @param refAddr the "referred to" address if the location is over a
|
||||
* reference
|
||||
* @param refAddr the "referred to" address if the location is over a reference
|
||||
* @throws NullPointerException if {@code addr} or {@code program} is null
|
||||
*/
|
||||
public ProgramLocation(Program program, Address addr, Address refAddr) {
|
||||
this(program, getCodeUnitAddress(program, addr), addr, null, refAddr, 0, 0, 0);
|
||||
@ -158,15 +168,15 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the componentPath for the codeUnit. Null will be returned if the
|
||||
* object is an Instruction or a top-level Data object.
|
||||
* Returns the componentPath for the {@link CodeUnit code unit}. Null will be returned if the
|
||||
* object is an {@link Instruction} or a top-level {@link Data} object.
|
||||
*/
|
||||
public int[] getComponentPath() {
|
||||
return componentPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns program associated with location or null if not specified.
|
||||
* Returns the program associated with this location.
|
||||
*/
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
@ -174,9 +184,11 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
|
||||
/**
|
||||
* Returns the address associated with this location.
|
||||
* <br>Note: this may not be the same as the byte address. For example, in
|
||||
* a code unit location this may be the minimum address of the code unit
|
||||
* that contains the byte address.
|
||||
*
|
||||
* <p>
|
||||
* Note: this may not be the same as the byte address. For example, in a {@link CodeUnit code
|
||||
* unit} location this may be the minimum address of the code unit that contains the byte
|
||||
* address.
|
||||
*/
|
||||
public Address getAddress() {
|
||||
return addr;
|
||||
@ -190,8 +202,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "referred to" address if the location is over an
|
||||
* address in some field.
|
||||
* Returns the "referred to" address if the location is over an address in some field.
|
||||
*/
|
||||
public Address getRefAddress() {
|
||||
return refAddr;
|
||||
@ -199,6 +210,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
|
||||
/**
|
||||
* Save this program location to the given save state object.
|
||||
*
|
||||
* @param obj the save state object for saving the location
|
||||
*/
|
||||
public void saveState(SaveState obj) {
|
||||
@ -218,8 +230,8 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore this program location using the given program
|
||||
* and save state object.
|
||||
* Restore this program location using the given program and save state object.
|
||||
*
|
||||
* @param program1 program to restore from
|
||||
* @param obj the save state to restore from
|
||||
*/
|
||||
@ -240,6 +252,13 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the program location for the given program and save state object.
|
||||
*
|
||||
* @param program the program for the location
|
||||
* @param saveState the state to restore
|
||||
* @return the restored program location
|
||||
*/
|
||||
public static ProgramLocation getLocation(Program program, SaveState saveState) {
|
||||
String className = saveState.getString("_CLASSNAME", null);
|
||||
if (className == null) {
|
||||
@ -248,7 +267,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
|
||||
try {
|
||||
Class<?> locClass = Class.forName(className);
|
||||
ProgramLocation loc = (ProgramLocation) locClass.newInstance();
|
||||
ProgramLocation loc = (ProgramLocation) locClass.getConstructor().newInstance();
|
||||
loc.restoreState(program, saveState);
|
||||
if (loc.getAddress() != null) {
|
||||
return loc;
|
||||
@ -260,23 +279,20 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
catch (ClassNotFoundException e) {
|
||||
// not sure why we are ignoring this--if you know, then please let everyone else know
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
Msg.showError(ProgramLocation.class, null, "Programming Error",
|
||||
"Class " + className + " must have default constructor!", e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) {
|
||||
Msg.showError(ProgramLocation.class, null, "Programming Error",
|
||||
"Class " + className + " must have public default constructor!", e);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
Msg.showError(ProgramLocation.class, null, "Programming Error",
|
||||
"Class " + className + " default constructor threw an exception!", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (addr == null) {
|
||||
return 0;
|
||||
}
|
||||
return addr.hashCode();
|
||||
return Objects.hash(program, addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -323,6 +339,9 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
return false;
|
||||
}
|
||||
ProgramLocation other = (ProgramLocation) obj;
|
||||
if (program != other.program) {
|
||||
return false;
|
||||
}
|
||||
if (compareAddr(addr, other.addr) != 0) {
|
||||
return false;
|
||||
}
|
||||
@ -343,7 +362,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
if (other == this) {
|
||||
return 0;
|
||||
}
|
||||
int result = ProgramLocationComparator.instance.compare(this, other);
|
||||
int result = ProgramLocationComparator.INSTANCE.compare(this, other);
|
||||
if (result == 0) {
|
||||
result = row - other.row;
|
||||
if (result == 0) {
|
||||
@ -419,6 +438,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
|
||||
/**
|
||||
* Returns true if this location represents a valid location in the given program
|
||||
*
|
||||
* @param testProgram the program to test if this location is valid.
|
||||
* @return true if this location represents a valid location in the given program
|
||||
*/
|
||||
@ -428,6 +448,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
|
||||
/**
|
||||
* Returns the row within the program location.
|
||||
*
|
||||
* @return the row within the program location.
|
||||
*/
|
||||
public int getRow() {
|
||||
@ -436,6 +457,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||
|
||||
/**
|
||||
* Returns the character offset in the display item at the (row,col)
|
||||
*
|
||||
* @return the character offset in the display item at the (row,col)
|
||||
*/
|
||||
public int getCharOffset() {
|
||||
|
@ -15,20 +15,33 @@
|
||||
*/
|
||||
package ghidra.program.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class ProgramLocationComparator {
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* A comparator for the common fields of {@link ProgramLocation}
|
||||
*
|
||||
* <p>
|
||||
* This comparator only compares the program, address, and class of the program location. To compare
|
||||
* at greater granularity, invoke the {@link ProgramLocation#compareTo(ProgramLocation)} method, or
|
||||
* use the natural ordering. Each particular type of location uses this comparator, and then
|
||||
* compares the more detailed fields, if necessary. If this comparator indicates equality, then the
|
||||
* two locations are definitely of the same class.
|
||||
*/
|
||||
public class ProgramLocationComparator implements Comparator<ProgramLocation> {
|
||||
/** The singleton instance */
|
||||
public static final ProgramLocationComparator INSTANCE = new ProgramLocationComparator();
|
||||
private static final Class<?>[] PROGRAM_LOCATION_CLASSES = {
|
||||
|
||||
DividerLocation.class, ProgramLocation.class, PlateFieldLocation.class, FunctionLocation.class,
|
||||
FunctionRepeatableCommentFieldLocation.class, FunctionSignatureFieldLocation.class,
|
||||
FunctionSignatureSourceFieldLocation.class, FunctionCallFixupFieldLocation.class,
|
||||
FunctionReturnTypeFieldLocation.class, FunctionCallingConventionFieldLocation.class,
|
||||
FunctionNameFieldLocation.class, FunctionStartParametersFieldLocation.class,
|
||||
FunctionParameterFieldLocation.class, FunctionParameterNameFieldLocation.class,
|
||||
FunctionEndParametersFieldLocation.class, VariableLocation.class,
|
||||
VariableTypeFieldLocation.class, VariableNameFieldLocation.class,
|
||||
DividerLocation.class, ProgramLocation.class, PlateFieldLocation.class,
|
||||
FunctionLocation.class, FunctionRepeatableCommentFieldLocation.class,
|
||||
FunctionSignatureFieldLocation.class, FunctionSignatureSourceFieldLocation.class,
|
||||
FunctionCallFixupFieldLocation.class, FunctionReturnTypeFieldLocation.class,
|
||||
FunctionCallingConventionFieldLocation.class, FunctionNameFieldLocation.class,
|
||||
FunctionStartParametersFieldLocation.class, FunctionParameterFieldLocation.class,
|
||||
FunctionParameterNameFieldLocation.class, FunctionEndParametersFieldLocation.class,
|
||||
VariableLocation.class, VariableTypeFieldLocation.class, VariableNameFieldLocation.class,
|
||||
VariableLocFieldLocation.class, VariableXRefFieldLocation.class,
|
||||
VariableCommentFieldLocation.class,
|
||||
|
||||
@ -42,20 +55,37 @@ public class ProgramLocationComparator {
|
||||
PostCommentFieldLocation.class,
|
||||
|
||||
SpaceFieldLocation.class, SpacerFieldLocation.class, SubDataFieldLocation.class,
|
||||
RegisterFieldLocation.class, };
|
||||
public static final ProgramLocationComparator instance = new ProgramLocationComparator();
|
||||
RegisterFieldLocation.class,
|
||||
|
||||
};
|
||||
|
||||
private Map<Class<?>, Integer> priorityMap;
|
||||
|
||||
private ProgramLocationComparator() {
|
||||
priorityMap = new HashMap<Class<?>, Integer>();
|
||||
priorityMap = new HashMap<>();
|
||||
for (int ordinal = 0; ordinal < PROGRAM_LOCATION_CLASSES.length; ordinal++) {
|
||||
priorityMap.put(PROGRAM_LOCATION_CLASSES[ordinal], ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ProgramLocation loc1, ProgramLocation loc2) {
|
||||
int result = loc1.getAddress().compareTo(loc2.getAddress());
|
||||
if (result == 0) {
|
||||
int result;
|
||||
// Try to make a sensible comparison of programs before just using identity hashes
|
||||
Program program1 = loc1.getProgram();
|
||||
Program program2 = loc2.getProgram();
|
||||
result = program1.getName().compareTo(program2.getName());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = Integer.compare(program1.hashCode(), program2.hashCode());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = loc1.getAddress().compareTo(loc2.getAddress());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
Class<?> class1 = loc1.getClass();
|
||||
Class<?> class2 = loc2.getClass();
|
||||
if (class1 == class2) {
|
||||
@ -72,9 +102,11 @@ public class ProgramLocationComparator {
|
||||
if (ordinal2 == null) {
|
||||
return -1;
|
||||
}
|
||||
result = ordinal1.intValue() - ordinal2.intValue();
|
||||
}
|
||||
result = Integer.compare(ordinal1.intValue(), ordinal2.intValue());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,17 +16,19 @@ data/languages/toy.ldefs||GHIDRA||||END|
|
||||
data/languages/toy.pspec||GHIDRA||reviewed||END|
|
||||
data/languages/toy.sinc||GHIDRA||||END|
|
||||
data/languages/toy64.cspec||GHIDRA||||END|
|
||||
data/languages/toy64_be.slaspec||GHIDRA||reviewed||END|
|
||||
data/languages/toy64_le.slaspec||GHIDRA||reviewed||END|
|
||||
data/languages/toy64_be.slaspec||GHIDRA||||END|
|
||||
data/languages/toy64_be_harvard.slaspec||GHIDRA||||END|
|
||||
data/languages/toy64_le.slaspec||GHIDRA||||END|
|
||||
data/languages/toyInstructions.sinc||GHIDRA||||END|
|
||||
data/languages/toyPosStack.cspec||GHIDRA||||END|
|
||||
data/languages/toy_be.slaspec||GHIDRA||||END|
|
||||
data/languages/toy_be_posStack.slaspec||GHIDRA||||END|
|
||||
data/languages/toy_builder.sinc||GHIDRA||||END|
|
||||
data/languages/toy_builder_be.slaspec||GHIDRA||||END|
|
||||
data/languages/toy_builder_be_align2.slaspec||GHIDRA||reviewed||END|
|
||||
data/languages/toy_builder_be_align2.slaspec||GHIDRA||||END|
|
||||
data/languages/toy_builder_le.slaspec||GHIDRA||||END|
|
||||
data/languages/toy_builder_le_align2.slaspec||GHIDRA||reviewed||END|
|
||||
data/languages/toy_builder_le_align2.slaspec||GHIDRA||||END|
|
||||
data/languages/toy_harvard.pspec||GHIDRA||||END|
|
||||
data/languages/toy_le.slaspec||GHIDRA||||END|
|
||||
data/languages/toy_wsz_be.slaspec||GHIDRA||reviewed||END|
|
||||
data/languages/toy_wsz_le.slaspec||GHIDRA||reviewed||END|
|
||||
data/languages/toy_wsz_be.slaspec||GHIDRA||||END|
|
||||
data/languages/toy_wsz_le.slaspec||GHIDRA||||END|
|
||||
|
@ -67,6 +67,17 @@
|
||||
<description>Toy (test) processor 64-bit big-endian</description>
|
||||
<compiler name="default" spec="toy64.cspec" id="default"/>
|
||||
</language>
|
||||
<language processor="Toy"
|
||||
endian="big"
|
||||
size="64"
|
||||
variant="harvard"
|
||||
version="1.0"
|
||||
slafile="toy64_be_harvard.sla"
|
||||
processorspec="toy_harvard.pspec"
|
||||
id="Toy:BE:64:harvard">
|
||||
<description>Toy (test) processor 64-bit big-endian Harvard</description>
|
||||
<compiler name="default" spec="toy64.cspec" id="default"/>
|
||||
</language>
|
||||
<language processor="Toy"
|
||||
endian="little"
|
||||
size="64"
|
||||
|
@ -1,5 +1,8 @@
|
||||
# Main slaspec must define endianess and alignment
|
||||
|
||||
define endian=$(ENDIAN);
|
||||
|
||||
|
||||
@ifndef WORDSIZE
|
||||
@define WORDSIZE "1"
|
||||
@endif
|
||||
@ -23,3 +26,22 @@ define register offset=0x1100 size=1 [
|
||||
C Z N V
|
||||
];
|
||||
|
||||
|
||||
@if SIZE == "4"
|
||||
@define HALFSIZE "2"
|
||||
@endif
|
||||
@if SIZE == "8"
|
||||
@define HALFSIZE "4"
|
||||
@endif
|
||||
|
||||
@if ENDIAN == "little"
|
||||
define register offset=0x1000 size=$(HALFSIZE) [
|
||||
r0l r0h r1l r1h r2l r2h r3l r3h r4l r4h r5l r5h r6l r6h r7l r7h
|
||||
r8l r8h r9l r9h r10l r10h r11l r1lh r12l r12h spl sph lrl lrh pcl pch
|
||||
];
|
||||
@else # ENDIAN == "big"
|
||||
define register offset=0x1000 size=$(HALFSIZE) [
|
||||
r0h r0l r1h r1l r2h r2l r3h r3l r4h r4l r5h r5l r6h r6l r7h r7l
|
||||
r8h r8l r9h r9l r10h r10l r11h r1ll r12h r12l sph spl lrh lrl pch pcl
|
||||
];
|
||||
#endif # ENDIAN
|
||||
|
@ -1,4 +1,4 @@
|
||||
define endian=big;
|
||||
@define ENDIAN "big"
|
||||
|
||||
@define SIZE "8"
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
@define ENDIAN "big"
|
||||
|
||||
@define SIZE "8"
|
||||
|
||||
@define INSTR_PHASE "" # not used by basic toy language
|
||||
|
||||
@include "toy.sinc"
|
||||
|
||||
define space data type=ram_space size=$(SIZE) wordsize=$(WORDSIZE);
|
||||
|
||||
@include "toyInstructions.sinc"
|
@ -1,4 +1,4 @@
|
||||
define endian=little;
|
||||
@define ENDIAN "little"
|
||||
|
||||
@define SIZE "8"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
define endian=big;
|
||||
@define ENDIAN "big"
|
||||
|
||||
@define SIZE "4"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
define endian=big;
|
||||
@define ENDIAN "big"
|
||||
|
||||
@define SIZE "4"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
define endian=big;
|
||||
@define ENDIAN "big"
|
||||
|
||||
@define SIZE "4"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
define endian=big;
|
||||
@define ENDIAN "big"
|
||||
define alignment=2;
|
||||
|
||||
@define SIZE "4"
|
||||
|
@ -1,4 +1,4 @@
|
||||
define endian=little;
|
||||
@define ENDIAN "little"
|
||||
|
||||
@define SIZE "4"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
define endian=little;
|
||||
@define ENDIAN "little"
|
||||
define alignment=2;
|
||||
|
||||
@define SIZE "4"
|
||||
|
6
Ghidra/Processors/Toy/data/languages/toy_harvard.pspec
Normal file
6
Ghidra/Processors/Toy/data/languages/toy_harvard.pspec
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<processor_spec>
|
||||
<programcounter register="pc"/>
|
||||
<data_space space="data"/>
|
||||
</processor_spec>
|
@ -1,4 +1,4 @@
|
||||
define endian=little;
|
||||
@define ENDIAN "little"
|
||||
|
||||
@define SIZE "4"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
define endian=big;
|
||||
@define ENDIAN "big"
|
||||
define alignment=2;
|
||||
|
||||
@define SIZE "4"
|
||||
|
@ -1,4 +1,4 @@
|
||||
define endian=little;
|
||||
@define ENDIAN "little"
|
||||
define alignment=2;
|
||||
|
||||
@define SIZE "4"
|
||||
|
Loading…
Reference in New Issue
Block a user