mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
Merge remote-tracking branch
'origin/GP-3255_ghidragon_enum_issues--SQUASHED' (Closes #3806)
This commit is contained in:
commit
08ea793ac9
9 changed files with 970 additions and 111 deletions
|
@ -28,12 +28,13 @@ import docking.widgets.OptionDialog;
|
|||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.table.GTableCellRenderer;
|
||||
import docking.widgets.table.GTableTextCellEditor;
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.textfield.GValidatedTextField;
|
||||
import docking.widgets.textfield.GValidatedTextField.LongField.LongValidator;
|
||||
import docking.widgets.textfield.GValidatedTextField.ValidationFailedException;
|
||||
import docking.widgets.textfield.GValidatedTextField.ValidationMessageListener;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.DataTypeArchive;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -55,6 +56,7 @@ class EnumEditorPanel extends JPanel {
|
|||
|
||||
private EnumDataType originalEnumDT;
|
||||
private EnumDataType editedEnumDT;
|
||||
boolean showValuesAsHex = true;
|
||||
|
||||
EnumEditorPanel(EnumDataType enumDT, EnumEditorProvider provider) {
|
||||
super(new BorderLayout());
|
||||
|
@ -302,7 +304,19 @@ class EnumEditorPanel extends JPanel {
|
|||
.setCellEditor(
|
||||
new EnumLongCellEditor());
|
||||
table.setDefaultRenderer(String.class, new GTableCellRenderer());
|
||||
table.setDefaultRenderer(Long.class, new EnumValueRenderer());
|
||||
add(createInfoPanel(), BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
private String getValueAsString(long value) {
|
||||
if (showValuesAsHex) {
|
||||
int length = editedEnumDT.getLength();
|
||||
if (editedEnumDT.isSigned()) {
|
||||
return NumericUtilities.toSignedHexString(value);
|
||||
}
|
||||
return NumericUtilities.toHexString(value, length);
|
||||
}
|
||||
return Long.toString(value);
|
||||
|
||||
}
|
||||
|
||||
|
@ -431,39 +445,22 @@ class EnumEditorPanel extends JPanel {
|
|||
|
||||
private boolean validateNewLength(Integer length) {
|
||||
EnumDataType enuum = tableModel.getEnum();
|
||||
String[] names = enuum.getNames();
|
||||
for (String name : names) {
|
||||
long value = enuum.getValue(name);
|
||||
if (tableModel.isValueTooBigForLength(value, length)) {
|
||||
vetoSizeChange(length, enuum.getLength(), value);
|
||||
int minLength = enuum.getMinimumPossibleLength();
|
||||
if (length < minLength) {
|
||||
vetoSizeChange(length, minLength, enuum.getLength());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean validateNewValue(Long value) {
|
||||
EnumDataType enuum = tableModel.getEnum();
|
||||
int length = enuum.getLength();
|
||||
return !tableModel.isValueTooBigForLength(value, length);
|
||||
}
|
||||
|
||||
private void vetoSizeChange(final int newLength, final int currentLength, final long badValue) {
|
||||
private void vetoSizeChange(int newLength, int minLength, int currentLength) {
|
||||
Swing.runLater(() -> {
|
||||
setStatusMessage("Enum size of " + newLength + " cannot contain the value " + "0x" +
|
||||
Long.toHexString(badValue));
|
||||
setStatusMessage(
|
||||
"Enum size of " + newLength + " is smaller than minimum enum size of " + minLength);
|
||||
sizeComboBox.setSelectedItem(Integer.valueOf(currentLength));
|
||||
});
|
||||
}
|
||||
|
||||
public String getValidValuesMessage() {
|
||||
EnumDataType enuum = tableModel.getEnum();
|
||||
int length = enuum.getLength();
|
||||
long maxValue = length == 8 ? -1 : (1L << (8 * length)) - 1;
|
||||
return "Valid values are from 0x0 to 0x" + Long.toHexString(maxValue);
|
||||
|
||||
}
|
||||
|
||||
private void setFieldInfo(EnumDataType enuum) {
|
||||
nameField.setText(enuum.getDisplayName());
|
||||
sizeComboBox.setSelectedItem(enuum.getLength());
|
||||
|
@ -482,6 +479,10 @@ class EnumEditorPanel extends JPanel {
|
|||
});
|
||||
}
|
||||
|
||||
void setHexDisplayMode(boolean showHex) {
|
||||
showValuesAsHex = showHex;
|
||||
tableModel.fireTableDataChanged();
|
||||
}
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
@ -494,12 +495,29 @@ class EnumEditorPanel extends JPanel {
|
|||
}
|
||||
|
||||
public class RangeValidator extends LongValidator {
|
||||
private long min;
|
||||
private long max;
|
||||
|
||||
public void setOriginalValue(long originalLong) {
|
||||
EnumDataType enuum = tableModel.getEnum();
|
||||
EnumDataType copy = (EnumDataType) enuum.copy(enuum.getDataTypeManager());
|
||||
String name = copy.getName(originalLong);
|
||||
copy.remove(name);
|
||||
min = copy.getMinPossibleValue();
|
||||
max = copy.getMaxPossibleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateLong(long oldLong, long newLong) throws ValidationFailedException {
|
||||
if (!validateNewValue(newLong)) {
|
||||
throw new ValidationFailedException(getValidValuesMessage());
|
||||
public void validateLong(long oldValue, long newValue) throws ValidationFailedException {
|
||||
if (newValue < min || newValue > max) {
|
||||
String minValue = getValueAsString(min);
|
||||
String maxValue = getValueAsString(max);
|
||||
String message =
|
||||
"Valid values are in the range (" + minValue + ", " + maxValue + ")";
|
||||
throw new ValidationFailedException(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class StatusBarValidationMessageListener implements ValidationMessageListener {
|
||||
|
@ -608,11 +626,46 @@ class EnumEditorPanel extends JPanel {
|
|||
}
|
||||
|
||||
private class EnumLongCellEditor extends EnumCellEditor {
|
||||
private RangeValidator validator;
|
||||
|
||||
public EnumLongCellEditor() {
|
||||
super(new GValidatedTextField.LongField(8));
|
||||
GValidatedTextField f = (GValidatedTextField) getComponent();
|
||||
f.addValidator(new RangeValidator());
|
||||
validator = new RangeValidator();
|
||||
f.addValidator(validator);
|
||||
f.addValidationMessageListener(new StatusBarValidationMessageListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellEditorComponent(JTable table1, Object value,
|
||||
boolean isSelected, int row, int column) {
|
||||
Long longValue = (Long) value;
|
||||
validator.setOriginalValue(longValue);
|
||||
String s = getValueAsString(longValue);
|
||||
return super.getTableCellEditorComponent(table1, s, isSelected, row, column);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class EnumValueRenderer extends GTableCellRenderer {
|
||||
EnumValueRenderer() {
|
||||
setFont(Gui.getFont("font.monospaced"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
JLabel renderer = (JLabel) super.getTableCellRendererComponent(data);
|
||||
renderer.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatNumber(Number value, Settings settings) {
|
||||
if (value instanceof Long longValue) {
|
||||
return getValueAsString(longValue);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.action.*;
|
||||
import docking.action.builder.ToggleActionBuilder;
|
||||
import docking.widgets.OptionDialog;
|
||||
import generic.theme.GIcon;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
|
@ -80,6 +81,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
|||
private CategoryPath originalCategoryPath;
|
||||
private Enum originalEnum;
|
||||
private long originalEnumID = -1;
|
||||
private ToggleDockingAction hexDisplayAction;
|
||||
|
||||
/**
|
||||
* Construct a new enum editor provider.
|
||||
|
@ -276,6 +278,14 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
private void createActions() {
|
||||
hexDisplayAction = new ToggleActionBuilder("Toggle Hex Mode", plugin.getName())
|
||||
.menuPath("Show Enum Values in Hex")
|
||||
.description("Toggles Enum value column to show values in hex or decimal")
|
||||
.keyBinding("Shift-H")
|
||||
.selected(true)
|
||||
.onAction(c -> editorPanel.setHexDisplayMode(hexDisplayAction.isSelected()))
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
addAction = new EnumPluginAction("Add Enum Value", e -> editorPanel.addEntry());
|
||||
addAction.setEnabled(true);
|
||||
String editGroup = "Edit";
|
||||
|
|
|
@ -68,6 +68,9 @@ class EnumTableModel extends AbstractSortedTableModel<EnumEntry> {
|
|||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
if (columnIndex == VALUE_COL) {
|
||||
return Long.class;
|
||||
}
|
||||
return String.class;
|
||||
}
|
||||
|
||||
|
@ -81,28 +84,8 @@ class EnumTableModel extends AbstractSortedTableModel<EnumEntry> {
|
|||
switch (columnIndex) {
|
||||
case NAME_COL:
|
||||
return v.getName();
|
||||
|
||||
case VALUE_COL:
|
||||
long mask;
|
||||
|
||||
switch (enuum.getLength()) {
|
||||
case 1:
|
||||
mask = 0xffL;
|
||||
break;
|
||||
case 2:
|
||||
mask = 0xffffL;
|
||||
break;
|
||||
case 4:
|
||||
mask = 0xffffffffL;
|
||||
break;
|
||||
default:
|
||||
case 8:
|
||||
mask = 0xffffffffffffffffL;
|
||||
break;
|
||||
}
|
||||
|
||||
return "0x" + Long.toHexString(v.getValue() & mask);
|
||||
|
||||
return v.getValue();
|
||||
case COMMENT_COL:
|
||||
return v.getComment();
|
||||
}
|
||||
|
@ -277,14 +260,15 @@ class EnumTableModel extends AbstractSortedTableModel<EnumEntry> {
|
|||
afterRow = 0;
|
||||
}
|
||||
long value = enumEntryList.get(afterRow).getValue() + 1;
|
||||
if (isTooBig(value)) {
|
||||
if (!isValidValue(value)) {
|
||||
value = 0;
|
||||
}
|
||||
boolean wrapOK = value != 0;
|
||||
while (enuum.getName(value) != null) {
|
||||
if (isTooBig(++value)) {
|
||||
if (!isValidValue(++value)) {
|
||||
if (wrapOK) {
|
||||
value = 0;
|
||||
wrapOK = false;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
|
@ -294,17 +278,10 @@ class EnumTableModel extends AbstractSortedTableModel<EnumEntry> {
|
|||
return value;
|
||||
}
|
||||
|
||||
boolean isValueTooBigForLength(long value, int length) {
|
||||
if (length < 8) {
|
||||
long max = (1L << (8 * length)) - 1;
|
||||
return value > max || value < 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isTooBig(long value) {
|
||||
int len = enuum.getLength();
|
||||
return isValueTooBigForLength(value, len);
|
||||
private boolean isValidValue(long value) {
|
||||
long min = enuum.getMinPossibleValue();
|
||||
long max = enuum.getMaxPossibleValue();
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
private String getUniqueName() {
|
||||
|
|
|
@ -0,0 +1,278 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.data;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.data.EnumDataType;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
|
||||
/**
|
||||
* Tests for Enum data types.
|
||||
*/
|
||||
public class EnumDbTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private ProgramDB program;
|
||||
private DataTypeManagerDB dataMgr;
|
||||
private int transactionID;
|
||||
private Enum enum1;
|
||||
private Enum enum2;
|
||||
private Enum enum4;
|
||||
private Enum enum8;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
program = createDefaultProgram("Test", ProgramBuilder._TOY, this);
|
||||
|
||||
dataMgr = program.getDataTypeManager();
|
||||
transactionID = program.startTransaction("Test");
|
||||
enum1 = new EnumDataType("Test", 1);
|
||||
enum2 = new EnumDataType("Test", 2);
|
||||
enum4 = new EnumDataType("Test", 4);
|
||||
enum8 = new EnumDataType("Test", 8);
|
||||
|
||||
enum1 = (Enum) dataMgr.resolve(enum1, null);
|
||||
enum2 = (Enum) dataMgr.resolve(enum2, null);
|
||||
enum4 = (Enum) dataMgr.resolve(enum4, null);
|
||||
enum8 = (Enum) dataMgr.resolve(enum8, null);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
program.endTransaction(transactionID, false);
|
||||
program.release(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAddHighUnsignedValue() {
|
||||
enum1.add("a", 0xff);
|
||||
assertEquals(0xff, enum1.getValue("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAddNegativeValue() {
|
||||
enum1.add("a", -1);
|
||||
assertEquals(-1, enum1.getValue("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCantAddNegativeAndHighUnsignedValue() {
|
||||
enum1.add("a", -1);
|
||||
try {
|
||||
enum1.add("b", 0xff);
|
||||
fail("Expected Illegal ArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCantAddHighUnsignedAndNegativeValue() {
|
||||
enum1.add("a", 0xff);
|
||||
try {
|
||||
enum1.add("b", -1);
|
||||
fail("Expected Illegal ArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAddNegativeAfterAddingThenRemovingHighUnsignedValue() {
|
||||
enum1.add("a", 0xff);
|
||||
enum1.remove("a");
|
||||
enum1.add("a", -1);
|
||||
assertEquals(-1, enum1.getValue("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinPossibleWidthUnsigned() {
|
||||
assertEquals(1, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("a", 0);
|
||||
assertEquals(1, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("b", 0xff);
|
||||
assertEquals(1, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("c", 0x100);
|
||||
assertEquals(2, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("d", 0xffff);
|
||||
assertEquals(2, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("e", 0x1ffff);
|
||||
assertEquals(4, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("f", 0xffffffffL);
|
||||
assertEquals(4, enum4.getMinimumPossibleLength());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinPossibleWidthSigned() {
|
||||
assertEquals(1, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("a", 0);
|
||||
assertEquals(1, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("b", -0x1);
|
||||
assertEquals(1, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("c", 0xff);
|
||||
assertEquals(2, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("d", 0xffff);
|
||||
assertEquals(4, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("e", 0x1ffff);
|
||||
assertEquals(4, enum4.getMinimumPossibleLength());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinPossibleWidthSignedWithBigNegatives() {
|
||||
assertEquals(1, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("a", 0);
|
||||
assertEquals(1, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("b", -0x1);
|
||||
assertEquals(1, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("c", -0xff);
|
||||
assertEquals(2, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("d", -0xffff);
|
||||
assertEquals(4, enum4.getMinimumPossibleLength());
|
||||
|
||||
enum4.add("e", -0x1ffff);
|
||||
assertEquals(4, enum4.getMinimumPossibleLength());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesNoSignednessSize1() {
|
||||
assertEquals(-0x80, enum1.getMinPossibleValue());
|
||||
assertEquals(0xff, enum1.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesNoSignednessSize2() {
|
||||
assertEquals(-0x8000, enum2.getMinPossibleValue());
|
||||
assertEquals(0xffff, enum2.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesNoSignednessSize4() {
|
||||
assertEquals(-0x80000000L, enum4.getMinPossibleValue());
|
||||
assertEquals(0xffffffffL, enum4.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesNoSignednessSize8() {
|
||||
assertEquals(Long.MIN_VALUE, enum8.getMinPossibleValue());
|
||||
assertEquals(Long.MAX_VALUE, enum8.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
public void testMinMaxPossibleValuesSignedSize1() {
|
||||
enum1.add("a", -1); // this makes it an signed enum
|
||||
assertEquals(-0x80, enum1.getMinPossibleValue());
|
||||
assertEquals(0x7f, enum1.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesSignedSize2() {
|
||||
enum2.add("a", -1); // this makes it an signed enum
|
||||
assertEquals(-0x8000, enum2.getMinPossibleValue());
|
||||
assertEquals(0x7fff, enum2.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesSignedSize4() {
|
||||
enum4.add("a", -1); // this makes it an signed enum
|
||||
assertEquals(-0x80000000L, enum4.getMinPossibleValue());
|
||||
assertEquals(0x7fffffffL, enum4.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesSignedSize8() {
|
||||
enum8.add("a", -1); // this makes it an signed enum
|
||||
assertEquals(Long.MIN_VALUE, enum8.getMinPossibleValue());
|
||||
assertEquals(Long.MAX_VALUE, enum8.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
public void testMinMaxPossibleValuesUnsignedSize1() {
|
||||
enum1.add("a", 0xff); // this makes it an unsigned enum
|
||||
assertEquals(0, enum1.getMinPossibleValue());
|
||||
assertEquals(0xff, enum1.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesUnsignedSize2() {
|
||||
enum2.add("a", 0xffff); // this makes it an signed enum
|
||||
assertEquals(0, enum2.getMinPossibleValue());
|
||||
assertEquals(0xffff, enum2.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesUnsignedSize4() {
|
||||
enum4.add("a", 0xffffffffL); // this makes it an signed enum
|
||||
assertEquals(0, enum4.getMinPossibleValue());
|
||||
assertEquals(0xffffffffL, enum4.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsName() {
|
||||
enum4.add("a", 0x1);
|
||||
enum4.add("b", -1);
|
||||
assertTrue(enum4.contains("a"));
|
||||
assertTrue(enum4.contains("b"));
|
||||
assertFalse(enum4.contains("c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsValue() {
|
||||
enum4.add("a", 0x1);
|
||||
enum4.add("b", -1);
|
||||
assertTrue(enum4.contains(-1));
|
||||
assertTrue(enum4.contains(1));
|
||||
assertFalse(enum4.contains(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinPossibleLengthFor8ByteEnum() {
|
||||
enum8.add("a", -1);
|
||||
assertEquals(1, enum8.getMinimumPossibleLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test8ByteEnumsAreSigned() {
|
||||
enum8.add("a", 0);
|
||||
assertEquals(Long.MIN_VALUE, enum8.getMinPossibleValue());
|
||||
assertEquals(Long.MAX_VALUE, enum8.getMaxPossibleValue());
|
||||
assertFalse(enum8.isSigned());
|
||||
enum8.add("b", -1);
|
||||
assertTrue(enum8.isSigned());
|
||||
}
|
||||
}
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.program.database.data;
|
||||
|
||||
import static ghidra.program.database.data.EnumSignedState.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
@ -37,7 +39,6 @@ import ghidra.util.UniversalID;
|
|||
* Database implementation for the enumerated data type.
|
||||
*/
|
||||
class EnumDB extends DataTypeDB implements Enum {
|
||||
|
||||
private static final SettingsDefinition[] ENUM_SETTINGS_DEFINITIONS =
|
||||
new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
|
||||
|
||||
|
@ -45,9 +46,10 @@ class EnumDB extends DataTypeDB implements Enum {
|
|||
private EnumValueDBAdapter valueAdapter;
|
||||
|
||||
private Map<String, Long> nameMap; // name to value
|
||||
private TreeMap<Long, List<String>> valueMap; // value to names
|
||||
private SortedMap<Long, List<String>> valueMap; // value to names
|
||||
private Map<String, String> commentMap; // name to comment
|
||||
private List<BitGroup> bitGroups;
|
||||
private EnumSignedState signedState = null;
|
||||
|
||||
EnumDB(DataTypeManagerDB dataMgr, DBObjectCache<DataTypeDB> cache, EnumDBAdapter adapter,
|
||||
EnumValueDBAdapter valueAdapter, DBRecord record) {
|
||||
|
@ -97,6 +99,27 @@ class EnumDB extends DataTypeDB implements Enum {
|
|||
String comment = rec.getString(EnumValueDBAdapter.ENUMVAL_COMMENT_COL);
|
||||
addToCache(valueName, value, comment);
|
||||
}
|
||||
signedState = computeSignedness();
|
||||
}
|
||||
|
||||
private EnumSignedState computeSignedness() {
|
||||
int length = record.getByteValue(EnumDBAdapter.ENUM_SIZE_COL);
|
||||
|
||||
if (valueMap.isEmpty()) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
long minValue = valueMap.firstKey();
|
||||
long maxValue = valueMap.lastKey();
|
||||
|
||||
if (minValue < 0) {
|
||||
return SIGNED;
|
||||
}
|
||||
if (maxValue > getMaxPossibleValue(length, true)) {
|
||||
return UNSIGNED;
|
||||
}
|
||||
|
||||
return NONE; // we have no negatives and no large unsigned values
|
||||
}
|
||||
|
||||
private void addToCache(String valueName, long value, String comment) {
|
||||
|
@ -258,8 +281,8 @@ class EnumDB extends DataTypeDB implements Enum {
|
|||
lock.acquire();
|
||||
try {
|
||||
checkDeleted();
|
||||
checkValue(value);
|
||||
initializeIfNeeded();
|
||||
checkValue(value);
|
||||
if (nameMap.containsKey(valueName)) {
|
||||
throw new IllegalArgumentException(valueName + " already exists in this enum");
|
||||
}
|
||||
|
@ -272,6 +295,7 @@ class EnumDB extends DataTypeDB implements Enum {
|
|||
valueAdapter.createRecord(key, valueName, value, comment);
|
||||
adapter.updateRecord(record, true);
|
||||
addToCache(valueName, value, comment);
|
||||
signedState = computeSignedness();
|
||||
dataMgr.dataTypeChanged(this, false);
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
@ -283,17 +307,17 @@ class EnumDB extends DataTypeDB implements Enum {
|
|||
}
|
||||
|
||||
private void checkValue(long value) {
|
||||
int length = getLength();
|
||||
int length = record.getByteValue(EnumDBAdapter.ENUM_SIZE_COL);
|
||||
if (length == 8) {
|
||||
return; // all long values permitted
|
||||
}
|
||||
// compute maximum enum value as a positive value: (2^length)-1
|
||||
long max = (1L << (getLength() * 8)) - 1;
|
||||
if (value > max) {
|
||||
throw new IllegalArgumentException(
|
||||
getName() + " enum value 0x" + Long.toHexString(value) +
|
||||
" is outside the range of 0x0 to 0x" + Long.toHexString(max));
|
||||
|
||||
long min = getMinPossibleValue();
|
||||
long max = getMaxPossibleValue();
|
||||
if (value < min || value > max) {
|
||||
throw new IllegalArgumentException(
|
||||
"Attempted to add a value outside the range for this enum: (" + min + ", " + max +
|
||||
"): " + value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,6 +341,7 @@ class EnumDB extends DataTypeDB implements Enum {
|
|||
}
|
||||
}
|
||||
adapter.updateRecord(record, true);
|
||||
signedState = computeSignedness();
|
||||
dataMgr.dataTypeChanged(this, false);
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
@ -366,7 +391,7 @@ class EnumDB extends DataTypeDB implements Enum {
|
|||
adapter.updateRecord(record, true);
|
||||
addToCache(valueName, value, comment);
|
||||
}
|
||||
|
||||
signedState = computeSignedness();
|
||||
if (oldLength != newLength) {
|
||||
notifySizeChanged(false);
|
||||
}
|
||||
|
@ -615,6 +640,56 @@ class EnumDB extends DataTypeDB implements Enum {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinPossibleValue() {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
int length = record.getByteValue(EnumDBAdapter.ENUM_SIZE_COL);
|
||||
return getMinPossibleValue(length, signedState != UNSIGNED);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxPossibleValue() {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
int length = record.getByteValue(EnumDBAdapter.ENUM_SIZE_COL);
|
||||
return getMaxPossibleValue(length, signedState == SIGNED);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private long getMaxPossibleValue(int bytes, boolean allowNegativeValues) {
|
||||
if (bytes == 8) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
int bits = bytes * 8;
|
||||
if (allowNegativeValues) {
|
||||
bits -= 1; // take away 1 bit for the sign
|
||||
}
|
||||
|
||||
// the largest value that can be held in n bits in 2^n -1
|
||||
return (1L << bits) - 1;
|
||||
}
|
||||
|
||||
private long getMinPossibleValue(int bytes, boolean allowNegativeValues) {
|
||||
if (!allowNegativeValues) {
|
||||
return 0;
|
||||
}
|
||||
int bits = bytes * 8;
|
||||
|
||||
// smallest value (largest negative) that can be stored in n bits is when the sign bit
|
||||
// is on (and sign extended), and all less significant bits are 0
|
||||
return -1L << (bits - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean refresh() {
|
||||
try {
|
||||
|
@ -754,4 +829,71 @@ class EnumDB extends DataTypeDB implements Enum {
|
|||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String name) {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
initializeIfNeeded();
|
||||
return nameMap.containsKey(name);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(long value) {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
initializeIfNeeded();
|
||||
return valueMap.containsKey(value);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
initializeIfNeeded();
|
||||
return signedState == SIGNED;
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumPossibleLength() {
|
||||
lock.acquire();
|
||||
try {
|
||||
if (valueMap.isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
long minValue = valueMap.firstKey();
|
||||
long maxValue = valueMap.lastKey();
|
||||
boolean hasNegativeValues = minValue < 0;
|
||||
|
||||
// check the min and max values in this enum to see if they fit in 1 byte enum, then
|
||||
// 2 byte enum, then 4 byte enum. If the min min and max values fit, then all other values
|
||||
// will fit as well
|
||||
for (int size = 1; size < 8; size *= 2) {
|
||||
long minPossible = getMinPossibleValue(size, hasNegativeValues);
|
||||
long maxPossible = getMaxPossibleValue(size, hasNegativeValues);
|
||||
if (minValue >= minPossible && maxValue <= maxPossible) {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.data;
|
||||
|
||||
/**
|
||||
* Keeps track of the signed state of an enum datatype. Enum are fundamentally either signed or
|
||||
* unsigned, but sometimes you can't tell based on the values they contain. Once a negative value
|
||||
* is added, then the enum becomes locked as signed, preventing high unsigned values from being
|
||||
* added. Once a high value unsigned value is added, then it becomes locked as unsigned value. If
|
||||
* neither a negative value or high unsigned value has been added, then the enum is not locked as
|
||||
* either signed or unsigned.
|
||||
*/
|
||||
public enum EnumSignedState {
|
||||
SIGNED, // Enum contains at least 1 negative value, preventing high unsigned values
|
||||
UNSIGNED, // Enum contains at least 1 high unsigned value, preventing negative values
|
||||
NONE // Enum contains neither a negative or a high unsigned value, so can go either way
|
||||
}
|
|
@ -107,4 +107,50 @@ public interface Enum extends DataType {
|
|||
* @return formatted integer string
|
||||
*/
|
||||
public String getRepresentation(BigInteger bigInt, Settings settings, int bitLength);
|
||||
|
||||
/**
|
||||
* Returns true if this enum has an entry with the given name.
|
||||
* @param name the name to check for an entry
|
||||
* @return true if this enum has an entry with the given name
|
||||
*/
|
||||
public boolean contains(String name);
|
||||
|
||||
/**
|
||||
* Returns true if this enum has an entry with the given value.
|
||||
* @param value the value to check for an entry
|
||||
* @return true if this enum has an entry with the given value
|
||||
*/
|
||||
public boolean contains(long value);
|
||||
|
||||
/**
|
||||
* Returns true if the enum contains at least one negative value. Internally, enums have
|
||||
* three states, signed, unsigned, and none (can't tell from the values). If any of
|
||||
* the values are negative, the enum is considered signed. If any of the values are large
|
||||
* unsigned values (upper bit set), then it is considered unsigned. This method will return
|
||||
* true if the enum is signed, and false if it is either unsigned or none (meaning that it
|
||||
* doesn't matter for the values that are contained in the enum.
|
||||
* @return true if the enum contains at least one negative value
|
||||
*/
|
||||
public boolean isSigned();
|
||||
|
||||
/**
|
||||
* Returns the maximum value that this enum can represent based on its size and signedness.
|
||||
* @return the maximum value that this enum can represent based on its size and signedness.
|
||||
*/
|
||||
public long getMaxPossibleValue();
|
||||
|
||||
/**
|
||||
* Returns the maximum value that this enum can represent based on its size and signedness.
|
||||
* @return the maximum value that this enum can represent based on its size and signedness.
|
||||
*/
|
||||
public long getMinPossibleValue();
|
||||
|
||||
/**
|
||||
* Returns the smallest length (size in bytes) this enum can be and still represent all of
|
||||
* it's current values. Note that that this will only return powers of 2 (1,2,4, or 8)
|
||||
* @return the smallest length (size in bytes) this enum can be and still represent all of
|
||||
* it's current values
|
||||
*/
|
||||
public int getMinimumPossibleLength();
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import static ghidra.program.database.data.EnumSignedState.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -23,6 +25,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.docking.settings.SettingsDefinition;
|
||||
import ghidra.program.database.data.DataTypeUtilities;
|
||||
import ghidra.program.database.data.EnumSignedState;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
|
@ -34,11 +37,12 @@ public class EnumDataType extends GenericDataType implements Enum {
|
|||
new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
|
||||
|
||||
private Map<String, Long> nameMap; // name to value
|
||||
private TreeMap<Long, List<String>> valueMap; // value to names
|
||||
private Map<String, String> commentMap; // name to comment
|
||||
private SortedMap<Long, List<String>> valueMap; // value to names
|
||||
private int length;
|
||||
private String description;
|
||||
private List<BitGroup> bitGroups;
|
||||
private EnumSignedState signedState = NONE;
|
||||
|
||||
public EnumDataType(String name, int length) {
|
||||
this(CategoryPath.ROOT, name, length, null);
|
||||
|
@ -157,9 +161,27 @@ public class EnumDataType extends GenericDataType implements Enum {
|
|||
if (!StringUtils.isBlank(comment)) {
|
||||
commentMap.put(valueName, comment);
|
||||
}
|
||||
signedState = computeSignedness();
|
||||
|
||||
}
|
||||
|
||||
private EnumSignedState computeSignedness() {
|
||||
if (valueMap.isEmpty()) {
|
||||
return NONE;
|
||||
}
|
||||
long minValue = valueMap.firstKey();
|
||||
long maxValue = valueMap.lastKey();
|
||||
|
||||
if (minValue < 0) {
|
||||
return SIGNED;
|
||||
}
|
||||
if (maxValue > getMaxPossibleValue(length, true)) {
|
||||
return UNSIGNED;
|
||||
}
|
||||
|
||||
return NONE; // we have no negatives and no large unsigned values
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String valueName) {
|
||||
bitGroups = null;
|
||||
|
@ -182,6 +204,7 @@ public class EnumDataType extends GenericDataType implements Enum {
|
|||
}
|
||||
|
||||
commentMap.remove(valueName);
|
||||
signedState = computeSignedness();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -220,50 +243,90 @@ public class EnumDataType extends GenericDataType implements Enum {
|
|||
if (newLength == length) {
|
||||
return;
|
||||
}
|
||||
if (newLength < 1 || newLength > 8) {
|
||||
throw new IllegalArgumentException("Enum length must be between 1 and 8 inclusive");
|
||||
}
|
||||
|
||||
checkValues(newLength);
|
||||
int minLength = getMinimumPossibleLength();
|
||||
if (newLength < minLength || newLength > 8) {
|
||||
throw new IllegalArgumentException(
|
||||
"Enum length must be between " + minLength + "and 8 inclusive");
|
||||
|
||||
}
|
||||
this.length = newLength;
|
||||
}
|
||||
|
||||
private void checkValues(int newLength) {
|
||||
if (newLength == 8) {
|
||||
return; // all long values permitted
|
||||
}
|
||||
|
||||
long newMaxValue = getMaxEnumValue(newLength);
|
||||
String[] names = getNames();
|
||||
for (String valueName : names) {
|
||||
long value = getValue(valueName);
|
||||
if (value > newMaxValue) {
|
||||
throw new IllegalArgumentException("Setting the length of this Enum to a size " +
|
||||
"that cannot contain the current value for \"" + valueName + "\" of 0x" +
|
||||
Long.toHexString(value) + "\nOld length: " + length + "; new length: " +
|
||||
newLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkValue(long value) {
|
||||
if (length == 8) {
|
||||
return; // all long values permitted
|
||||
}
|
||||
|
||||
long max = getMaxEnumValue(length);
|
||||
if (value > max) {
|
||||
long min = getMinPossibleValue();
|
||||
long max = getMaxPossibleValue();
|
||||
if (value < min || value > max) {
|
||||
throw new IllegalArgumentException(
|
||||
getName() + " enum value 0x" + Long.toHexString(value) +
|
||||
" is outside the range of 0x0 to 0x" + Long.toHexString(max));
|
||||
|
||||
"Attempted to add a value outside the range for this enum: (" + min + ", " + max +
|
||||
"): " + value);
|
||||
}
|
||||
}
|
||||
|
||||
private long getMaxEnumValue(int bytes) {
|
||||
int bits = bytes * 8; // number of bits used for the given size
|
||||
long power2 = 1L << bits; // 2^length is the number of values that 'bytes' can represent
|
||||
return power2 - 1; // max value is always 1 less than 2^length (0-based)
|
||||
@Override
|
||||
public boolean isSigned() {
|
||||
return signedState == SIGNED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinPossibleValue() {
|
||||
return getMinPossibleValue(length, signedState != UNSIGNED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxPossibleValue() {
|
||||
return getMaxPossibleValue(length, signedState == SIGNED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumPossibleLength() {
|
||||
if (valueMap.isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
long minValue = valueMap.firstKey();
|
||||
long maxValue = valueMap.lastKey();
|
||||
boolean hasNegativeValues = minValue < 0;
|
||||
|
||||
// check the min and max values in this enum to see if they fit in 1 byte enum, then
|
||||
// 2 byte enum, then 4 byte enum. If the min min and max values fit, then all other values
|
||||
// will fit as well
|
||||
for (int size = 1; size < 8; size *= 2) {
|
||||
long minPossible = getMinPossibleValue(size, hasNegativeValues);
|
||||
long maxPossible = getMaxPossibleValue(size, hasNegativeValues);
|
||||
if (minValue >= minPossible && maxValue <= maxPossible) {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
|
||||
private long getMaxPossibleValue(int bytes, boolean allowNegativeValues) {
|
||||
if (bytes == 8) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
int bits = bytes * 8;
|
||||
if (allowNegativeValues) {
|
||||
bits -= 1; // take away 1 bit for the sign
|
||||
}
|
||||
|
||||
// the largest value that can be held in n bits in 2^n -1
|
||||
return (1L << bits) - 1;
|
||||
}
|
||||
|
||||
private long getMinPossibleValue(int bytes, boolean allowNegativeValues) {
|
||||
if (!allowNegativeValues) {
|
||||
return 0;
|
||||
}
|
||||
int bits = bytes * 8;
|
||||
|
||||
// smallest value (largest negative) that can be stored in n bits is when the sign bit
|
||||
// is on (and sign extended), and all less significant bits are 0
|
||||
return -1L << (bits - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -438,10 +501,30 @@ public class EnumDataType extends GenericDataType implements Enum {
|
|||
for (String valueName : names) {
|
||||
add(valueName, enumm.getValue(valueName), enumm.getComment(valueName));
|
||||
}
|
||||
computeSignedness();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultLabelPrefix() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String entryName) {
|
||||
return nameMap.containsKey(entryName);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(long value) {
|
||||
return valueMap.containsKey(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this enum to it smallest (power of 2) size that it can be and still represent all its
|
||||
* current values.
|
||||
*/
|
||||
public void pack() {
|
||||
setLength(getMinimumPossibleLength());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -52,6 +54,244 @@ public class EnumDataTypeTest {
|
|||
BigEndianDataConverter.INSTANCE.getBytes(Integer.MIN_VALUE), true);
|
||||
|
||||
Assert.assertEquals("bob", enumDt.getRepresentation(memBuffer, null, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAddHighUnsignedValue() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 1);
|
||||
enumDt.add("a", 0xff);
|
||||
assertEquals(0xff, enumDt.getValue("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAddNegativeValue() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 1);
|
||||
enumDt.add("a", -1);
|
||||
assertEquals(-1, enumDt.getValue("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCantAddNegativeAndHighUnsignedValue() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 1);
|
||||
enumDt.add("a", -1);
|
||||
try {
|
||||
enumDt.add("b", 0xff);
|
||||
fail("Expected Illegal ArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCantAddHighUnsignedAndNegativeValue() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 1);
|
||||
enumDt.add("a", 0xff);
|
||||
try {
|
||||
enumDt.add("b", -1);
|
||||
fail("Expected Illegal ArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAddNegativeAfterAddingThenRemovingHighUnsignedValue() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 1);
|
||||
enumDt.add("a", 0xff);
|
||||
enumDt.remove("a");
|
||||
enumDt.add("a", -1);
|
||||
assertEquals(-1, enumDt.getValue("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinPossibleWidthUnsigned() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 4);
|
||||
assertEquals(1, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("a", 0);
|
||||
assertEquals(1, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("b", 0xff);
|
||||
assertEquals(1, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("c", 0x100);
|
||||
assertEquals(2, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("d", 0xffff);
|
||||
assertEquals(2, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("e", 0x1ffff);
|
||||
assertEquals(4, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("f", 0xffffffffL);
|
||||
assertEquals(4, enumDt.getMinimumPossibleLength());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinPossibleLengthFor8ByteEnum() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 8);
|
||||
enumDt.add("a", -1);
|
||||
assertEquals(1, enumDt.getMinimumPossibleLength());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test8ByteEnums() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 8);
|
||||
enumDt.add("a", 0);
|
||||
assertEquals(Long.MIN_VALUE, enumDt.getMinPossibleValue());
|
||||
assertEquals(Long.MAX_VALUE, enumDt.getMaxPossibleValue());
|
||||
assertFalse(enumDt.isSigned());
|
||||
enumDt.add("b", -1);
|
||||
assertTrue(enumDt.isSigned());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinPossibleWidthSigned() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 4);
|
||||
assertEquals(1, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("a", 0);
|
||||
assertEquals(1, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("b", -0x1);
|
||||
assertEquals(1, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("c", 0xff);
|
||||
assertEquals(2, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("d", 0xffff);
|
||||
assertEquals(4, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("e", 0x1ffff);
|
||||
assertEquals(4, enumDt.getMinimumPossibleLength());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinPossibleWidthSignedWithBigNegatives() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 4);
|
||||
assertEquals(1, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("a", 0);
|
||||
assertEquals(1, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("b", -0x1);
|
||||
assertEquals(1, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("c", -0xff);
|
||||
assertEquals(2, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("d", -0xffff);
|
||||
assertEquals(4, enumDt.getMinimumPossibleLength());
|
||||
|
||||
enumDt.add("e", -0x1ffff);
|
||||
assertEquals(4, enumDt.getMinimumPossibleLength());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesNoSignednessSize1() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 1);
|
||||
assertEquals(-0x80, enumDt.getMinPossibleValue());
|
||||
assertEquals(0xff, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesNoSignednessSize2() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 2);
|
||||
assertEquals(-0x8000, enumDt.getMinPossibleValue());
|
||||
assertEquals(0xffff, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesNoSignednessSize4() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 4);
|
||||
assertEquals(-0x80000000L, enumDt.getMinPossibleValue());
|
||||
assertEquals(0xffffffffL, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesNoSignednessSize8() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 8);
|
||||
assertEquals(Long.MIN_VALUE, enumDt.getMinPossibleValue());
|
||||
assertEquals(Long.MAX_VALUE, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
public void testMinMaxPossibleValuesSignedSize1() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 1);
|
||||
enumDt.add("a", -1); // this makes it an signed enum
|
||||
assertEquals(-0x80, enumDt.getMinPossibleValue());
|
||||
assertEquals(0x7f, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesSignedSize2() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 2);
|
||||
enumDt.add("a", -1); // this makes it an signed enum
|
||||
assertEquals(-0x8000, enumDt.getMinPossibleValue());
|
||||
assertEquals(0x7fff, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesSignedSize4() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 4);
|
||||
enumDt.add("a", -1); // this makes it an signed enum
|
||||
assertEquals(-0x80000000L, enumDt.getMinPossibleValue());
|
||||
assertEquals(0x7fffffffL, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesSignedSize8() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 8);
|
||||
enumDt.add("a", -1); // this makes it an signed enum
|
||||
assertEquals(Long.MIN_VALUE, enumDt.getMinPossibleValue());
|
||||
assertEquals(Long.MAX_VALUE, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
public void testMinMaxPossibleValuesUnsignedSize1() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 1);
|
||||
enumDt.add("a", 0xff); // this makes it an unsigned enum
|
||||
assertEquals(0, enumDt.getMinPossibleValue());
|
||||
assertEquals(0xff, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesUnsignedSize2() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 2);
|
||||
enumDt.add("a", 0xffff); // this makes it an signed enum
|
||||
assertEquals(0, enumDt.getMinPossibleValue());
|
||||
assertEquals(0xffff, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxPossibleValuesUnsignedSize4() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 4);
|
||||
enumDt.add("a", 0xffffffffL); // this makes it an signed enum
|
||||
assertEquals(0, enumDt.getMinPossibleValue());
|
||||
assertEquals(0xffffffffL, enumDt.getMaxPossibleValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsName() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 4);
|
||||
enumDt.add("a", 0x1);
|
||||
enumDt.add("b", -1);
|
||||
assertTrue(enumDt.contains("a"));
|
||||
assertTrue(enumDt.contains("b"));
|
||||
assertFalse(enumDt.contains("c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsValue() {
|
||||
EnumDataType enumDt = new EnumDataType("Test", 4);
|
||||
enumDt.add("a", 0x1);
|
||||
enumDt.add("b", -1);
|
||||
assertTrue(enumDt.contains(-1));
|
||||
assertTrue(enumDt.contains(1));
|
||||
assertFalse(enumDt.contains(3));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue