Merge remote-tracking branch 'origin/patch'

This commit is contained in:
Ryan Kurtz
2026-02-02 05:37:00 -05:00
12 changed files with 654 additions and 184 deletions
@@ -528,7 +528,7 @@ public class DataTypeMergeManager implements MergeResolver {
}
sb.append(dt.getDisplayName());
sb.append(", ");
if (info.index < 0) {
if (info.resultOrdinal < 0) {
if (dt instanceof FunctionDefinition) {
sb.append("return-type");
}
@@ -539,16 +539,16 @@ public class DataTypeMergeManager implements MergeResolver {
else {
if (dt instanceof FunctionDefinition) {
sb.append("param-");
sb.append(info.index);
sb.append(info.resultOrdinal);
}
else if (dt instanceof Union) {
sb.append("component-");
sb.append(info.index);
sb.append(info.resultOrdinal);
}
else if (dt instanceof Structure) {
Structure resultStruct = (Structure) info.ht.get(info.id);
sb.append("component-");
sb.append(info.index);
sb.append(info.resultOrdinal);
if (!resultStruct.isPackingEnabled()) {
sb.append(", offset-");
sb.append("0x");
@@ -556,8 +556,8 @@ public class DataTypeMergeManager implements MergeResolver {
}
}
else {
sb.append("index-"); // unknown use case
sb.append(info.index);
sb.append("resultOrdinal-"); // unknown use case
sb.append(info.resultOrdinal);
}
}
@@ -1254,36 +1254,19 @@ public class DataTypeMergeManager implements MergeResolver {
// Add each of the defined components back in.
DataTypeComponent[] comps = sourceDt.getDefinedComponents();
// Component sequence must be flipped for components that share the same offset
// (i.e., when zero-length components exist) to ensure that the insert at offset
// does not alter the intended order.
List<DataTypeComponent> compList = new ArrayList<>();
int prevOffset = -1;
int index = -1;
for (DataTypeComponent dtc : comps) {
int offset = dtc.getOffset();
if (offset == prevOffset) {
compList.add(index, dtc);
}
else {
prevOffset = offset;
index = compList.size();
compList.add(dtc);
}
}
comps = null; // prevent improper use
// Track dependency errors to avoid duplicate popups
HashMap<Long, String> badIdDtMsgs = new HashMap<>();
for (int i = 0; i < compList.size(); i++) {
for (int i = 0; i < comps.length; i++) {
DataTypeComponent sourceComp = compList.get(i);
DataTypeComponent sourceComp = comps[i];
DataTypeComponent resultComp;
DataType sourceCompDt = sourceComp.getDataType();
BitFieldDataType bfDt = null;
String comment = sourceComp.getComment();
DataType resultCompDt = null;
boolean fixupRequired = false;
if (sourceComp.isBitFieldComponent()) {
// NOTE: primitive type will be used if unable to resolve base type
@@ -1307,13 +1290,7 @@ public class DataTypeMergeManager implements MergeResolver {
// Not added so should be in result if it wasn't deleted there.
resultCompDt = dtms[RESULT].getDataType(sourceComponentID);
}
if (resultCompDt == null) {
// Not added/resolved yet
// put an entry in the fixup list
fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID,
sourceComp.getOrdinal(), sourceComp, resolvedDataTypes));
fixUpIDSet.add(sourceDtID);
}
fixupRequired = (resultCompDt == null);
if (bfDt != null &&
(resultCompDt == null || !BitFieldDataType.isValidBaseDataType(resultCompDt))) {
// use primitive type as fixup placeholder
@@ -1335,7 +1312,8 @@ public class DataTypeMergeManager implements MergeResolver {
if (packed) {
if (bfDt != null) {
try {
destStruct.addBitField(resultCompDt, bfDt.getDeclaredBitSize(),
resultComp =
destStruct.addBitField(resultCompDt, bfDt.getDeclaredBitSize(),
sourceComp.getFieldName(), comment);
}
catch (InvalidDataTypeException e) {
@@ -1345,7 +1323,8 @@ public class DataTypeMergeManager implements MergeResolver {
comment = buildDataTypeFailureComment(sourceCompDt, e.getMessage(),
sourceComp.getComment());
try {
destStruct.addBitField(primitiveBaseDt, bfDt.getDeclaredBitSize(),
resultComp = destStruct.addBitField(primitiveBaseDt,
bfDt.getDeclaredBitSize(),
sourceComp.getFieldName(), comment);
}
catch (InvalidDataTypeException exc) {
@@ -1356,13 +1335,15 @@ public class DataTypeMergeManager implements MergeResolver {
else if (badMsg == null) {
try {
// If I have compDt, it should now be from result DTM.
destStruct.add(resultCompDt, length, sourceComp.getFieldName(),
resultComp =
destStruct.add(resultCompDt, length, sourceComp.getFieldName(),
comment);
}
catch (IllegalArgumentException e) {
comment =
buildDataTypeFailureComment(sourceCompDt, e.getMessage(), comment);
destStruct.add(BadDataType.dataType, sourceComp.getLength(),
resultComp =
destStruct.add(BadDataType.dataType, sourceComp.getLength(),
sourceComp.getFieldName(), comment);
if (e.getCause() instanceof DataTypeDependencyException) {
badIdDtMsgs.put(dtId, e.getMessage());
@@ -1373,14 +1354,15 @@ public class DataTypeMergeManager implements MergeResolver {
else {
// Preserve non-fixup ordinal with bad placeholder
comment = buildDataTypeFailureComment(sourceCompDt, badMsg, comment);
destStruct.add(BadDataType.dataType, sourceComp.getLength(),
resultComp = destStruct.add(BadDataType.dataType, sourceComp.getLength(),
sourceComp.getFieldName(), badMsg + "; " + comment);
}
}
else if (bfDt != null) {
// non-packed bitfield
try {
destStruct.insertBitFieldAt(sourceComp.getOffset(), sourceComp.getLength(),
resultComp = destStruct
.insertBitFieldAt(sourceComp.getOffset(), sourceComp.getLength(),
bfDt.getBitOffset(), resultCompDt, bfDt.getDeclaredBitSize(),
sourceComp.getFieldName(), comment);
}
@@ -1391,7 +1373,8 @@ public class DataTypeMergeManager implements MergeResolver {
comment = buildDataTypeFailureComment(sourceCompDt, e.getMessage(),
sourceComp.getComment());
try {
destStruct.addBitField(primitiveBaseDt, bfDt.getDeclaredBitSize(),
resultComp =
destStruct.addBitField(primitiveBaseDt, bfDt.getDeclaredBitSize(),
sourceComp.getFieldName(), comment);
}
catch (InvalidDataTypeException exc) {
@@ -1404,9 +1387,9 @@ public class DataTypeMergeManager implements MergeResolver {
if (badMsg == null) {
try {
// If not last component must constrain length to original component size
if (i < compList.size() - 1) {
if (i < (comps.length - 1)) {
int offset = sourceComp.getOffset();
DataTypeComponent nextDtc = compList.get(i + 1);
DataTypeComponent nextDtc = comps[i + 1];
int available = nextDtc.getOffset() - offset;
if (length > available) {
// The data type is too big, so adjust the component length to what will fit.
@@ -1423,13 +1406,15 @@ public class DataTypeMergeManager implements MergeResolver {
}
}
destStruct.insertAtOffset(sourceComp.getOffset(), resultCompDt, length,
resultComp = destStruct.insertAtOffset(sourceComp.getOffset(),
resultCompDt, length,
sourceComp.getFieldName(), comment);
}
catch (IllegalArgumentException e) {
comment =
buildDataTypeFailureComment(sourceCompDt, e.getMessage(), comment);
destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
resultComp = destStruct.insertAtOffset(sourceComp.getOffset(),
BadDataType.dataType,
length, sourceComp.getFieldName(), comment);
if (e.getCause() instanceof DataTypeDependencyException) {
@@ -1441,7 +1426,8 @@ public class DataTypeMergeManager implements MergeResolver {
else {
// Preserve non-fixup ordinal with bad placeholder
comment = buildDataTypeFailureComment(sourceCompDt, badMsg, comment);
destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
resultComp =
destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
sourceComp.getLength(), sourceComp.getFieldName(), comment);
}
}
@@ -1450,15 +1436,22 @@ public class DataTypeMergeManager implements MergeResolver {
// Add fixup placeholder to prevent the ordinal values and component sizes from
// changing. Nothing we can do about packing which may be affected.
// These should get fixed-up later.
destStruct.add(BadDataType.dataType, sourceComp.getLength(),
resultComp = destStruct.add(BadDataType.dataType, sourceComp.getLength(),
sourceComp.getFieldName(), comment);
}
else {
// Add fixup placeholder to prevent the ordinal values and component sizes from
// changing. These should get fixed-up later.
destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
resultComp = destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
sourceComp.getLength(), sourceComp.getFieldName(), comment);
}
if (fixupRequired) {
// Component datatype has not been added/resolved yet, put an entry in the fixup list
fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID,
resultComp.getOrdinal(), sourceComp, resolvedDataTypes));
fixUpIDSet.add(sourceDtID);
}
}
if (!packed) {
adjustStructureSize(destStruct, sourceDt.getLength());
@@ -2548,22 +2541,22 @@ public class DataTypeMergeManager implements MergeResolver {
long lastChangeTime = fd.getLastChangeTime(); // Don't let the time change.
try {
if (dt != null) {
if (info.index < 0) { // -1 for return type
if (info.resultOrdinal < 0) { // -1 for return type
fd.setReturnType(dt);
}
else {
ParameterDefinition[] args = fd.getArguments();
args[info.index].setDataType(dt);
args[info.resultOrdinal].setDataType(dt);
}
return true;
}
if (info.index < 0) { // -1 for return type
if (info.resultOrdinal < 0) { // -1 for return type
// nowhere to set error comment
}
else {
ParameterDefinition[] args = fd.getArguments();
ParameterDefinition arg = args[info.index];
ParameterDefinition arg = args[info.resultOrdinal];
String comment =
buildDataTypeFailureComment(info.componentDataType, null, arg.getComment());
arg.setComment(comment);
@@ -2583,15 +2576,15 @@ public class DataTypeMergeManager implements MergeResolver {
* @return false if component not found, else true
*/
private boolean fixUpPackedStructureComponent(FixUpInfo info, Structure struct, DataType dt) {
int ordinal = info.index;
int ordinal = info.resultOrdinal;
DataTypeComponent dtc = null;
DataTypeComponent dtc;
if (ordinal >= 0 || ordinal < struct.getNumComponents()) {
dtc = struct.getComponent(ordinal);
}
if (dtc == null) {
throw new AssertException("Expected bad datatype placeholder");
else {
throw new AssertException(
"Expected fixup component at ordinal " + ordinal + " in " + struct.getPathName());
}
long lastChangeTime = struct.getLastChangeTime(); // Don't let the time change.
@@ -2680,12 +2673,16 @@ public class DataTypeMergeManager implements MergeResolver {
private boolean fixUpNonPackedStructureComponent(FixUpInfo info, Structure struct,
DataType dt) {
int ordinal = info.index;
int ordinal = info.resultOrdinal;
DataTypeComponent dtc = null;
DataTypeComponent dtc;
if (ordinal >= 0 || ordinal < struct.getNumComponents()) {
dtc = struct.getComponent(ordinal);
}
else {
throw new AssertException(
"Expected fixup component at ordinal " + ordinal + " in " + struct.getPathName());
}
long lastChangeTime = struct.getLastChangeTime(); // Don't let the time change.
try {
@@ -2740,35 +2737,13 @@ public class DataTypeMergeManager implements MergeResolver {
}
// handle non-bitfield component fixup
int offset = dtc.getOffset();
int dtcLength = dtc.getLength();
int length = dt.getLength();
if (length <= 0) {
length = dtcLength;
}
int bytesNeeded = length - dtcLength;
if (bytesNeeded > 0) {
int nextOffset = offset + dtcLength;
DataTypeComponent nextDefinedDtc =
struct.getDefinedComponentAtOrAfterOffset(nextOffset);
if (nextDefinedDtc != null) {
int bytesAvailable = nextDefinedDtc.getOffset() - nextOffset;
if (bytesAvailable < bytesNeeded) {
// The data type is too big, so adjust the component length to what will fit.
length = dtcLength + bytesAvailable;
// Output a warning indicating the structure has a data type that doesn't fit.
String message = "Structure Merge: Not enough undefined bytes to fit " +
dt.getPathName() + " in structure " + struct.getPathName() +
" at offset 0x" + Integer.toHexString(offset) + "." +
"\nIt needs " + (bytesNeeded - bytesAvailable) +
" more byte(s) to be able to fit.";
Msg.warn(this, message);
}
}
length = dtc.getLength();
}
try {
struct.replaceAtOffset(offset, dt, length, dtc.getFieldName(),
dtc.getComment());
struct.replace(ordinal, dt, length, dtc.getFieldName(), dtc.getComment());
return true;
}
catch (IllegalArgumentException e) {
displayError(struct, e);
@@ -2804,7 +2779,7 @@ public class DataTypeMergeManager implements MergeResolver {
String loc;
if (struct.isPackingEnabled()) {
loc = "ordinal " + info.index;
loc = "ordinal " + info.resultOrdinal;
}
else {
loc = "offset 0x" + Integer.toHexString(info.offset);
@@ -2818,7 +2793,7 @@ public class DataTypeMergeManager implements MergeResolver {
DataType compDt = resolve(info.compID, info.getDataTypeManager(), info.ht);
int ordinal = info.index;
int ordinal = info.resultOrdinal;
DataTypeComponent dtc = null;
if (ordinal >= 0 && ordinal <= union.getNumComponents()) {
@@ -2889,7 +2864,7 @@ public class DataTypeMergeManager implements MergeResolver {
}
Msg.warn(this,
"Union Merge: Failed to resolve data type '" + info.componentDataType.getName() +
"' at ordinal " + info.index + " in " + union.getPathName());
"' at ordinal " + info.resultOrdinal + " in " + union.getPathName());
return false;
}
@@ -2913,7 +2888,7 @@ public class DataTypeMergeManager implements MergeResolver {
FixUpInfo info = fixUpList.get(i); // assume info applies to union
if (!fixUpUnionComponent(union, info)) {
Msg.warn(this, "Union Merge: Failed to apply data type at ordinal " +
info.index + " in " + union.getPathName());
info.resultOrdinal + " in " + union.getPathName());
unresolvedFixups.add(info);
}
}
@@ -3447,13 +3422,18 @@ public class DataTypeMergeManager implements MergeResolver {
*/
private class FixUpInfo implements Comparable<FixUpInfo> {
final long id;
final long compID;
final DataType componentDataType;
final int index;
final long id; // Souce datatype ID
final long compID; // Source datatype ID for component
final DataType componentDataType; // Source component datatype
// Result ordinal when id represents a container such as a structure/union or
// function definition. In such cases, 'compID' corresponds to the component datatype.
// A -1 may be used when not applicable.
final int resultOrdinal;
final MyIdentityHashMap<Long, DataType> ht;
// component offset - for display/logging use only
// source component offset - for display/logging use only
// only meaningful for non-packed structure
// may not be unique (e.g., bitfields, 0-length components)
int offset = -1;
@@ -3468,16 +3448,16 @@ public class DataTypeMergeManager implements MergeResolver {
* @param id source data type ID needing to be fixed up
* @param compID source datatype ID of either param/component or bitfield base type
* @param componentDataType source component/dependency datatype
* @param index offset into non-packed structure, or ordinal into union or packed
* structure; or parameter/return ordinal; for other data types index is not used (specify -1).
* @param resultOrdinal the result ordinal into a structure/union; or
* parameter/return ordinal; or -1 for other cases where index is not used
* @param resolvedDataTypes hashtable used for resolving the data type
*/
FixUpInfo(long id, long compID, DataType componentDataType, int index,
FixUpInfo(long id, long compID, DataType componentDataType, int resultOrdinal,
MyIdentityHashMap<Long, DataType> resolvedDataTypes) {
this.id = id;
this.compID = compID;
this.componentDataType = componentDataType;
this.index = index;
this.resultOrdinal = resultOrdinal;
this.ht = resolvedDataTypes;
if (componentDataType instanceof BitFieldDataType) {
@@ -3491,13 +3471,13 @@ public class DataTypeMergeManager implements MergeResolver {
* or components were resolved.
* @param id id of data type needing to be fixed up
* @param compID datatype ID of either param/component or bitfield base type
* @param destOrdinal component ordinal within destination composite
* @param resultOrdinal component ordinal within result composite
* @param sourceDtc associated composite datatype component
* @param resolvedDataTypes hashtable used for resolving the data type
*/
FixUpInfo(long id, long compID, int destOrdinal, DataTypeComponent sourceDtc,
FixUpInfo(long id, long compID, int resultOrdinal, DataTypeComponent sourceDtc,
MyIdentityHashMap<Long, DataType> resolvedDataTypes) {
this(id, compID, getDataType(sourceDtc), destOrdinal, resolvedDataTypes);
this(id, compID, getDataType(sourceDtc), resultOrdinal, resolvedDataTypes);
offset = sourceDtc.getOffset();
if (sourceDtc.isBitFieldComponent()) {
BitFieldDataType bfDt = (BitFieldDataType) sourceDtc.getDataType();
@@ -3516,11 +3496,11 @@ public class DataTypeMergeManager implements MergeResolver {
@Override
public int compareTo(FixUpInfo o) {
// Compare such that items are grouped by id and sort such that the greatest index
// Compare such that items are grouped by id and sort such that the greatest resultOrdinal
// is first within that group.
long c = id - o.id;
if (c == 0) {
c = Integer.toUnsignedLong(o.index) - Integer.toUnsignedLong(index);
c = Integer.toUnsignedLong(o.resultOrdinal) - Integer.toUnsignedLong(resultOrdinal);
}
if (c == 0) {
return 0;
@@ -3546,7 +3526,8 @@ public class DataTypeMergeManager implements MergeResolver {
}
return "\n" + "ID = " + Long.toHexString(id) + ",\ndt = " + dtm.getDataType(id) +
",\ncomponent ID = " + Long.toHexString(compID) + ",\ncomponent dt = " +
dtm.getDataType(compID) + ",\noffset/index = " + index + ",\n" + bitInfo + "ht = " +
dtm.getDataType(compID) + ",\nresultOrdinal = " + resultOrdinal + ",\n" + bitInfo +
"ht = " +
htStr + "\n";
}
@@ -115,6 +115,8 @@ public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {
// choose MY for Bar conflict
chooseOption(DataTypeMergeManager.OPTION_MY);
pressButtonByName(waitForWindow("Structure Update Failed"), "OK"); // expected dependency error on ABC
waitForCompletion();
FrontEndPlugin frontEndPlugin = getPlugin(frontEndTool, FrontEndPlugin.class);
@@ -122,8 +124,9 @@ public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {
JLabel label = (JLabel) TestUtils.getInstanceField("label", logPanel);
String statusText = label.getText();
String expectedText =
"Structure Merge: Not enough undefined bytes to fit /XYZ in structure " +
"/MISC/ABC at offset 0x4. It needs 3 more byte(s) to be able to fit.";
"Structure Update Failed: Some of your changes to ABC cannot be merged. " +
"Problem: Not enough undefined bytes to fit /XYZ in structure /MISC/ABC at " +
"offset 0x4. It needs 3 more byte(s) to be able to fit.";
assertTrue("Wrong status text: " + statusText, statusText.contains(expectedText));
}
}
@@ -367,4 +367,85 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest {
//@formatter:on
}
@Test
public void testNonPackedZeroLengthComponentFixup() throws Exception {
// Goal is to fixup zero-length component at end of structure where its ordinal will
// be revised during the merge processing
final CategoryPath rootPath = new CategoryPath("/");
mtf.initialize("notepad", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
DataTypeManager dtm = program.getDataTypeManager();
Union inner = new UnionDataType("inner");
inner.add(DWordDataType.dataType);
inner = (Union) dtm.addDataType(inner, null);
Structure other = new StructureDataType("other", 0);
other.add(WordDataType.dataType);
other = (Structure) dtm.addDataType(other, null);
Structure outer = new StructureDataType("outer", 20, dtm);
outer.replaceAtOffset(0, other, -1, null, null); // prevent size change
outer = (Structure) dtm.addDataType(outer, null);
assertEquals(20, outer.getLength());
}
@Override
public void modifyLatest(ProgramDB program) throws Exception {
DataTypeManager dtm = program.getDataTypeManager();
// Increase size of other struct
Structure other = (Structure) dtm.getDataType(rootPath, "other");
other.add(DWordDataType.dataType);
// remove inner to trigger conflict with its modification
Union inner = (Union) dtm.getDataType(rootPath, "inner");
dtm.remove(inner);
}
@Override
public void modifyPrivate(ProgramDB program) throws Exception {
DataTypeManager dtm = program.getDataTypeManager();
Structure outer = (Structure) dtm.getDataType(rootPath, "outer");
Union inner = (Union) dtm.getDataType(rootPath, "inner");
// change inner to trigger conflict with its removal
inner.add(WordDataType.dataType);
// Add zero-length array at end of struct
outer.insertAtOffset(20, new ArrayDataType(inner, 0), -1);
assertEquals(20, outer.getLength());
}
});
executeMerge();
chooseOption(DataTypeMergeManager.OPTION_MY); // resolve inner conflict
chooseOption(DataTypeMergeManager.OPTION_MY); // resolve outer conflict
waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
StructureInternal outer = (StructureInternal) dtm.getDataType(rootPath, "outer");
assertNotNull(outer);
//@formatter:off
assertEquals("/outer\n" +
"pack(disabled)\n" +
"Structure outer {\n" +
" 0 other 6 \"\"\n" +
" 20 inner[0] 0 \"\"\n" +
"}\n" +
"Length: 20 Alignment: 1\n", outer.toString());
//@formatter:on
}
}
@@ -338,6 +338,74 @@ public class StructureDataTypeTest extends AbstractGenericTest {
assertEquals(ByteDataType.class, comps[2].getDataType().getClass());
}
@Test
public void testInsertAtSameOffset1() {
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
struct = createStructure("Test", 100);
assertFalse(struct.isPackingEnabled());
struct.insertAtOffset(0, zeroDt, -1, "a", "comment a");
struct.insertAtOffset(0, zeroDt, -1, "b", "comment b");
struct.insertAtOffset(0, WordDataType.dataType, -1, "c", "comment c");
DataTypeComponent[] definedComponents = struct.getDefinedComponents();
assertEquals("a", definedComponents[0].getFieldName());
assertEquals("b", definedComponents[1].getFieldName());
assertEquals("c", definedComponents[2].getFieldName());
}
@Test
public void testInsertAtSameOffset2() {
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
struct = createStructure("Test", 100);
assertFalse(struct.isPackingEnabled());
struct.insertAtOffset(0, WordDataType.dataType, -1, "c", "comment c");
struct.insertAtOffset(0, zeroDt, -1, "a", "comment a");
struct.insertAtOffset(0, zeroDt, -1, "b", "comment b");
DataTypeComponent[] definedComponents = struct.getDefinedComponents();
assertEquals("a", definedComponents[0].getFieldName());
assertEquals("b", definedComponents[1].getFieldName());
assertEquals("c", definedComponents[2].getFieldName());
}
@Test
public void testInsertAtEndOffset() {
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
struct = createStructure("Test2", 100);
assertFalse(struct.isPackingEnabled());
struct.insertAtOffset(100, zeroDt, -1, "a", "comment a");
struct.insertAtOffset(100, zeroDt, -1, "b", "comment b");
assertEquals("/Test2\n" +
"pack(disabled)\n" +
"Structure Test2 {\n" +
" 100 byte[0] 0 a \"comment a\"\n" +
" 100 byte[0] 0 b \"comment b\"\n" +
"}\n" +
"Length: 100 Alignment: 1\n", struct.toString());
// Insert non-zero-length component will increase struct size
struct.insertAtOffset(100, WordDataType.dataType, -1, "c", "comment c");
assertEquals("/Test2\n" +
"pack(disabled)\n" +
"Structure Test2 {\n" +
" 100 byte[0] 0 a \"comment a\"\n" +
" 100 byte[0] 0 b \"comment b\"\n" +
" 100 word 2 c \"comment c\"\n" +
"}\n" +
"Length: 102 Alignment: 1\n", struct.toString());
}
// test inserting at offset 0
@Test
public void testInsertAtOffset() {
@@ -458,32 +526,18 @@ public class StructureDataTypeTest extends AbstractGenericTest {
Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1);
struct.insertAtOffset(2, zeroArray, -1);
struct.insertAtOffset(2, FloatDataType.dataType, -1);
assertEquals(13, struct.getLength());
DataTypeComponent[] comps = struct.getDefinedComponents();
assertEquals(6, comps.length);
assertEquals(0, comps[0].getOffset());
assertEquals(0, comps[0].getOrdinal());
assertEquals(ByteDataType.class, comps[0].getDataType().getClass());
assertEquals(2, comps[1].getOffset());
assertEquals(2, comps[1].getOrdinal());
assertEquals(FloatDataType.class, comps[1].getDataType().getClass());
assertEquals(6, comps[2].getOffset());
assertEquals(3, comps[2].getOrdinal());
assertTrue(zeroArray.isEquivalent(comps[2].getDataType()));
assertEquals(6, comps[3].getOffset());
assertEquals(4, comps[3].getOrdinal());
assertEquals(WordDataType.class, comps[3].getDataType().getClass());
assertEquals(8, comps[4].getOffset());
assertEquals(5, comps[4].getOrdinal());
assertEquals(DWordDataType.class, comps[4].getDataType().getClass());
assertEquals("/TestStruct\n" +
"pack(disabled)\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 2 float[0] 0 \"\"\n" +
" 2 float 4 \"\"\n" +
" 6 word 2 \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 13 Alignment: 1\n", struct.toString());
}
@Test
@@ -660,7 +714,15 @@ public class StructureDataTypeTest extends AbstractGenericTest {
}
@Test
public void testInsertBitFieldAtLittleEndian() throws Exception {
public void testInsertBitFieldAtLittleEndian2() throws Exception {
Array zeroArray = new ArrayDataType(CharDataType.dataType, 0, -1);
struct.insertAtOffset(2, zeroArray, -1, "A", null);
struct.insertAtOffset(2, zeroArray, -1, "B", null);
struct.insertAtOffset(2, zeroArray, -1, "C", null);
struct.insertAtOffset(2, zeroArray, -1, "D", null);
struct.insertAtOffset(2, zeroArray, -1, "E", null);
struct.insertAtOffset(2, CharDataType.dataType, -1, "XXX", null);
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
@@ -670,15 +732,21 @@ public class StructureDataTypeTest extends AbstractGenericTest {
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 char[0] 0 A \"\"\n" +
" 2 char[0] 0 B \"\"\n" +
" 2 char[0] 0 C \"\"\n" +
" 2 char[0] 0 D \"\"\n" +
" 2 char[0] 0 E \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
// " 3 undefined 1 \"\"\n" +
// " 4 undefined 1 \"\"\n" +
// " 5 undefined 1 \"\"\n" +
" 6 word 2 \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
// " 5 undefined 1 \"\"\n" +
" 6 char 1 XXX \"\"\n" +
" 7 word 2 \"Comment2\"\n" +
" 9 dword 4 field3 \"\"\n" +
" 13 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 13 Alignment: 1", struct);
"Length: 14 Alignment: 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
@@ -688,17 +756,23 @@ public class StructureDataTypeTest extends AbstractGenericTest {
"pack(disabled)\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 char[0] 0 A \"\"\n" +
" 2 char[0] 0 B \"\"\n" +
" 2 char[0] 0 C \"\"\n" +
" 2 char[0] 0 D \"\"\n" +
" 2 char[0] 0 E \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
// " 3 undefined 1 \"\"\n" +
// " 4 undefined 1 \"\"\n" +
// " 5 undefined 1 \"\"\n" +
" 6 word 2 \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
" 6 char 1 XXX \"\"\n" +
" 7 word 2 \"Comment2\"\n" +
" 9 dword 4 field3 \"\"\n" +
" 13 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 13 Alignment: 1", struct);
"Length: 14 Alignment: 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment");
@@ -708,16 +782,22 @@ public class StructureDataTypeTest extends AbstractGenericTest {
"pack(disabled)\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 char[0] 0 A \"\"\n" +
" 2 char[0] 0 B \"\"\n" +
" 2 char[0] 0 C \"\"\n" +
" 2 char[0] 0 D \"\"\n" +
" 2 char[0] 0 E \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
// " 5 undefined 1 \"\"\n" +
" 6 word 2 \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
" 6 char 1 XXX \"\"\n" +
" 7 word 2 \"Comment2\"\n" +
" 9 dword 4 field3 \"\"\n" +
" 13 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 13 Alignment: 1", struct);
"Length: 14 Alignment: 1", struct);
//@formatter:on
try {
@@ -737,15 +817,47 @@ public class StructureDataTypeTest extends AbstractGenericTest {
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 char[0] 0 A \"\"\n" +
" 2 char[0] 0 B \"\"\n" +
" 2 char[0] 0 C \"\"\n" +
" 2 char[0] 0 D \"\"\n" +
" 2 char[0] 0 E \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
" 4 int:11(5) 2 bf4 \"bf4Comment\"\n" +
" 6 word 2 \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
" 6 char 1 XXX \"\"\n" +
" 7 word 2 \"Comment2\"\n" +
" 9 dword 4 field3 \"\"\n" +
" 13 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 13 Alignment: 1", struct);
"Length: 14 Alignment: 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 0, "z", "zero bitfield");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"pack(disabled)\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 char[0] 0 A \"\"\n" +
" 2 char[0] 0 B \"\"\n" +
" 2 char[0] 0 C \"\"\n" +
" 2 char[0] 0 D \"\"\n" +
" 2 char[0] 0 E \"\"\n" +
" 2 int:0(0) 0 \"zero bitfield\"\n" + // field name discarded
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
" 4 int:11(5) 2 bf4 \"bf4Comment\"\n" +
" 6 char 1 XXX \"\"\n" +
" 7 word 2 \"Comment2\"\n" +
" 9 dword 4 field3 \"\"\n" +
" 13 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 14 Alignment: 1", struct);
//@formatter:on
}
@@ -839,6 +951,26 @@ public class StructureDataTypeTest extends AbstractGenericTest {
"}\n" +
"Length: 13 Alignment: 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 31, IntegerDataType.dataType, 0, "z", "zero bitfield");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"pack(disabled)\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 int:0(7) 0 \"zero bitfield\"\n" + // field name discarded
" 2 int:3(5) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(2) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(3) 3 bf3 \"bf3Comment\"\n" +
" 4 int:11(0) 2 bf4 \"bf4Comment\"\n" +
" 6 word 2 \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 13 Alignment: 1", struct);
//@formatter:on
}
@Test
@@ -30,7 +30,7 @@ import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException;
/**
* Database implementation for a structure or union.
* {@link CompositeDB} provides an abstract database implementation for a structure or union.
*/
abstract class CompositeDB extends DataTypeDB implements CompositeInternal {
@@ -1007,6 +1007,26 @@ class StructureDB extends CompositeDB implements StructureInternal {
return index;
}
/**
* Find insertion index such that the index falls after all zero-length components at the
* specified offset and before any bitfields at that offset.
*
* @param index any defined component index which contains offset.
* @param offset offset within structure.
* @return index of first non-zero-length defined checking in the forward direction.
*/
private int afterNonZeroComponentsAtOffset(int index, int offset) {
int maxIndex = components.size();
while (index < maxIndex) {
DataTypeComponentDB dtc = components.get(index);
if (dtc.getOffset() != offset || dtc.getLength() != 0) {
break;
}
++index;
}
return index;
}
/**
* Identify defined-component index of the first non-zero-length component which contains the
* specified offset. If only zero-length components exist, the last zero-length component which
@@ -1322,14 +1342,20 @@ class StructureDB extends CompositeDB implements StructureInternal {
structLength = offset;
}
// Any component insert at an offset should be placed after any zero-length components
// at the same offset but before any non-zero-length component.
int index = Collections.binarySearch(components, Integer.valueOf(offset),
OffsetComparator.INSTANCE);
int additionalShift = 0;
if (index >= 0) {
index = backupToFirstComponentContainingOffset(index, offset);
DataTypeComponentDB dtc = components.get(index);
additionalShift = offset - dtc.getOffset();
index = afterNonZeroComponentsAtOffset(index, offset);
if (index < components.size()) {
DataTypeComponentDB dtc = components.get(index);
additionalShift = offset - dtc.getOffset();
}
}
else {
index = -index - 1;
@@ -184,6 +184,11 @@ public class BitFieldDataType extends AbstractDataType {
* Get the packing storage size in bytes associated with this bit-field which may be
* larger than the base type associated with the fields original definition.
* Returned value is the same as {@link #getLength()}.
* <p>
* NOTE: Bitfields with a bit-size of zero will report a storage size of 1, although
* {@link #isZeroLength()} will return true. This is consistent with other datatypes which
* support a zero-length {@link DataTypeComponent} such as a zero-element Array.
*
* @return packing storage size in bytes
*/
public int getStorageSize() {
@@ -331,6 +336,9 @@ public class BitFieldDataType extends AbstractDataType {
}
}
/**
* @see #getStorageSize()
*/
@Override
public int getLength() {
return storageSize;
@@ -192,7 +192,7 @@ public interface DataTypeComponent {
if (dataType instanceof Array) {
return true;
}
// assumes undefined types will ultimately have a non-zero length
// assumes not-yet-defined types will ultimately have a non-zero length
return !dataType.isNotYetDefined();
}
return false;
@@ -61,6 +61,9 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
this.fieldName = InternalDataTypeComponent.cleanupFieldName(fieldName);
setDataType(dataType);
setComment(comment);
if (isZeroBitFieldComponent()) {
this.length = 0; // previously stored as 1, force to 0
}
}
/**
@@ -240,9 +240,16 @@ public interface Structure extends Composite {
/**
* Inserts a new datatype at the specified offset into this structure. Inserting a component
* will cause any conflicting components to shift down to the extent necessary to avoid a
* conflict.
* conflict. The overall structure length will always increase when a non-zero-length
* component is inserted. NOTE: bitfields may share an offset with other bitfields and
* zero-length components.
* <p>
* This method does not support bit-field insertions which must use the method
* Any component insert at an offset will be placed after any zero-length components
* at the same offset but before any non-zero-length components. The components which
* fall after the insertion point will have there ordinal incremented and offset
* adjusted as needed.
* <p>
* This method will defer bit-field insertions to the method
* {@link #insertBitFieldAt(int, int, int, DataType, int, String, String)}.
*
* @param offset the byte offset into the structure where the new datatype is to be inserted.
@@ -389,7 +396,8 @@ public interface Structure extends Composite {
/**
* Replaces all components containing the specified byte offset with a new component using the
* specified datatype, length, name and comment. If the offset corresponds to a bit-field
* more than one component may be consumed by this replacement.
* more than one component may be consumed by this replacement. In general, this method
* should not be used to replace bitfield components.
* <p>
* This method may not be used to replace a zero-length component since there may be any number
* of zero-length components at the same offset. If the only defined component(s) at the specified
@@ -523,14 +523,20 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
structLength = offset;
}
// Any component insert at an offset should be placed after any zero-length components
// at the same offset but before any non-zero-length component.
int index = Collections.binarySearch(components, Integer.valueOf(offset),
OffsetComparator.INSTANCE);
int additionalShift = 0;
if (index >= 0) {
index = backupToFirstComponentContainingOffset(index, offset);
DataTypeComponent dtc = components.get(index);
additionalShift = offset - dtc.getOffset();
index = afterNonZeroComponentsAtOffset(index, offset);
if (index < components.size()) {
DataTypeComponentImpl dtc = components.get(index);
additionalShift = offset - dtc.getOffset();
}
}
else {
index = -index - 1;
@@ -898,6 +904,26 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
return index;
}
/**
* Find insertion index such that the index falls after all zero-length components at the
* specified offset and before any bitfields at that offset.
*
* @param index any defined component index which contains offset.
* @param offset offset within structure.
* @return index of first non-zero-length defined checking in the forward direction.
*/
private int afterNonZeroComponentsAtOffset(int index, int offset) {
int maxIndex = components.size();
while (index < maxIndex) {
DataTypeComponentImpl dtc = components.get(index);
if (dtc.getOffset() != offset || dtc.getLength() != 0) {
break;
}
++index;
}
return index;
}
/**
* Identify defined-component index of the first non-zero-length component which contains the specified offset.
* If only zero-length components exist, the last zero-length component which contains the offset will be returned.
@@ -364,6 +364,74 @@ public class StructureDBTest extends AbstractGenericTest {
assertEquals(ByteDataType.class, comps[2].getDataType().getClass());
}
@Test
public void testInsertAtSameOffset1() {
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
struct = createStructure("Test", 100);
assertFalse(struct.isPackingEnabled());
struct.insertAtOffset(0, zeroDt, -1, "a", "comment a");
struct.insertAtOffset(0, zeroDt, -1, "b", "comment b");
struct.insertAtOffset(0, WordDataType.dataType, -1, "c", "comment c");
DataTypeComponentDB[] definedComponents = struct.getDefinedComponents();
assertEquals("a", definedComponents[0].getFieldName());
assertEquals("b", definedComponents[1].getFieldName());
assertEquals("c", definedComponents[2].getFieldName());
}
@Test
public void testInsertAtSameOffset2() {
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
struct = createStructure("Test", 100);
assertFalse(struct.isPackingEnabled());
struct.insertAtOffset(0, WordDataType.dataType, -1, "c", "comment c");
struct.insertAtOffset(0, zeroDt, -1, "a", "comment a");
struct.insertAtOffset(0, zeroDt, -1, "b", "comment b");
DataTypeComponentDB[] definedComponents = struct.getDefinedComponents();
assertEquals("a", definedComponents[0].getFieldName());
assertEquals("b", definedComponents[1].getFieldName());
assertEquals("c", definedComponents[2].getFieldName());
}
@Test
public void testInsertAtEndOffset() {
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
struct = createStructure("Test2", 100);
assertFalse(struct.isPackingEnabled());
struct.insertAtOffset(100, zeroDt, -1, "a", "comment a");
struct.insertAtOffset(100, zeroDt, -1, "b", "comment b");
assertEquals("/Test2\n" +
"pack(disabled)\n" +
"Structure Test2 {\n" +
" 100 byte[0] 0 a \"comment a\"\n" +
" 100 byte[0] 0 b \"comment b\"\n" +
"}\n" +
"Length: 100 Alignment: 1\n", struct.toString());
// Insert non-zero-length component will increase struct size
struct.insertAtOffset(100, WordDataType.dataType, -1, "c", "comment c");
assertEquals("/Test2\n" +
"pack(disabled)\n" +
"Structure Test2 {\n" +
" 100 byte[0] 0 a \"comment a\"\n" +
" 100 byte[0] 0 b \"comment b\"\n" +
" 100 word 2 c \"comment c\"\n" +
"}\n" +
"Length: 102 Alignment: 1\n", struct.toString());
}
// test inserting at offset 0
@Test
public void testInsertAtOffset() {
@@ -485,32 +553,18 @@ public class StructureDBTest extends AbstractGenericTest {
Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1);
struct.insertAtOffset(2, zeroArray, -1);
struct.insertAtOffset(2, FloatDataType.dataType, -1);
assertEquals(13, struct.getLength());
DataTypeComponent[] comps = struct.getDefinedComponents();
assertEquals(6, comps.length);
assertEquals(0, comps[0].getOffset());
assertEquals(0, comps[0].getOrdinal());
assertEquals(ByteDataType.class, comps[0].getDataType().getClass());
assertEquals(2, comps[1].getOffset());
assertEquals(2, comps[1].getOrdinal());
assertEquals(FloatDataType.class, comps[1].getDataType().getClass());
assertEquals(6, comps[2].getOffset());
assertEquals(3, comps[2].getOrdinal());
assertTrue(zeroArray.isEquivalent(comps[2].getDataType()));
assertEquals(6, comps[3].getOffset());
assertEquals(4, comps[3].getOrdinal());
assertEquals(WordDataType.class, comps[3].getDataType().getClass());
assertEquals(8, comps[4].getOffset());
assertEquals(5, comps[4].getOrdinal());
assertEquals(DWordDataType.class, comps[4].getDataType().getClass());
assertEquals("/Test\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 2 float[0] 0 \"\"\n" +
" 2 float 4 \"\"\n" +
" 6 word 2 \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 13 Alignment: 1\n", struct.toString());
}
@Test
@@ -815,6 +869,154 @@ public class StructureDBTest extends AbstractGenericTest {
//@formatter:on
}
@Test
public void testInsertBitFieldAtLittleEndian2() throws Exception {
Array zeroArray = new ArrayDataType(CharDataType.dataType, 0, -1);
struct.insertAtOffset(2, zeroArray, -1, "A", null);
struct.insertAtOffset(2, zeroArray, -1, "B", null);
struct.insertAtOffset(2, zeroArray, -1, "C", null);
struct.insertAtOffset(2, zeroArray, -1, "D", null);
struct.insertAtOffset(2, zeroArray, -1, "E", null);
struct.insertAtOffset(2, CharDataType.dataType, -1, "XXX", null);
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 char[0] 0 A \"\"\n" +
" 2 char[0] 0 B \"\"\n" +
" 2 char[0] 0 C \"\"\n" +
" 2 char[0] 0 D \"\"\n" +
" 2 char[0] 0 E \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
// " 3 undefined 1 \"\"\n" +
// " 4 undefined 1 \"\"\n" +
// " 5 undefined 1 \"\"\n" +
" 6 char 1 XXX \"\"\n" +
" 7 word 2 \"Comment2\"\n" +
" 9 dword 4 field3 \"\"\n" +
" 13 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 14 Alignment: 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 char[0] 0 A \"\"\n" +
" 2 char[0] 0 B \"\"\n" +
" 2 char[0] 0 C \"\"\n" +
" 2 char[0] 0 D \"\"\n" +
" 2 char[0] 0 E \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
// " 3 undefined 1 \"\"\n" +
// " 4 undefined 1 \"\"\n" +
// " 5 undefined 1 \"\"\n" +
" 6 char 1 XXX \"\"\n" +
" 7 word 2 \"Comment2\"\n" +
" 9 dword 4 field3 \"\"\n" +
" 13 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 14 Alignment: 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 char[0] 0 A \"\"\n" +
" 2 char[0] 0 B \"\"\n" +
" 2 char[0] 0 C \"\"\n" +
" 2 char[0] 0 D \"\"\n" +
" 2 char[0] 0 E \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
// " 5 undefined 1 \"\"\n" +
" 6 char 1 XXX \"\"\n" +
" 7 word 2 \"Comment2\"\n" +
" 9 dword 4 field3 \"\"\n" +
" 13 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 14 Alignment: 1", struct);
//@formatter:on
try {
struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 12, "bf4", "bf4Comment");
fail(
"expected - IllegalArgumentException: Bitfield does not fit within specified constraints");
}
catch (IllegalArgumentException e) {
// expected
}
struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 11, "bf4", "bf4Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 char[0] 0 A \"\"\n" +
" 2 char[0] 0 B \"\"\n" +
" 2 char[0] 0 C \"\"\n" +
" 2 char[0] 0 D \"\"\n" +
" 2 char[0] 0 E \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
" 4 int:11(5) 2 bf4 \"bf4Comment\"\n" +
" 6 char 1 XXX \"\"\n" +
" 7 word 2 \"Comment2\"\n" +
" 9 dword 4 field3 \"\"\n" +
" 13 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 14 Alignment: 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 0, "z", "zero bitfield");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 \"\"\n" +
" 2 char[0] 0 A \"\"\n" +
" 2 char[0] 0 B \"\"\n" +
" 2 char[0] 0 C \"\"\n" +
" 2 char[0] 0 D \"\"\n" +
" 2 char[0] 0 E \"\"\n" +
" 2 int:0(0) 0 \"zero bitfield\"\n" + // field name discarded
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
" 4 int:11(5) 2 bf4 \"bf4Comment\"\n" +
" 6 char 1 XXX \"\"\n" +
" 7 word 2 \"Comment2\"\n" +
" 9 dword 4 field3 \"\"\n" +
" 13 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Length: 14 Alignment: 1", struct);
//@formatter:on
}
@Test
public void testInsertBitFieldAtBigEndian() throws Exception {