Merge remote-tracking branch 'origin/Ghidra_11.2'

This commit is contained in:
ghidra1 2024-09-25 13:44:40 -04:00
commit 4e1532be14
19 changed files with 341 additions and 270 deletions

View file

@ -17,6 +17,8 @@ package ghidra.app.plugin.core.compositeeditor;
import java.util.*;
import javax.help.UnsupportedOperationException;
import docking.widgets.OptionDialog;
import docking.widgets.fieldpanel.support.*;
import ghidra.program.database.DatabaseObject;
@ -586,7 +588,7 @@ public abstract class CompEditorModel extends CompositeEditorModel {
@Override
public DataTypeComponent add(int rowIndex, DataType dt) throws UsrException {
String descr = rowIndex < getNumComponents() ? "Replace Component" : "Add Component";
return viewDTM.withTransaction(descr, () -> {
DataTypeComponent dtc = viewDTM.withTransaction(descr, () -> {
DataType resolvedDt = viewDTM.resolve(dt, DataTypeConflictHandler.DEFAULT_HANDLER);
try {
DataTypeInstance dti = getDropDataType(rowIndex, resolvedDt);
@ -596,7 +598,11 @@ public abstract class CompEditorModel extends CompositeEditorModel {
return null;
}
});
fixSelection();
componentEdited();
selectionChanged();
return dtc;
}
/**
@ -628,6 +634,10 @@ public abstract class CompEditorModel extends CompositeEditorModel {
else {
dtc = viewDTM.withTransaction("Add Component", () -> insert(rowIndex, dt, dtLength));
}
fixSelection();
componentEdited();
selectionChanged();
return dtc;
}
@ -733,10 +743,8 @@ public abstract class CompEditorModel extends CompositeEditorModel {
int sizeDiff = newCompSize - oldCompSize;
// New one is larger so check to make sure it will fit.
if (sizeDiff > 0) {
if (!checkForReplace(rowIndex, datatype)) {
throw new InvalidDataTypeException(datatype.getDisplayName() + " doesn't fit.");
}
if (!isAtEnd(rowIndex) && sizeDiff > 0) {
checkForReplace(rowIndex, datatype, newCompSize);
}
// Replace the component at index.
@ -811,35 +819,76 @@ public abstract class CompEditorModel extends CompositeEditorModel {
*
* @param rowIndex index of the row (component).
* @param datatype the type
* @return true if the replace is allowed
* @param length component length
* @throws InvalidDataTypeException if check fails
*/
boolean checkForReplace(int rowIndex, DataType datatype) {
private void checkForReplace(int rowIndex, DataType datatype, int length) throws InvalidDataTypeException {
DataTypeComponent dtc = getComponent(rowIndex);
if (dtc == null) {
return false;
throw new InvalidDataTypeException("Invalid component selection");
}
if (!isShowingUndefinedBytes()) {
return true;
if (!(viewComposite instanceof Structure struct)) {
return;
}
if (struct.isPackingEnabled()) {
return;
}
if (isAtEnd(rowIndex)) {
return;
}
// Does the new data type fit by replacing the component at index.
// Get the current data type at the index.
DataTypeComponent comp = getComponent(rowIndex);
int currentCompSize = comp.getLength();
int newCompSize = datatype.getLength();
int currentCompSize = dtc.getLength();
int newCompSize = length;
int sizeDiff = newCompSize - currentCompSize;
int numUndefs = 0;
// New one is larger.
if (sizeDiff > 0) {
if (isAtEnd(rowIndex) || onlyUndefinedsUntilEnd(rowIndex + 1)) {
return true;
}
// structure needs to have enough undefined bytes or replace fails.
numUndefs = getNumUndefinedBytesAt(rowIndex + 1);
if (sizeDiff <= 0) {
return;
}
return (sizeDiff <= numUndefs);
int undefinedSpaceAvail = getNumUndefinedBytesAfter(dtc);
if (sizeDiff > undefinedSpaceAvail) {
int spaceNeeded = sizeDiff - undefinedSpaceAvail;
String msg = newCompSize + " byte replacement at 0x" + Integer.toHexString(dtc.getOffset());
if (struct.getDefinedComponentAtOrAfterOffset(dtc.getOffset() + 1) == null) {
// suggest growing structure
int suggestedSize = getLength() + spaceNeeded;
throw new InvalidDataTypeException(msg + " requires structure length of " + suggestedSize + "-bytes.");
}
// suggest insert bytes (NOTE: in the future a conflict removal/grow could be offered)
throw new InvalidDataTypeException(msg + " requires " + spaceNeeded + " additional undefined bytes.");
}
}
/**
* Get the number of undefined bytes after the specified component.
* The viewComposite must be a non-packed structure.
* @param dtc datatype component
* @return number of undefined bytes after non-packed structure component or -1 if no additional
* defined components exist which will impead component growth or placement.
*/
protected final int getNumUndefinedBytesAfter(DataTypeComponent dtc) {
if (!isShowingUndefinedBytes()) {
throw new UnsupportedOperationException();
}
if (!(viewComposite instanceof Structure struct)) {
throw new UnsupportedOperationException();
}
if (struct.isPackingEnabled()) {
throw new UnsupportedOperationException();
}
// TODO: May need special logic if dtc is zero-length component
int length = getLength();
int nextCompOffset = dtc.getEndOffset() + 1;
if (nextCompOffset >= length) {
return 0;
}
DataTypeComponent nextDefinedDtc = struct.getDefinedComponentAtOrAfterOffset(nextCompOffset);
int nextDefinedOffset = (nextDefinedDtc == null) ? length : nextDefinedDtc.getOffset();
return Math.max(0, nextDefinedOffset - nextCompOffset); // prevent negative return value
}
/**
@ -1013,34 +1062,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
});
}
/**
* Returns the number of undefined bytes that are available in the structure
* beginning at the specified row index.
*
* @param rowIndex the index of the row
* @return the number of bytes
*/
protected int getNumUndefinedBytesAt(int rowIndex) {
int numRowComponents = getNumComponents();
if (rowIndex < 0 || rowIndex >= numRowComponents) {
return 0;
}
DataTypeComponent startComponent = getComponent(rowIndex);
int previousOffset = (startComponent != null) ? startComponent.getOffset() : 0;
for (int currentRowIndex =
rowIndex; currentRowIndex < numRowComponents; currentRowIndex++) {
// Get the current data type at the index.
DataTypeComponent comp = getComponent(currentRowIndex);
DataType dt = comp.getDataType();
int currentOffset = comp.getOffset();
if (!dt.equals(DataType.DEFAULT)) {
return currentOffset - previousOffset; // Ran into data type other than undefined byte.
}
}
return viewComposite.getLength() - previousOffset;
}
/**
* Determine if the indicated row index is at or beyond the last component in this composite.
*
@ -1062,36 +1083,6 @@ public abstract class CompEditorModel extends CompositeEditorModel {
return false;
}
/**
* Determine whether or not there are only undefined data types from the indicated rowIndex
* until the end of the composite. There must be at least one undefined data type to return true.
*
* @param rowIndex the index of the row to begin checking for undefined data types.
* @return true if an undefined data type is at the indicated row index and all components
* from there to the end of the composite are undefined data types.
*/
protected boolean onlyUndefinedsUntilEnd(int rowIndex) {
if (!isShowingUndefinedBytes()) {
return false;
}
int numRowComponents = getNumComponents();
if (rowIndex < 0) {
return false;
}
if (rowIndex >= numRowComponents) {
return false; // Beyond last component.
}
for (int i = rowIndex; i < numRowComponents; i++) {
// Get the current data type at the index.
DataTypeComponent comp = getComponent(i);
DataType dt = comp.getDataType();
if (!dt.equals(DataType.DEFAULT)) {
return false; // Ran into data type other than undefined byte.
}
}
return true;
}
/**
* Cause the component at the specified index to consume undefined bytes
* that follow it.
@ -1670,16 +1661,17 @@ public abstract class CompEditorModel extends CompositeEditorModel {
if ((rowIndex < 0) || (rowIndex >= numRowComponents)) {
return 0;
}
if (rowIndex + 1 == numRowComponents) {
return Integer.MAX_VALUE; // On last component.
DataTypeComponent dtc = getComponent(rowIndex);
DataType dt = dtc.getDataType();
int dtcLen = dt.getLength();
if (dtcLen < 0) {
dtcLen = dtc.getLength();
}
DataType dt = getComponent(rowIndex).getDataType();
int maxDups = Integer.MAX_VALUE;
// If editModel is showing undefined bytes (non-packed)
// then constrain by number of undefined bytes that follow.
if (isShowingUndefinedBytes() && (dt != DataType.DEFAULT)) {
int numBytes = getNumUndefinedBytesAt(rowIndex + 1);
maxDups = (numBytes / dt.getLength());
int maxDups = (Integer.MAX_VALUE - getLength()) / dtcLen;
if (dt != DataType.DEFAULT && isShowingUndefinedBytes() && !isAtEnd(rowIndex)) {
// If editModel is showing undefined bytes (non-packed)
// then constrain by number of undefined bytes that follow.
maxDups = getNumUndefinedBytesAfter(dtc) / dtcLen;
}
return maxDups;
}
@ -1715,15 +1707,7 @@ public abstract class CompEditorModel extends CompositeEditorModel {
return numBytesInRange / len;
}
// single line selected.
// If editModel is locked then constrain by number of undefined bytes that follow.
if (!isShowingUndefinedBytes() || isAtEnd(rowIndex) ||
onlyUndefinedsUntilEnd(rowIndex + 1)) {
return Integer.MAX_VALUE;
}
int numBytes = getNumUndefinedBytesAt(rowIndex + 1);
return 1 + (numBytes / len);
return getMaxDuplicates(rowIndex) + 1;
}
return 0;
}

View file

@ -30,8 +30,7 @@ import ghidra.util.HelpLocation;
* <p>
* Note: Any new actions must be registered in the editor manager via the actions's name.
*/
abstract public class CompositeEditorTableAction extends DockingAction
implements CompositeEditorModelListener {
abstract public class CompositeEditorTableAction extends DockingAction {
static final String MAIN_ACTION_GROUP = "0_MAIN_EDITOR_ACTION";
static final String UNDOREDO_ACTION_GROUP = "1_UNDOREDO_EDITOR_ACTION";
@ -79,14 +78,12 @@ abstract public class CompositeEditorTableAction extends DockingAction
this.model = provider.getModel();
this.plugin = provider.plugin;
this.tool = plugin.getTool();
model.addCompositeEditorModelListener(this);
String helpAnchor = provider.getHelpName() + "_" + getHelpName();
setHelpLocation(new HelpLocation(provider.getHelpTopic(), helpAnchor));
}
@Override
public void dispose() {
model.removeCompositeEditorModelListener(this);
super.dispose();
provider = null;
model = null;
@ -108,43 +105,4 @@ abstract public class CompositeEditorTableAction extends DockingAction
return getName();
}
@Override
public void selectionChanged() {
provider.contextChanged();
}
public void editStateChanged(int i) {
provider.contextChanged();
}
@Override
public void compositeEditStateChanged(int type) {
provider.contextChanged();
}
@Override
public void endFieldEditing() {
provider.contextChanged();
}
@Override
public void componentDataChanged() {
provider.contextChanged();
}
@Override
public void compositeInfoChanged() {
provider.contextChanged();
}
@Override
public void statusChanged(String message, boolean beep) {
// we are an action; don't care about status messages
}
@Override
public void showUndefinedStateChanged(boolean showUndefinedBytes) {
provider.contextChanged();
}
}

View file

@ -1146,6 +1146,7 @@ abstract class CompositeViewerModel extends AbstractTableModel
for (CompositeViewerModelListener listener : modelListeners) {
listener.selectionChanged();
}
provider.contextChanged();
});
}

View file

@ -215,36 +215,8 @@ class StructureEditorModel extends CompEditorModel {
}
viewDTM.withTransaction("Set Size", () -> {
int length = currentLength;
Structure structure = (Structure) viewComposite;
if (length > size) {
int numComponents = structure.getNumComponents();
DataTypeComponent dtc = structure.getComponentContaining(size);
int ordinal = dtc.getOrdinal();
// retain any zero-length components which have an offset equal the new size
while (dtc.getOffset() == size && dtc.getLength() == 0 &&
(ordinal + 1) < numComponents) {
dtc = structure.getComponent(++ordinal);
}
// remove trailing components outside of new size
for (int index = numComponents - 1; index >= ordinal; index--) {
structure.delete(index);
int bitFieldResidualBytes = structure.getNumComponents() - index;
for (int i = 0; i < bitFieldResidualBytes; i++) {
// bitfield removal may cause injection of undefined bytes - remove them
structure.delete(index);
}
}
// structure may shrink too much from component removal - may need to grow
length = (viewComposite.isZeroLength()) ? 0 : viewComposite.getLength();
}
if (length < size) {
// Increasing structure length.
structure.growStructure(size - length);
}
structure.setLength(size);
});
notifyCompositeChanged();
}
@ -372,13 +344,17 @@ class StructureEditorModel extends CompEditorModel {
throw new IllegalArgumentException("Invalid component index specified");
}
DataType dt = originalComp.getDataType();
int dtLen = dt.getLength();
int len = dt.getLength();
if (len < 0) {
len = originalComp.getLength();
}
checkIsAllowableDataType(dt);
int dtcLen = len;
viewDTM.withTransaction("Duplicate Components", () -> {
int startIndex = index + 1;
if (isShowingUndefinedBytes() && (dt != DataType.DEFAULT)) {
int endIndex = startIndex + (dtLen * multiple) - 1;
if (dt != DataType.DEFAULT && isShowingUndefinedBytes() && !isAtEnd(index)) {
int endIndex = startIndex + (dtcLen * multiple) - 1;
if (startIndex < getNumComponents()) {
deleteComponentRange(startIndex, endIndex, monitor);
}
@ -605,21 +581,16 @@ class StructureEditorModel extends CompEditorModel {
if (comp == null) {
return false;
}
DataType dt = comp.getDataType();
if (viewComposite.isPackingEnabled()) {
if (viewComposite.isPackingEnabled() || isAtEnd(rowIndex)) {
return true;
}
DataType dt = comp.getDataType();
if (dt.equals(DataType.DEFAULT)) {
return true; // Insert an undefined and push everything down.
}
if (comp.isBitFieldComponent()) {
return false; // unable to place non-packed bitfield in a reasonable fashion
}
// Can always duplicate at the end.
if (isAtEnd(rowIndex) || onlyUndefinedsUntilEnd(rowIndex + 1)) {
return true;
}
// Otherwise can only duplicate if enough room.
// Get the size of the data type at this index and the number of
// undefined bytes following it.
@ -627,8 +598,7 @@ class StructureEditorModel extends CompEditorModel {
if (dtSize <= 0) {
dtSize = comp.getLength();
}
int undefSize = getNumUndefinedBytesAt(rowIndex + 1);
if (dtSize <= undefSize) {
if (dtSize <= getNumUndefinedBytesAfter(comp)) {
return true;
}
return false;
@ -701,8 +671,7 @@ class StructureEditorModel extends CompEditorModel {
1 == currentRange.getEnd().getIndex().intValue());
if (isOneComponent) {
if (!isShowingUndefinedBytes() || isAtEnd(currentIndex) ||
onlyUndefinedsUntilEnd(currentIndex + 1)) {
if (isPackingEnabled() || isAtEnd(currentIndex)) {
return true; // allow replace of component when aligning.
}
@ -711,10 +680,6 @@ class StructureEditorModel extends CompEditorModel {
DataTypeComponent comp = getComponent(currentIndex);
if (comp != null) {
DataType compDt = comp.getDataType();
int numCompBytes = comp.getLength();
int numFollowing = getNumUndefinedBytesAt(currentIndex + 1);
int numAvailable = numCompBytes + numFollowing;
// Drop on pointer.
if (compDt instanceof Pointer ||
DataTypeHelper.getBaseType(compDt) instanceof Pointer) {
// Don't create undefined byte pointers.
@ -723,10 +688,8 @@ class StructureEditorModel extends CompEditorModel {
}
return true;
}
else if (datatype.getLength() <= numAvailable) {
return true;
}
return false;
int numAvailable = comp.getLength() + getNumUndefinedBytesAfter(comp);
return datatype.getLength() <= numAvailable;
}
return true;
}
@ -774,17 +737,18 @@ class StructureEditorModel extends CompEditorModel {
catch (InvalidDataTypeException e) {
return false;
}
if (isShowingUndefinedBytes()) {
if (isAtEnd(rowIndex)) {
return true;
}
int maxBytes = dtc.getLength() + getNumUndefinedBytesAt(rowIndex + 1);
if (dataType.getLength() > maxBytes) {
return false;
}
if (isPackingEnabled() || isAtEnd(rowIndex)) {
return true;
}
return true;
int undefSize = getNumUndefinedBytesAfter(dtc);
if (undefSize < 0) {
return true;
}
int numAvailable = dtc.getLength() + undefSize;
return dataType.getLength() <= numAvailable;
}
// *************************************************************
@ -804,9 +768,11 @@ class StructureEditorModel extends CompEditorModel {
*/
@Override
public int getMaxAddLength(int rowIndex) {
int maxLength = Integer.MAX_VALUE;
if (rowIndex >= getNumComponents() - 1) {
return maxLength;
return Integer.MAX_VALUE;
}
if (isPackingEnabled() || isAtEnd(rowIndex)) {
return Integer.MAX_VALUE;
}
DataTypeComponent comp = getComponent(rowIndex);
FieldRange currentRange = getSelectedRangeContaining(rowIndex);
@ -817,18 +783,9 @@ class StructureEditorModel extends CompEditorModel {
1 == currentRange.getEnd().getIndex().intValue());
if (isOneComponent) {
if (!isShowingUndefinedBytes()) {
return maxLength;
}
// FreeForm editing mode (showing Undefined Bytes).
int numAvailable = comp.getLength() + getNumUndefinedBytesAt(rowIndex + 1);
return (maxLength == -1) ? numAvailable : Math.min(maxLength, numAvailable);
return comp.getLength() + getNumUndefinedBytesAfter(comp);
}
DataTypeComponent startComp = getComponent(currentRange.getStart().getIndex().intValue());
DataTypeComponent endComp = getComponent(currentRange.getEnd().getIndex().intValue() - 1);
int numAvailable = endComp.getOffset() + endComp.getLength() - startComp.getOffset();
return (maxLength == -1) ? numAvailable : Math.min(maxLength, numAvailable);
return getNumBytesInRange(currentRange);
}
/**
@ -842,26 +799,34 @@ class StructureEditorModel extends CompEditorModel {
*/
@Override
public int getMaxReplaceLength(int currentIndex) {
if (!isShowingUndefinedBytes()) { // Can replace at any index
if (currentIndex >= getNumComponents() - 1) {
return Integer.MAX_VALUE;
}
if (isPackingEnabled() || isAtEnd(currentIndex)) {
return Integer.MAX_VALUE;
}
// Can only replace with what fits unless at last component or empty last line.
DataTypeComponent comp = getComponent(currentIndex);
int numComponents = getNumComponents();
if ((currentIndex >= (numComponents - 1)) && (currentIndex <= numComponents)) {
return Integer.MAX_VALUE; // Last component or empty entry immediately after it.
}
else if (comp == null) {
if (comp == null) {
return 0; // No such component. Not at valid edit index.
}
// Otherwise, get size of component and number of Undefined bytes after it.
FieldRange range = getSelectedRangeContaining(currentIndex);
if (range == null ||
range.getStart().getIndex().intValue() == range.getEnd().getIndex().intValue() - 1) {
return comp.getLength() + getNumUndefinedBytesAt(currentIndex + 1);
FieldRange currentRange = getSelectedRangeContaining(currentIndex);
boolean isOneComponent =
(currentRange == null) || (currentRange.getStart().getIndex().intValue() +
1 == currentRange.getEnd().getIndex().intValue());
if (isOneComponent) {
return comp.getLength() + getNumUndefinedBytesAfter(comp);
}
return getNumBytesInRange(range);
return getNumBytesInRange(currentRange);
}
/**
@ -959,7 +924,7 @@ class StructureEditorModel extends CompEditorModel {
int componentOrdinal = convertRowToOrdinal(rowIndex);
// FreeForm editing mode (showing Undefined Bytes).
if (isShowingUndefinedBytes() && !isAtEnd(rowIndex)) {
if (!isPackingEnabled() && !isAtEnd(rowIndex)) {
int origLen = getComponent(rowIndex).getLength();
dtc = viewDTM.withTransaction("Replace Component", () -> {
return ((Structure) viewComposite).replace(componentOrdinal, dataType, length,
@ -983,14 +948,22 @@ class StructureEditorModel extends CompEditorModel {
}
else {
dtc = viewDTM.withTransaction("Replace Component", () -> {
((Structure) viewComposite).delete(componentOrdinal);
return ((Structure) viewComposite).insert(componentOrdinal, dataType, length,
name, comment);
Structure struct = (Structure) viewComposite;
DataTypeComponent comp = getComponent(rowIndex);
if (!isPackingEnabled()) {
// We are at end with packing disabled - grow structure if needed
int avail = comp.getLength() + getNumUndefinedBytesAfter(comp);
if (length > avail) {
struct.growStructure(length - avail);
}
}
return ((Structure) viewComposite).replace(componentOrdinal, dataType, length, name, comment);
});
}
return dtc;
}
catch (IllegalArgumentException exc) {
// NOTE: Use of exception may cause transaction rollback
throw new InvalidDataTypeException(exc.getMessage());
}
}

View file

@ -525,17 +525,6 @@ class UnionEditorModel extends CompEditorModel {
});
}
/**
* Returns the number of undefined bytes that are available in the structure
* beginning at the specified row index.
*
* @param rowIndex the index of the row
*/
@Override
protected int getNumUndefinedBytesAt(int rowIndex) {
return 0;
}
/**
* ?????
*

View file

@ -4,9 +4,9 @@
* 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.
@ -393,6 +393,11 @@ public abstract class BiDirectionDataType extends StructureDataType
notifySizeChanged();
return dtc;
}
@Override
public void setLength(int len) {
throw new UnsupportedOperationException("setLength not supported");
}
/**
* Increases the size of the bidirectional data type If amount is positive then the positive

View file

@ -278,7 +278,7 @@ public class StackEditorPanel extends CompositeEditorPanel {
@Override
public void componentDataChanged() {
// Don't need to update other than table when component data changes.
provider.contextChanged();
}
@Override

View file

@ -593,12 +593,12 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest {
assertNotNull(asciiDt);
addAtPoint(asciiDt, 2, 3);
assertEquals(3, structureModel.getNumComponents());
assertEquals(4, structureModel.getRowCount());
assertEquals(7, structureModel.getNumComponents());
assertEquals(8, structureModel.getRowCount());
checkRow(0, 0, 1, "db", new ByteDataType(), "", "");
checkRow(1, 1, 4, "float", new FloatDataType(), "", "");
checkRow(2, 5, 1, "char", asciiDt, "", "");
assertLength(6);
assertLength(10);
assertActualAlignment(1);
}

View file

@ -4,9 +4,9 @@
* 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.
@ -196,7 +196,7 @@ public class StructureEditorLockedActions3Test extends AbstractStructureEditorTe
assertEquals(9, getModel().getNumComponents());
assertEquals("byte", getDataType(1).getName());
assertEquals(getDataType(1), dt1);
assertEquals("pointer doesn't fit.", getModel().getStatus());
assertTrue(getModel().getStatus().contains("requires 1 additional"));
assertEquals(1, getDataType(1).getLength());
assertEquals(1, getModel().getComponent(1).getLength());
assertEquals(DataType.DEFAULT, getDataType(2));

View file

@ -4,9 +4,9 @@
* 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.
@ -78,7 +78,7 @@ public class StructureEditorLockedDnDTest extends AbstractStructureEditorTest {
assertTrue(getDataType(1).isEquivalent(dt1));
assertEquals(dt1.getLength(), model.getComponent(1).getLength());
assertEquals(325, model.getLength());
assertEquals("double doesn't fit.", model.getStatus());
assertTrue(model.getStatus().contains("requires 7 additional"));
}
@Test

View file

@ -4,9 +4,9 @@
* 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.
@ -119,14 +119,14 @@ public class StructureEditorUnlockedActions2Test
checkSelection(new int[] { 0 });
invoke(action);
assertEquals(1, model.getNumComponents());
assertEquals(8, model.getNumComponents());
assertTrue(getDataType(0).isEquivalent(new ByteDataType()));
assertEquals(1, getLength(0));
checkSelection(new int[] { 0 });
CycleGroupAction floatAction = getCycleGroup(new FloatDataType());
invoke(floatAction);
assertEquals(1, model.getNumComponents());
assertEquals(5, model.getNumComponents());
assertTrue(getDataType(0).isEquivalent(new FloatDataType()));
assertEquals(4, getLength(0));
checkSelection(new int[] { 0 });
@ -138,7 +138,7 @@ public class StructureEditorUnlockedActions2Test
checkSelection(new int[] { 0 });
invoke(floatAction);
assertEquals(1, model.getNumComponents());
assertEquals(5, model.getNumComponents());
assertTrue(getDataType(0).isEquivalent(new FloatDataType()));
assertEquals(4, getLength(0));
checkSelection(new int[] { 0 });

View file

@ -412,7 +412,7 @@ public class StructureEditorUnlockedActions5Test extends AbstractStructureEditor
assertTrue(getDataType(2).isEquivalent(new WordDataType()));
invoke(pointerAction);
assertEquals("pointer doesn't fit.", model.getStatus());
assertTrue(model.getStatus().contains("requires 2 additional"));
assertEquals(num, model.getNumComponents());
assertEquals("word", getDataType(2).getDisplayName());
assertTrue(getDataType(2).isEquivalent(new WordDataType()));

View file

@ -4,9 +4,9 @@
* 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.
@ -45,7 +45,7 @@ public class StructureEditorUnlockedDnD4Test extends AbstractStructureEditorTest
addAtPoint(dt, 0, 3);
assertEquals(num, model.getNumComponents());
assertTrue(getDataType(0).isEquivalent(DataType.DEFAULT));
assertEquals("word doesn't fit.", model.getStatus());
assertTrue(model.getStatus().contains("requires 1 additional"));
dt = programDTM.getDataType("/char");
assertNotNull(dt);
@ -114,7 +114,7 @@ public class StructureEditorUnlockedDnD4Test extends AbstractStructureEditorTest
addAtPoint(dt, 5, 3);
assertEquals("qword doesn't fit.", model.getStatus());
assertTrue(model.getStatus().contains("requires 4 additional"));
assertEquals(num, model.getNumComponents());
assertEquals("float", getDataType(5).getDisplayName());
assertEquals(29, model.getLength());

View file

@ -4,9 +4,9 @@
* 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.
@ -1046,6 +1046,41 @@ public class StructureDataTypeTest extends AbstractGenericTest {
assertEquals(10, comps[3].getOffset());
assertEquals(7, comps[3].getOrdinal());
}
@Test
public void testSetLength() {
assertEquals(8, struct.getLength());
assertEquals(4, struct.getNumComponents());
assertEquals(4, struct.getNumDefinedComponents());
struct.setLength(20);
assertEquals(20, struct.getLength());
assertEquals(16, struct.getNumComponents());
assertEquals(4, struct.getNumDefinedComponents());
// new length is offcut within 3rd component at offset 0x3 which should get cleared
struct.setLength(4);
assertEquals(4, struct.getLength());
assertEquals(3, struct.getNumComponents());
assertEquals(2, struct.getNumDefinedComponents());
// Maximum length supported by GUI editor is ~Integer.MAX_VALUE/10
int len = Integer.MAX_VALUE / 10;
struct.setLength(len);
assertEquals(len, struct.getLength());
assertEquals(len - 1, struct.getNumComponents());
assertEquals(2, struct.getNumDefinedComponents());
len /= 2;
struct.replaceAtOffset(len-2, WordDataType.dataType, -1, "x", null); // will be preserved below
struct.replaceAtOffset(len+2, WordDataType.dataType, -1, "y", null); // will be cleared below
struct.setLength(len);
assertEquals(len, struct.getLength());
assertEquals(len - 2, struct.getNumComponents());
assertEquals(3, struct.getNumDefinedComponents());
}
@Test
public void testDeleteMany() {

View file

@ -236,9 +236,57 @@ class StructureDB extends CompositeDB implements StructureInternal {
}
}
@Override
public void setLength(int len) {
if (len < 0) {
throw new IllegalArgumentException("Invalid length: " + len);
}
if (len == structLength || isPackingEnabled()) {
return;
}
lock.acquire();
try {
checkDeleted();
if (len < structLength) {
// identify index of first defined-component to be removed
int index = Collections.binarySearch(components, Integer.valueOf(len),
OffsetComparator.INSTANCE);
if (index < 0) {
index = -index - 1;
}
else {
index = backupToFirstComponentContainingOffset(index, len);
}
int definedComponentCount = components.size();
if (index >= 0 && index < definedComponentCount) {
for (int i = index; i < definedComponentCount; i++) {
doDelete(components.get(i));
}
components = components.subList(0, index);
}
}
else {
numComponents += len - structLength;
}
structLength = len;
repack(false, false);
notifySizeChanged(false);
}
catch (IOException e) {
dataMgr.dbError(e);
}
finally {
lock.release();
}
}
@Override
public void growStructure(int amount) {
if (isPackingEnabled()) {
if (amount < 0) {
throw new IllegalArgumentException("Invalid growth amount: " + amount);
}
if (amount == 0 || isPackingEnabled()) {
return;
}
lock.acquire();

View file

@ -914,6 +914,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
}
}
if (restored) {
invalidateCache();
notifyRestored();
}
}

View file

@ -4,9 +4,9 @@
* 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.
@ -430,13 +430,22 @@ public interface Structure extends Composite {
String comment) throws IllegalArgumentException;
/**
* Increases the size of the structure by the specified amount by adding undefined filler at the
* Increases the size of the structure by the specified positive amount by adding undefined filler at the
* end of the structure. NOTE: This method only has an affect on non-packed structures.
*
* @param amount the amount by which to grow the structure.
* @throws IllegalArgumentException if amount &lt; 1
* @throws IllegalArgumentException if amount &lt; 0
*/
public void growStructure(int amount);
/**
* Set the size of the structure to the specified byte-length. If the length is shortened defined
* components will be cleared and removed as required.
* NOTE: This method only has an affect on non-packed structures.
* @param length new structure length
* @throws IllegalArgumentException if length &lt; 0
*/
public void setLength(int length);
/**
* <code>BitOffsetComparator</code> provides ability to compare an normalized bit offset (see

View file

@ -624,9 +624,43 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
return dtc;
}
@Override
public void setLength(int len) {
if (len < 0) {
throw new IllegalArgumentException("Invalid length: " + len);
}
if (len == structLength || isPackingEnabled()) {
return;
}
if (len < structLength) {
// identify index of first defined-component to be removed
int index = Collections.binarySearch(components, Integer.valueOf(len),
OffsetComparator.INSTANCE);
if (index < 0) {
index = -index - 1;
}
else {
index = backupToFirstComponentContainingOffset(index, len);
}
int definedComponentCount = components.size();
if (index >= 0 && index < definedComponentCount) {
components = components.subList(0, index);
}
}
else {
numComponents += len - structLength;
}
structLength = len;
repack(false);
notifySizeChanged();
}
@Override
public void growStructure(int amount) {
if (isPackingEnabled()) {
if (amount < 0) {
throw new IllegalArgumentException("Invalid growth amount: " + amount);
}
if (amount == 0 || isPackingEnabled()) {
return;
}
doGrowStructure(amount);

View file

@ -1449,6 +1449,40 @@ public class StructureDBTest extends AbstractGenericTest {
assertEquals(dtc1, barStruct.getComponent(6));
}
@Test
public void testSetLength() {
assertEquals(8, struct.getLength());
assertEquals(4, struct.getNumComponents());
assertEquals(4, struct.getNumDefinedComponents());
struct.setLength(20);
assertEquals(20, struct.getLength());
assertEquals(16, struct.getNumComponents());
assertEquals(4, struct.getNumDefinedComponents());
// new length is offcut within 3rd component at offset 0x3 which should get cleared
struct.setLength(4);
assertEquals(4, struct.getLength());
assertEquals(3, struct.getNumComponents());
assertEquals(2, struct.getNumDefinedComponents());
// Maximum length supported by GUI editor is ~Integer.MAX_VALUE/10
int len = Integer.MAX_VALUE / 10;
struct.setLength(len);
assertEquals(len, struct.getLength());
assertEquals(len - 1, struct.getNumComponents());
assertEquals(2, struct.getNumDefinedComponents());
len /= 2;
struct.replaceAtOffset(len-2, WordDataType.dataType, -1, "x", null); // will be preserved below
struct.replaceAtOffset(len+2, WordDataType.dataType, -1, "y", null); // will be cleared below
struct.setLength(len);
assertEquals(len, struct.getLength());
assertEquals(len - 2, struct.getNumComponents());
assertEquals(3, struct.getNumDefinedComponents());
}
@Test
public void testDeleteMany() {